【问题标题】:Mapbox gl js url parameterMapbox gl js url参数
【发布时间】:2022-11-17 06:53:00
【问题描述】:

我们已经成功配置了 mapbox gl js。我们有一个工作地图,其中包含可点击的位置/商店,显示 mapbox 地图弹出窗口,为用户提供一些详细信息,例如联系信息、营业时间等...

我们希望在 URL 中包含参数以轻松访问特定列表(商店/位置),例如:

https://example.com/store-locator?store=1,通过访问 URL 中 ID 为 1 的“商店定位器”,我们希望地图聚焦于该位置并让地图框弹出窗口显示匹配位置,例如 1。对于这种情况,让我们假设 JSON属性“LocationX”将被赋予一个 ID,该 ID 为 1

我尝试了下面的代码,但没有成功

const queryString = window.location.search;
        const urlParams = new URLSearchParams(queryString);
        const storeID = urlParams.get('branch')
        console.log(storeID);

        $(document).ready(function() {
                if (storeID == "1") {
                  $("#link-0").click();
                };
        })

#Link-0 是其中一个位置/列表的 ID。

<div id="listing-0" class="item active"><a href="#/" class="title" id="link-0">Broxburn</a><div>

Mapbox GL JS

/**
       * Add the map to the page
       */
      const map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/light-v10',
        center: [-1.5466858011960802, 53.478230478528126],
        zoom: 5,
        scrollZoom: true
      });

// Add geolocate control to the map.
map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
// When active the map will receive updates to the device's location as it changes.
trackUserLocation: true,
// Draw an arrow next to the location dot to indicate which direction the device is heading.
showUserHeading: true
})
);

      const stores = {
        'type': 'FeatureCollection',
        'features': [
          {
            'type': 'Feature',
            'geometry': {
              'type': 'Point',
              'coordinates': [-3.4524379167753025, 55.942792181678804]
            },
          'properties': {
          'refName': 'Broxburn',
              'phoneFormatted': 'xxxxxxxxxxxxxxxxxx',
              'phone': 'xxxxxxxxxxxxxxxxxx',
              'phone2': 'xxxxxxxxxxxxxxxxxx',
              'address': 'xxxxxxxxxxxxxxxxxx',
              'city': 'xxxxxxxxxxxxxxxxxx',
              'country': 'xxxxxxxxxxxxxxxxxx',
              'postalCode': 'xxx xxx',
              'email': 'xxxxxxxxxxxxxxxxxx',
              'service1': 'xxxxxxx',
              'service2': 'xxxxxxx',
              'weekday': 'xxxxxxx - xxxxxxx',
              'weekend': 'xxxxxxx - xxxxxxx',
              'sunday': 'Closed'
            }
          },
          {
            'type': 'Feature',
            'geometry': {
              'type': 'Point',
              'coordinates': [-2.9544548591328614, 54.92245269446434]
            },
          'properties': {
          'refName': 'Carlisle',
              'phoneFormatted': 'xxxxxxxxxxxxxxxxxx',
              'phone': 'xxxxxxxxxxxxxxxxxx',
              'phone2': 'xxxxxxxxxxxxxxxxxx',
              'address': 'xxxxxxxxxxxxxxxxxx',
              'city': 'xxxxxxxxxxxxxxxxxx',
              'country': 'xxxxxxxxxxxxxxxxxx',
              'postalCode': 'xxx xxx',
              'email': 'xxxxxxxxxxxxxxxxxx',
              'service1': 'xxxxxxx',
              'service2': 'xxxxxxx',
              'weekday': 'xxxxxxx - xxxxxxx',
              'weekend': 'xxxxxxx - xxxxxxx',
              'sunday': 'Closed'
            }
           }   
          }
        ]
      };

      /**
       * Assign a unique id to each store. You'll use this `id`
       * later to associate each point on the map with a listing
       * in the sidebar.
       */
      stores.features.forEach((store, i) => {
        store.properties.id = i;
      });

      /**
       * Wait until the map loads to make changes to the map.
       */
      map.on('load', () => {




        /**
         * This is where your '.addLayer()' used to be, instead
         * add only the source without styling a layer
         */
        map.addSource('places', {
          'type': 'geojson',
          'data': stores
        });

        /**
         * Create a new MapboxGeocoder instance.
         */
        const geocoder = new MapboxGeocoder({
          accessToken: mapboxgl.accessToken,
          mapboxgl: mapboxgl,
          marker: true,
          
        });

          /**
         * Add all the things to the page:
         * - The location listings on the side of the page
         * - The search box (MapboxGeocoder) onto the map
         * - The markers onto the map
         */
        buildLocationList(stores);
        map.addControl(geocoder, 'top-left');
        addMarkers();

        /**
         * Listen for when a geocoder result is returned. When one is returned:
         * - Calculate distances
         * - Sort stores by distance
         * - Rebuild the listings
         * - Adjust the map camera
         * - Open a popup for the closest store
         * - Highlight the listing for the closest store.
         */
        geocoder.on('result', (event) => {
          /* Get the coordinate of the search result */
          const searchResult = event.result.geometry;

          /**
           * Calculate distances:
           * For each store, use turf.disance to calculate the distance
           * in miles between the searchResult and the store. Assign the
           * calculated value to a property called `distance`.
           */
          const options = { units: 'miles' };
          for (const store of stores.features) {
            store.properties.distance = turf.distance(
              searchResult,
              store.geometry,
              options
            );
          }

          /**
           * Sort stores by distance from closest to the `searchResult`
           * to furthest.
           */
          stores.features.sort((a, b) => {
            if (a.properties.distance > b.properties.distance) {
              return 1;
            }
            if (a.properties.distance < b.properties.distance) {
              return -1;
            }
            return 0; // a must be equal to b
          });

          /**
           * Rebuild the listings:
           * Remove the existing listings and build the location
           * list again using the newly sorted stores.
           */
          const listings = document.getElementById('listings');
          while (listings.firstChild) {
            listings.removeChild(listings.firstChild);
          }
          buildLocationList(stores);

          /* Open a popup for the closest store. */
          createPopUp(stores.features[0]);

          /** Highlight the listing for the closest store. */
          const activeListing = document.getElementById(
            `listing-${stores.features[0].properties.id}`
          );
          activeListing.classList.add('active');

          /**
           * Adjust the map camera:
           * Get a bbox that contains both the geocoder result and
           * the closest store. Fit the bounds to that bbox.
           */
          const bbox = getBbox(stores, 0, searchResult);
          map.fitBounds(bbox, {
            padding: 100
          });
        });
      });

      /**
       * Using the coordinates (lng, lat) for
       * (1) the search result and
       * (2) the closest store
       * construct a bbox that will contain both points
       */
      function getBbox(sortedStores, storeIdentifier, searchResult) {
        const lats = [
          sortedStores.features[storeIdentifier].geometry.coordinates[1],
          searchResult.coordinates[1]
        ];
        const lons = [
          sortedStores.features[storeIdentifier].geometry.coordinates[0],
          searchResult.coordinates[0]
        ];
        const sortedLons = lons.sort((a, b) => {
          if (a > b) {
            return 1;
          }
          if (a.distance < b.distance) {
            return -1;
          }
          return 0;
        });
        const sortedLats = lats.sort((a, b) => {
          if (a > b) {
            return 1;
          }
          if (a.distance < b.distance) {
            return -1;
          }
          return 0;
        });
        return [
          [sortedLons[0], sortedLats[0]],
          [sortedLons[1], sortedLats[1]]
        ];
      }

      /**
       * Add a marker to the map for every store listing.
       **/
      function addMarkers() {
        /* For each feature in the GeoJSON object above: */
        for (const marker of stores.features) {
          /* Create a div element for the marker. */
          const el = document.createElement('div');
          /* Assign a unique `id` to the marker. */
          el.id = `marker-${marker.properties.id}`;
          /* Assign the `marker` class to each marker for styling. */
          el.className = 'marker';

          /**
           * Create a marker using the div element
           * defined above and add it to the map.
           **/
          new mapboxgl.Marker(el, { offset: [0, -23] })
            .setLngLat(marker.geometry.coordinates)
            .addTo(map);

          /**
           * Listen to the element and when it is clicked, do three things:
           * 1. Fly to the point
           * 2. Close all other popups and display popup for clicked store
           * 3. Highlight listing in sidebar (and remove highlight for all other listings)
           **/
          el.addEventListener('click', (e) => {
            flyToStore(marker);
            createPopUp(marker);
            const activeItem = document.getElementsByClassName('active');
            e.stopPropagation();
            if (activeItem[0]) {
              activeItem[0].classList.remove('active');
            }
            const listing = document.getElementById(
              `listing-${marker.properties.id}`
            );
            listing.classList.add('active');
          });
        }
      }

      /**
       * Add a listing for each store to the sidebar.
       **/
      function buildLocationList(stores) {
        for (const store of stores.features) {
          /* Add a new listing section to the sidebar. */
          const listings = document.getElementById('listings');
          const listing = listings.appendChild(document.createElement('div'));
          /* Assign a unique `id` to the listing. */
          listing.id = `listing-${store.properties.id}`;
          /* Assign the `item` class to each listing for styling. */
          listing.className = 'item';

          /* Add the link to the individual listing created above. */
          const link = listing.appendChild(document.createElement('a'));
          link.href = '#/';
          link.className = 'title';
          link.id = `link-${store.properties.id}`;
          link.innerHTML = `${store.properties.refName}`;

           /* Add details to the individual listing. */
          const details = listing.appendChild(document.createElement('div'));
          details.innerHTML = details.innerHTML = `${store.properties.address}, ${store.properties.city}`;
          if (store.properties.phone) {
            details.innerHTML += ` &middot; ${store.properties.phoneFormatted}`;
          }

          if (store.properties.service1) {
            
            details.innerHTML += `<br><i class="fa-solid fa-truck-front"></i>`;

      if (store.properties.service2) { details.innerHTML +=`<i data-bs-toggle="tooltip" data-bs-placement="bottom" title="Tooltip on bottom" class="fa-solid fa-spray-can" style="padding-left: 15px;"></i>`;

if (store.properties.service3) { details.innerHTML +=`<i class="fa-solid fa-helmet-safety" style="padding-left: 15px;"></i>`;

     }
       }
          }

          /**
           * Listen to the element and when it is clicked, do four things:
           * 1. Update the `currentFeature` to the store associated with the clicked link
           * 2. Fly to the point
           * 3. Close all other popups and display popup for clicked store
           * 4. Highlight listing in sidebar (and remove highlight for all other listings)
           **/
          link.addEventListener('click', function () {
            for (const feature of stores.features) {
              if (this.id === `link-${feature.properties.id}`) {
                flyToStore(feature);
                createPopUp(feature);
              }
            }
            const activeItem = document.getElementsByClassName('active');
            if (activeItem[0]) {
              activeItem[0].classList.remove('active');
            }
            this.parentNode.classList.add('active');
          });
        }
      }

      /**
       * Use Mapbox GL JS's `flyTo` to move the camera smoothly
       * a given center point.
       **/
      function flyToStore(currentFeature) {
        map.flyTo({
          center: currentFeature.geometry.coordinates,
          zoom: 15
        });
      }

      /**
       * Create a Mapbox GL JS `Popup`.
       **/
      function createPopUp(currentFeature) {
        const popUps = document.getElementsByClassName('mapboxgl-popup');
        if (popUps[0]) popUps[0].remove();

        const popup = new mapboxgl.Popup({ closeOnClick: false })
          .setLngLat(currentFeature.geometry.coordinates)
          .setHTML(
            `<h3>Company Name - ${currentFeature.properties.refName}</h3>
            
            <h4>${currentFeature.properties.address}, ${currentFeature.properties.city}, ${currentFeature.properties.postalCode}</h4>
            
            <h4><i style="margin-right:5px" class="fa-solid fa-clock"></i>Mon-Fri:<span style="margin-right:19px"></span> ${currentFeature.properties.weekday}<br><i style="margin-right:17px" class=""></i>Saturday: <span style="margin-right:10px"></span> ${currentFeature.properties.weekend}<br><i style="margin-right:17px" class=""></i>Sunday: <span style="margin-right:20px"></span> ${currentFeature.properties.sunday}</h4>
            
            <h4><i style="margin-right:5px" class="fa-solid fa-phone"></i>${currentFeature.properties.phone} <br></h4>
            
            <div style="padding:0;" id="alt-phone2" class="alt-phone2"> <h4 style="overflow-wrap: break-word;"><i style="margin-right:5px" class="fa-solid fa-phone"></i>${currentFeature.properties.phone2}</h4></div>
            
            <div style="padding:0;" id="alt-phone2" class="alt-phone3"> <h4 style="overflow-wrap: break-word;"><i style="margin-right:5px" class="fa-solid fa-phone"></i>${currentFeature.properties.phone3}</h4></div>

            <h4 style="overflow-wrap: break-word;"><i style="margin-right:5px" class="fa-solid fa-envelope"></i>${currentFeature.properties.email}</h4>`
            
            
          
          )
          
          .addTo(map);
          
           if (currentFeature.properties.phone2 === undefined) {
                    document.getElementsByClassName("alt-phone2")[0].style.display = "none";
                }

           if (currentFeature.properties.phone3 === undefined) {
                    document.getElementsByClassName("alt-phone3")[0].style.display = "none";
                }
          
         
          
          }

【问题讨论】:

    标签: javascript json mapbox mapbox-gl-js mapbox-marker


    【解决方案1】:

    我认为您的代码看起来不错,但 $(document).ready(...)map.on('load', ...) 事件之间可能存在竞争条件。第一个在第二个之前触发。这意味着,“单击”链接的 JavaScript 会尝试单击 DOM 中尚不存在的链接。如果是这种情况,解决方案是将“点击”代码移到创建链接的代码之后。

    即创建此功能:

    function clickSelectedStore() {
      const queryString = window.location.search;
      const urlParams = new URLSearchParams(queryString);
      const storeID = urlParams.get('branch')
      console.log(storeID);
      
      if (storeID) {
        $('#link-' + storeID).click();
      }
    }
    

    并在 map.on('load', ...) 事件结束时调用它(在 buildLocationList(...) 函数已经被调用之后。

    如果这没有帮助,您应该创建一个Minimal Reproducible Example,以便其他人可以看到您的代码。

    【讨论】:

      猜你喜欢
      • 2017-01-26
      • 2016-06-05
      • 1970-01-01
      • 2017-04-24
      • 2020-07-17
      • 2020-02-09
      • 2016-07-07
      • 2016-12-10
      • 2017-03-14
      相关资源
      最近更新 更多