Добавить панель поиска в Mapbox GL JS

#javascript #mapbox

#javascript #mapbox

Вопрос:

Я работаю над формой демонстрационного файла MapBox для создания локатора магазина (https://docs.mapbox.com/help/tutorials/building-a-store-locator /)

Я хотел бы добавить панель поиска, которая фильтрует результаты на основе поля «город». Я пытался работать с документацией на веб-сайте MapBox, но безуспешно. https://docs.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view /

HTML / JS

 <!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8' />
    <title>Store Locator</title>
    <meta name='robots' content='noindex, nofollow'>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />

    <link href='https://fonts.googleapis.com/css?family=Source Sans Pro:400,700' rel='stylesheet'>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css' rel='stylesheet' />
    <link href='stylesheet.css' rel='stylesheet' />

    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js'></script>
    <script src='store-info.js'></script>

    <script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.5.1/mapbox-gl-geocoder.min.js"></script>
    <link
    rel="stylesheet"
    href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.5.1/mapbox-gl-geocoder.css"
    type="text/css"
    />
    <!-- Promise polyfill script required to use Mapbox GL Geocoder in IE 11 -->
    <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>

  </head>


  <body>
      <div class="center-all">
      <div class="WTB-container">
        <h2>Find a riderwear stockist near you.</h2>
        <p>Please contact your chosen store to confirm stock before beginning your journey.</p>
    <div class="map-container">
    <div class='sidebar'>
      <div class='heading'>
        <h1>Stores</h1>
      </div>
      <div id='listings' class='listings'></div>
    </div>
    <div id='map' class='map'></div>
  </div>
  </div>
</div>
  </body>
</html>


<script>
  // This will let you use the .remove() function later on
  if (!('remove' in Element.prototype)) {
    Element.prototype.remove = function() {
      if (this.parentNode) {
          this.parentNode.removeChild(this);
      }
    };
  }

  mapboxgl.accessToken = 'pk.eyJ1IjoibWFyY3VzdGVzdGFjY291bnQiLCJhIjoiY2tmaTA3ODJ5MHFnZjJxczJla2M3bjRneSJ9.iafUvLt1Zn1Cs8IrRbYhOw';
  /** 
   * Add the map to the page
  */
  var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/light-v10',
    center: [-1.480048, 53.553637],
    zoom: 5,
    scrollZoom: true
  });


  /**
   * 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(function(store, i){
    store.properties.id = i;
  });

  /**
   * Wait until the map loads to make changes to the map.
  */
  map.on('load', function (e) {
    /** 
     * This is where your '.addLayer()' used to be, instead
     * add only the source without styling a layer
    */
    map.addSource("places", {
      "type": "geojson",
      "data": stores
    });

    /**
     * Add all the things to the page:
     * - The location listings on the side of the page
     * - The markers onto the map
    */
    buildLocationList(stores);
    addMarkers();
  });

  /**
   * Add a marker to the map for every store listing.
  **/
  function addMarkers() {
    /* For each feature in the GeoJSON object above: */
    stores.features.forEach(function(marker) {
      /* Create a div element for the marker. */
      var 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', function(e){
        /* Fly to the point */
        flyToStore(marker);
        /* Close all other popups and display popup for clicked store */
        createPopUp(marker);
        /* Highlight listing in sidebar */
        var activeItem = document.getElementsByClassName('active');
        e.stopPropagation();
        if (activeItem[0]) {
          activeItem[0].classList.remove('active');
        }
        var listing = document.getElementById('listing-'   marker.properties.id);
        listing.classList.add('active');
      });
    });
  }

  /**
   * Add a listing for each store to the sidebar.
  **/
  function buildLocationList(data) {
    data.features.forEach(function(store, i){
      /**
       * Create a shortcut for `store.properties`,
       * which will be used several times below.
      **/
      var prop = store.properties;

      /* Add a new listing section to the sidebar. */
      var listings = document.getElementById('listings');
      var listing = listings.appendChild(document.createElement('div'));
      /* Assign a unique `id` to the listing. */
      listing.id = "listing-"   prop.id;
      /* Assign the `item` class to each listing for styling. */
      listing.className = 'item';

      /* Add the link to the individual listing created above. */
      var link = listing.appendChild(document.createElement('a'));
      link.href = '#';
      link.className = 'title';
      link.id = "link-"   prop.id;
      link.innerHTML = prop.name;

      /* Add details to the individual listing. */
      var details = listing.appendChild(document.createElement('div'));
      details.innerHTML = prop.city;
      if (prop.phone) {
        details.innerHTML  = ' · '   prop.phone;
      }

      /**
       * 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(e){
        for (var i=0; i < data.features.length; i  ) {
          if (this.id === "link-"   data.features[i].properties.id) {
            var clickedListing = data.features[i];
            flyToStore(clickedListing);
            createPopUp(clickedListing);
          }
        }
        var 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) {
    var popUps = document.getElementsByClassName('mapboxgl-popup');
    if (popUps[0]) popUps[0].remove();
    var popup = new mapboxgl.Popup({closeOnClick: false})
      .setLngLat(currentFeature.geometry.coordinates)
      .setHTML('<h3>'   currentFeature.properties.name   '</h3>'  
      '<h4>'   currentFeature.properties.address   '</h4>'   
      '<h4>'  currentFeature.properties.county   '</h4>'    
      '<h4>'   currentFeature.properties.postalCode   '</h4>')
      .addTo(map);
  }


  
// Add geolocate control to the map.
map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
})
);

// Add zoom and rotation controls to the map.
map.addControl(new mapboxgl.NavigationControl());
  

CSS

     body {
        color:#404040;
        font:400 15px/22px 'Source Sans Pro', 'Helvetica Neue', Sans-serif;
        margin:0;
        padding:0;
        -webkit-font-smoothing:antialiased;
      }

      * {
        -webkit-box-sizing:border-box;
        -moz-box-sizing:border-box;
        box-sizing:border-box;
      }
h2{
  margin-bottom: 0;
}



  .center-all{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;

    height: 100vh;
  }

      .WTB-container{
        width: 1200px;
        height: auto;

        margin: auto;

        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;

        text-align: left;
      }
      .map-container{
        width: 80%;
        height: auto;

        

        display: flex;
        justify-content: center;
        align-items: center;

        margin: 0;
          margin-top: 50px;
      }

      .sidebar {
        width:30%;
        height:500px;

        overflow:hidden;
        border-right:1px solid rgba(0,0,0,0.25);
      }
      .pad2 {
        padding:20px;
      }

      .map {
        width:60%;
        height: 500px;
      }

      h1 {
        font-size:22px;
        margin:0;
        font-weight:400;
        line-height: 20px;
        padding: 20px 2px;
      }

      a {
        color:#404040;
        text-decoration:none;
      }

      a:hover {
        color:#101010;
      }

      .heading {
        background:#fff;
        border-bottom:1px solid #eee;
        min-height:60px;
        line-height:60px;
        padding:0 10px;
        background-color: #1060AB;
        color: #fff;
      }

      .listings {
        height:100%;
        overflow:auto;
        padding-bottom:60px;
      }

      .listings .item {
        display:block;
        border-bottom:1px solid #eee;
        padding:10px;
        text-decoration:none;
      }

      .listings .item:last-child { border-bottom:none; }
      .listings .item .title {
        display:block;
        color:#1060AB;
        font-weight:700;
      }

      .listings .item .title small { font-weight:400; }
      .listings .item.active .title,
      .listings .item .title:hover { color:#1060AB; }
      .listings .item.active {
        background-color:#f8f8f8;
      }
      ::-webkit-scrollbar {
        width:3px;
        height:3px;
        border-left:0;
        background:rgba(0,0,0,0.1);
      }
      ::-webkit-scrollbar-track {
        background:none;
      }
      ::-webkit-scrollbar-thumb {
        background:#1060AB;
        border-radius:0;
      }

      .marker {
        border: none;
        cursor: pointer;
        height: 56px;
        width: 56px;
        background-image: url(marker.png);
        background-color: rgba(0, 0, 0, 0);
      }

      .clearfix { display:block; }
      .clearfix:after {
        content:'.';
        display:block;
        height:0;
        clear:both;
        visibility:hidden;
      }

      /* Marker tweaks */
      .mapboxgl-popup {
        padding-bottom: 50px;
      }

      .mapboxgl-popup-close-button {
        display:none;
      }
      .mapboxgl-popup-content {
        font:400 15px/22px 'Source Sans Pro', 'Helvetica Neue', Sans-serif;
        padding:0;
        width:180px;
      }
      .mapboxgl-popup-content-wrapper {
        padding:1%;
      }
      .mapboxgl-popup-content h3 {
        background:#1060AB;
        color:#fff;
        margin:0;
        display:block;
        padding:10px;
        border-radius:3px 3px 0 0;
        font-weight:700;
        margin-top:-15px;
      }

      .mapboxgl-popup-content h4 {
        margin:0;
        display:block;
        padding: 10px 10px 10px 10px;
        font-weight:400;
      }

      .mapboxgl-popup-content div {
        padding:10px;
      }

      .mapboxgl-container .leaflet-marker-icon {
        cursor:pointer;
      }

      .mapboxgl-popup-anchor-top > .mapboxgl-popup-content {
        margin-top: 15px;
      }

      .mapboxgl-popup-anchor-top > .mapboxgl-popup-tip {
        border-bottom-color: #1060AB;
      }


        .mapboxgl-ctrl-compass{
          display: none !important;
        }

        
  

GeoJSON

 var stores = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
        -1.310840,
        51.830350
        ]
      },
      "properties": {
        "name":"Helmet City",
        "phone": "01865 372529",
        "address": "Saint Georges House, Langford Lane",
        "city": "Kidlington",
        "county": "Oxfordshire",
        "country": "England",
        "postalCode": "OX5 1HT",

      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          -1.714590,
          52.200806
        ]
      },
      "properties": {
        "name":"Performance Triumph",
        "phone": "01789 205149",
        "address": "14 Western Road",
        "city": "Stratford Upon Avon",
        "county": "Warwickshire",
        "country": "England",
        "postalCode": "CV37 0AH",

      }
    }

  ]
};
  

Nike (https://www.nike.com/gb/retail?latitude=51.50722amp;longitude=-0.1275amp;page=1amp;storesPerPage=15 ) у меня есть функция, которая мне нужна, но я не могу ее воссоздать. Любая помощь будет оценена.

Комментарии:

1. Первое, что нужно понять в mapbox, это то, что вы либо создаете источник и стиль и позволяете ему обрабатывать отображение маркеров, либо создаете маркеры один за другим и забываете об определении источника (здесь вы делаете оба). Кроме того, вы на самом деле не указали, в чем проблема? И последнее, но не менее важное: попробуйте создать скрипку, чтобы вы могли поделиться живым примером