Zelfstudie: Azure Kaarten gebruiken om een winkelzoeker te maken

In deze zelfstudie wordt u begeleid bij het maken van een eenvoudige winkelzoeker met behulp van Azure Kaarten.

In deze zelfstudie leert u het volgende:

  • Een nieuwe webpagina maken met de Azure Map Control-API.
  • Aangepaste gegevens laden uit een bestand en weergeven op een kaart.
  • Gebruik de zoekservice van Azure Maps om een adres te vinden of een query in te voeren.
  • Haal de locatie van de gebruiker op van de browser en laat deze op de kaart zien.
  • Combineer meerdere lagen om aangepaste symbolen te maken op de kaart.
  • Cluster gegevenspunten.
  • Voeg besturingselementen voor in- en uitzoomen toe aan de kaart.

Vereisten

Notitie

Zie Verificatie beheren in Azure Maps voor meer informatie over verificatie in Azure Maps.

Voorbeeldcode

Deze zelfstudie laat zien hoe u een winkelzoeker maakt voor een fictief bedrijf met de naam Contoso Coffee, samen met tips voor het uitbreiden van de winkelzoeker met extra functionaliteit.

Als u een livevoorbeeld wilt zien van wat u in deze zelfstudie maakt, raadpleegt u Simple Store Locator op de site codevoorbeelden van Azure Kaarten.

Als u deze zelfstudie eenvoudiger wilt volgen en er gebruik van wilt maken, downloadt u de volgende bronnen:

Winkelzoekerfuncties

In deze sectie vindt u de Azure Kaarten-functies die worden gedemonstreerd in de contoso Coffee-winkelzoekertoepassing die in deze zelfstudie is gemaakt.

Functies van de gebruikersinterface

  • Een winkellogo op de koptekst
  • Een kaart die pannen en zoomen ondersteunt
  • Een knop Mijn locatie om te zoeken op de huidige locatie van de gebruiker.
  • Een pagina-indeling die wordt aangepast op basis van de breedte van het scherm van het apparaat
  • Een zoekvak en een zoekknop

Functionaliteitsfuncties

  • Een keypress gebeurtenis die is toegevoegd aan het zoekvak activeert een zoekopdracht wanneer de gebruiker op Enter drukt.
  • Wanneer de kaart wordt verplaatst, wordt de afstand naar elke locatie vanaf het midden van de kaart opnieuw berekend. De lijst met resultaten wordt bijgewerkt om de dichtstbijzijnde locaties boven aan de kaart weer te geven.
  • Wanneer de gebruiker een resultaat selecteert in de lijst met resultaten, wordt de kaart gecentreerd op de geselecteerde locatie en wordt informatie over de locatie weergegeven in een pop-upvenster.
  • Wanneer de gebruiker een specifieke locatie selecteert, activeert de kaart een pop-upvenster.
  • Wanneer de gebruiker uitzoomt, worden locaties gegroepeerd in clusters. Elk cluster wordt vertegenwoordigd door een cirkel met een getal in de cirkel. Clusters worden gevormd of lossen op als de gebruiker het zoomniveau wijzigt.
  • Als u een cluster selecteert, zoomt u in twee niveaus op de kaart en centreert u op de locatie van het cluster.

Ontwerp winkelzoeker

In de volgende schermopname ziet u de algemene indeling van de locatortoepassing contoso Coffee Store. Als u het livevoorbeeld wilt bekijken en ermee wilt werken, raadpleegt u de simple Store Locator-voorbeeldtoepassing op de site codevoorbeelden van Azure Kaarten.

Een schermopname van de Voorbeeldtoepassing contoso Coffee Store-locator Azure Kaarten.

Om de bruikbaarheid van deze winkelzoeker te maximaliseren, gebruiken we een responsieve lay-out die wordt aangepast wanneer de schermbreedte van een gebruiker kleiner is dan 700 pixels. Een responsieve lay-out maakt het gemakkelijk om de winkelzoeker op een klein scherm te gebruiken, zoals op een mobiel apparaat. Hier volgt een schermopname met een voorbeeld van de indeling voor een klein scherm:

Een schermopname van hoe de locatortoepassing Contoso Coffee store eruitziet op een mobiel apparaat.

De gegevensset met winkellocaties maken

In deze sectie wordt beschreven hoe u een gegevensset maakt van de winkels die u op de kaart wilt weergeven. De gegevensset voor de Contoso Coffee-locator wordt gemaakt in een Excel-werkmap. De gegevensset bevat 10.213 Contoso Coffee-koffiebarlocaties verspreid over negen landen of regio's: het Verenigde Staten, Canada, het Verenigd Koninkrijk, Frankrijk, Duitsland, Italië, Nederland, Denemarken en Spanje. Hier volgt een schermopname van hoe de gegevens eruitzien:

Schermopname van de winkelzoekergegevens in een Excel-werkmap.

Download het Excel-bestand met de volledige gegevensset voor de voorbeeldtoepassing Contoso Coffee-locator uit de gegevensmap van de opslagplaats met codevoorbeelden van Azure Kaarten in GitHub.

In de bovenstaande schermopname van de gegevens kunnen we de volgende opmerkingen maken:

  • Locatiegegevens worden opgeslagen in de volgende zes kolommen: AddressLine, Plaats, Gemeente (provincie), Beheer Division (staat/provincie), PostCode (postcode) en Land.
  • De kolommen Breedtegraad en Lengtegraad bevatten de coördinaten voor elke Contoso Coffee-locatie. Als u geen coördinatengegevens hebt, kunt u de Search-service gebruiken om de locatiecoördinaten te bepalen.
  • Sommige andere kolommen bevatten metagegevens die betrekking hebben op de koffiebars: een telefoonnummer, Booleaanse kolommen en het opslaan van openings- en sluitingstijden in de indeling van 24 uur. De Booleaanse kolommen zijn voor WiFi-beschikbaarheid en toegankelijkheid voor rolstoelgebruikers. U kunt uw eigen kolommen maken met metagegevens die relevanter zijn voor uw locatiegegevens.

Notitie

Azure Kaarten geeft gegevens weer in de Spherical Mercator-projectie EPSG:3857, maar leest gegevens in EPSG:4326 die gebruikmaken van de WGS84-datum.

Gegevensset van contoso Coffee shop-locator laden

De gegevensset contoso Coffee shop locator is klein, zodat deze kan worden geconverteerd naar een door tabs gescheiden tekstbestand dat door de browser wordt gedownload wanneer de toepassing wordt geladen.

Tip

Als uw gegevensset te groot is voor het downloaden van clients of regelmatig wordt bijgewerkt, kunt u overwegen uw gegevensset op te slaan in een database. Nadat uw gegevens in een database zijn geladen, kunt u vervolgens een webservice instellen die query's voor de gegevens accepteert en vervolgens de resultaten naar de browser van de gebruiker verzendt.

Gegevens converteren naar tekstbestand met tabscheidingstekens

De locatiegegevens van Contoso Coffee Shop vanuit een Excel-werkmap converteren naar een tekstbestand met tabscheidingstekens:

  1. Download de Excel-werkmap ContosoCoffee.xlsx en open deze in Excel.

  2. Selecteer Bestand > opslaan als....

  3. Selecteer in de vervolgkeuzelijst Opslaan als de optie Tekst (tab is scheidingsteken) (*.txt).

  4. Geef het bestand de naam ContosoCoffee.

Schermopname van het dialoogvenster Opslaan als.

Als u het tekstbestand opent in Kladblok, ziet het er ongeveer als volgt uit:

Schermopname van een Kladblok-bestand met een door tabs gescheiden gegevensset.

Het project instellen

  1. Open Visual Studio Code of uw keuze voor ontwikkelomgevingen.

  2. Selecteer De werkruimte Bestand > openen....

  3. Maak een nieuwe map met de naam ContosoCoffee.

  4. Selecteer ContosoCoffee in de verkenner.

  5. Maak de volgende drie bestanden waarmee de indeling, stijl en logica voor de toepassing worden gedefinieerd:

    • Index.html
    • index.css
    • index.js
  6. Maak een map met de naam gegevens.

  7. Voeg het ContosoCoffee.txt bestand toe dat u eerder hebt gemaakt vanuit de Excel-werkmap ContosoCoffee.xlsx aan de gegevensmap .

  8. Maak een andere map met de naam images (afbeeldingen).

  9. Als u dat nog niet hebt gedaan, downloadt u de 10 kaartafbeeldingen uit de map met afbeeldingen in de GitHub-opslagplaats en voegt u deze toe aan de map afbeeldingen .

    De werkruimtemap moet er nu uitzien als in de volgende schermopname:

    Schermopname van de map afbeeldingen in de map Contoso Coffee.

De HTML maken

De HTML maken:

  1. Voeg de volgende meta tags toe aan de head index.html:

    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
  2. Voeg verwijzingen toe naar de JavaScript- en CSS-bestanden voor de Azure Maps-webbesturingselementen :

    <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
    <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.css" type="text/css">
    <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.js"></script>
    
  3. Voeg verwijzingen toe aan index.js en index.css.

    <!-- Add references to the store locator JavaScript and CSS files. -->
    <link rel="stylesheet" href="index.css" type="text/css">
    <script src="index.js"></script>
    
  4. Voeg in de hoofdtekst van het document een header-tag toe. Voeg in de header-tag het logo en de bedrijfsnaam toe.

    <header>
        <img src="images/Logo.png" />
        <span>Contoso Coffee</span>
    </header>
    
  5. Voeg een main-tag toe en maak een zoekvenster met een tekstvak en zoekknop. Voeg ook div-tags toe met verwijzingen naar de kaart, het lijstvenster en de GPS-knop Mijn locatie.

    <main>
        <div class="searchPanel">
            <div>
                <input id="searchTbx" type="search" placeholder="Find a store" />
                <button id="searchBtn" title="Search"></button>
            </div>
        </div>
        <div id="listPanel"></div>
        <div id="myMap"></div>
        <button id="myLocationBtn" title="My Location"></button>
    </main>
    

Zodra dit is voltooid, ziet index.html eruit als Simple Store Locator.html in de voorbeeldcode van de zelfstudie.

De CSS-stijlen definiëren

De volgende stap is het definiëren van de CSS-stijlen. CSS-stijlen definiëren de lay-out van de onderdelen van de toepassing en het uiterlijk van de toepassing.

  1. Open index.css.

  2. Voeg de volgende css-code toe:

    Notitie

    De @media-stijl definieert alternatieve stijlopties om te gebruiken wanneer het scherm minder dan 700 pixels breed is.

     html, body {
         padding: 0;
         margin: 0;
         font-family: Gotham, Helvetica, sans-serif;
         overflow-x: hidden;
     } 
    
     header {
         width: calc(100vw - 10px);
         height: 30px;
         padding: 15px 0 20px 20px;
         font-size: 25px;
         font-style: italic;
         font-family: "Comic Sans MS", cursive, sans-serif;
         line-height: 30px;
         font-weight: bold;
         color: white;
         background-color: #007faa;
     }
    
     header span {
         vertical-align: middle;
     }
    
     header img {
         height: 30px;
         vertical-align: middle;
     }
    
     .searchPanel {
         position: relative;
         width: 350px;
     }
    
     .searchPanel div {
         padding: 20px;
     }
    
     .searchPanel input {
         width: calc(100% - 50px);
         font-size: 16px;
         border: 0;
         border-bottom: 1px solid #ccc;
     }
    
     #listPanel {
         position: absolute;
         top: 135px;
         left: 0px;
         width: 350px;
         height: calc(100vh - 135px);
         overflow-y: auto;
     }
    
     #myMap { 
         position: absolute;
         top: 65px;
         left: 350px;
         width: calc(100vw - 350px);
         height: calc(100vh - 65px);
     }
    
     .statusMessage {
         margin: 10px;
     }
    
     #myLocationBtn, #searchBtn {
         margin: 0;
         padding: 0;
         border: none;
         border-collapse: collapse;
         width: 32px;
         height: 32px; 
         text-align: center;
         cursor: pointer;
         line-height: 32px;
         background-repeat: no-repeat;
         background-size: 20px;
         background-position: center center;
         z-index: 200;
     }
    
     #myLocationBtn {
         position: absolute;
         top: 150px;
         right: 10px;
         box-shadow: 0px 0px 4px rgba(0,0,0,0.16);
         background-color: white;
         background-image: url("images/GpsIcon.png");
     }
    
     #myLocationBtn:hover {
         background-image: url("images/GpsIcon-hover.png");
     }
    
     #searchBtn {
         background-color: transparent;
         background-image: url("images/SearchIcon.png");
     }
    
     #searchBtn:hover {
         background-image: url("images/SearchIcon-hover.png");
     }
    
     .listItem {
         height: 50px;
         padding: 20px;
         font-size: 14px;
     }
    
     .listItem:hover {
         cursor: pointer;
         background-color: #f1f1f1;
     }
    
     .listItem-title {
         color: #007faa;
         font-weight: bold;
     }
    
     .storePopup {
         min-width: 150px;
     }
    
     .storePopup .popupTitle {
         border-top-left-radius: 4px;
         border-top-right-radius: 4px;
         padding: 8px;
         height: 30px;
         background-color: #007faa;
         color: white;
         font-weight: bold;
     }
    
     .storePopup .popupSubTitle {
         font-size: 10px;
         line-height: 12px;
     }
    
     .storePopup .popupContent {
         font-size: 11px;
         line-height: 18px;
         padding: 8px;
     }
    
     .storePopup img {
         vertical-align:middle;
         height: 12px;
         margin-right: 5px;
     }
    
     /* Adjust the layout of the page when the screen width is fewer than 700 pixels. */
     @media screen and (max-width: 700px) {
         .searchPanel {
             width: 100vw;
         }
    
         #listPanel {
             top: 385px;
             width: 100%;
             height: calc(100vh - 385px);
         }
    
         #myMap {
             width: 100vw;
             height: 250px;
             top: 135px;
             left: 0px;
         }
    
         #myLocationBtn {
             top: 220px;
         }
     }
    
     .mapCenterIcon {
         display: block;
         width: 10px;
         height: 10px;
         border-radius: 50%;
         background: orange;
         border: 2px solid white;
         cursor: pointer;
         box-shadow: 0 0 0 rgba(0, 204, 255, 0.4);
         animation: pulse 3s infinite;
     }
    
     @keyframes pulse {
         0% {
             box-shadow: 0 0 0 0 rgba(0, 204, 255, 0.4);
         }
    
         70% {
             box-shadow: 0 0 0 50px rgba(0, 204, 255, 0);
         }
    
         100% {
             box-shadow: 0 0 0 0 rgba(0, 204, 255, 0);
         }
     }
    

Als u de toepassing op dit moment uitvoert, wordt de koptekst, het zoekvak en de zoekknop weergegeven. De kaart is echter niet zichtbaar omdat deze nog niet is geladen. Als u een zoekopdracht probeert uit te voeren, gebeurt er niets. In de volgende sectie wordt beschreven hoe u de JavaScript-logica toevoegt die nodig is voor toegang tot alle functionaliteit van de winkelzoeker.

JavaScript-code toevoegen

De JavaScript-code in de locator-app contoso coffee shop maakt de volgende processen mogelijk:

  1. Voegt een gebeurtenislistener toe die wordt aangeroepen ready om te wachten totdat de pagina het laadproces heeft voltooid. Wanneer het laden van de pagina is voltooid, maakt de gebeurtenis-handler meer gebeurtenislisteners om het laden van de kaart te controleren en functionaliteit te bieden aan de knoppen Zoeken en Mijn locatie .

  2. Wanneer de gebruiker de zoekknop selecteert of een locatie in het zoekvak typt, wordt een fuzzy zoekopdracht voor de query van de gebruiker gestart. De code geeft iso 2-waarden door in een matrix met ISO 2-waarden voor land/regio aan de countrySet optie om de zoekresultaten te beperken tot die landen/regio's. Het beperken van de te doorzoeken landen/regio's helpt de nauwkeurigheid van de geretourneerde zoekresultaten te verhogen.

  3. Zodra de zoekopdracht is voltooid, wordt het eerste locatieresultaat gebruikt als de middelpunt van de kaart. Wanneer de gebruiker de knop Mijn locatie selecteert, haalt de code de locatie van de gebruiker op met behulp van de HTML5 Geolocation-API die is ingebouwd in de browser. Zodra de locatie is opgehaald, wordt de kaart gecentreerd via de locatie van de gebruiker.

Ga als volgende te werk om javaScript toe te voegen:

  1. Open index.js.

  2. Voeg algemene opties toe om het gemakkelijker te maken instellingen bij te werken. Definieer de variabelen voor de kaart, het pop-upvenster, de gegevensbron, de pictogramlaag en de HTML-markering. Stel de HTML-markering in om het midden van een zoekgebied aan te geven. Definieer tevens een exemplaar van de Azure Maps-zoekserviceclient.

    //The maximum zoom level to cluster data point data on the map.
    var maxClusterZoomLevel = 11;
    
    //The URL to the store location data.
    var storeLocationDataUrl = 'data/ContosoCoffee.txt';
    
    //The URL to the icon image. 
    var iconImageUrl = 'images/CoffeeIcon.png';
    
    //An array of country region ISO2 values to limit searches to.
    var countrySet = ['US', 'CA', 'GB', 'FR','DE','IT','ES','NL','DK'];      
    
    //
    var map, popup, datasource, iconLayer, centerMarker;
    
    // Used in function updateListItems
    var listItemTemplate = '<div class="listItem" onclick="itemSelected(\'{id}\')"><div class="listItem-title">{title}</div>{city}<br />Open until {closes}<br />{distance} miles away</div>';
    
    
  3. Voeg de volgende initialisatiecode toe. Zorg ervoor dat u deze vervangt door <Your Azure Maps Key> uw Azure Kaarten-abonnementssleutel.

    Tip

    Wanneer u een pop-upvensters gebruikt, is het raadzaam één Popup-instantie te maken en deze te hergebruiken door de inhoud en positie ervan bij te werken. Voor elke Popup-instantie die u toevoegt aan uw code, worden meerdere DOM-elementen toegevoegd aan de pagina. Hoe meer DOM-elementen er op een pagina zijn, hoe meer dingen de browser moet bijhouden. Als er te veel items zijn, kan de browser traag worden.

    
    function initialize() {
        //Initialize a map instance.
        map = new atlas.Map('myMap', {
            center: [-90, 40],
            zoom: 2,
    
            //Add your Azure Maps subscription key to the map SDK.
            authOptions: {
                authType: 'subscriptionKey',
                subscriptionKey: '<Your Azure Maps Key>'
            }
        });
    
        //Create a pop-up window, but leave it closed so we can update it and display it later.
        popup = new atlas.Popup();
    
        //If the user selects the search button, geocode the value the user passed in.
        document.getElementById('searchBtn').onclick = performSearch;
    
        //If the user presses Enter in the search box, perform a search.
        document.getElementById('searchTbx').onkeyup = function(e) {
            if (e.keyCode === 13) {
                performSearch();
            }
        };
    
        //If the user selects the My Location button, use the Geolocation API to get the user's location. Center and zoom the map on that location.
        document.getElementById('myLocationBtn').onclick = setMapToUserLocation;
    
        //Wait until the map resources are ready.
        map.events.add('ready', function() {
    
            //Add your maps post load functionality.
    
        });
    }
    
    function performSearch() {
        var query = document.getElementById('searchTbx').value;
        //Pass in the array of country/region ISO2 for which we want to limit the search to.
        var url = `https://atlas.microsoft.com/search/fuzzy/json?api-version=1.0&countrySet=${countrySet}&query=${query}&view=Auto`;
    
        //Perform a fuzzy search on the users query.
        fetch(url, {
            headers: {
                "Subscription-Key": map.authentication.getToken()
            }
        })
        .then((response) => response.json())
        .then((response) => {
            if (Array.isArray(response.results) && response.results.length > 0) {
                var result = response.results[0];
                var bbox = [
                    result.viewport.topLeftPoint.lon,
                    result.viewport.btmRightPoint.lat,
                    result.viewport.btmRightPoint.lon,
                    result.viewport.topLeftPoint.lat
                ];
                //Set the camera to the bounds of the first result.
                map.setCamera({
                    bounds: bbox,
                    padding: 40
                });
            } else {
                document.getElementById('listPanel').innerHTML = '<div class="statusMessage">Unable to find the location you searched for.</div>';
            }
        });
    }
    
    function setMapToUserLocation() {
        //Request the user's location.
        navigator.geolocation.getCurrentPosition(function(position) {
            //Convert the geolocation API position into a longitude/latitude position value the map can understand and center the map over it.
            map.setCamera({
                center: [position.coords.longitude, position.coords.latitude],
                zoom: maxClusterZoomLevel + 1
            });
        }, function(error) {
            //If an error occurs when trying to access the users position information, display an error message.
            switch (error.code) {
                case error.PERMISSION_DENIED:
                    alert('User denied the request for geolocation.');
                    break;
                case error.POSITION_UNAVAILABLE:
                    alert('Position information is unavailable.');
                    break;
                case error.TIMEOUT:
                    alert('The request to get user position timed out.');
                    break;
                case error.UNKNOWN_ERROR:
                    alert('An unknown error occurred.');
                    break;
            }
        });
    }
    
    //Initialize the application when the page is loaded.
    window.onload = initialize;
    
  4. Voeg in de gebeurtenis-handler van ready de kaart een zoom besturingselement en een HTML-markering toe om het midden van een zoekgebied weer te geven.

    //Add a zoom control to the map.
    map.controls.add(new atlas.control.ZoomControl(), {
        position: 'top-right'
    });
    
    //Add an HTML marker to the map to indicate the center to use for searching.
    centerMarker = new atlas.HtmlMarker({
        htmlContent: '<div class="mapCenterIcon"></div>',
        position: map.getCamera().center
    });
    
    map.markers.add(centerMarker);
    
  5. Voeg in de gebeurtenis-handler van ready de kaart een gegevensbron toe. Maak vervolgens een aanroep om de gegevensset te laden en te parseren. Schakel clustering voor de gegevensbron in. Door clustering van de gegevensbrongroepen worden overlappende punten samengevoegd tot een cluster. Terwijl de gebruiker inzoomt, scheiden de clusters zich in afzonderlijke punten. Dit gedrag zorgt voor een betere gebruikerservaring en verbetert de prestaties.

    //Create a data source, add it to the map, and then enable clustering.
    datasource = new atlas.source.DataSource(null, {
        cluster: true,
        clusterMaxZoom: maxClusterZoomLevel - 1
    });
    
    map.sources.add(datasource);
    
    //Load all the store data now that the data source has been defined.  
    loadStoreData();
    
  6. Nadat de gegevensset in de gebeurtenis-handler van de kaart ready is geladen, definieert u een set lagen om de gegevens weer te geven. Een bellenlaag geeft geclusterde gegevenspunten weer. Een symboollaag geeft het aantal punten weer in elk cluster boven de bellenlaag. Met een tweede symboollaag wordt een aangepast pictogram voor afzonderlijke locaties op de kaart weergegeven.

    Voeg mouseover- en mouseout-gebeurtenissen toe aan de bellen- en pictogramlagen om de muisaanwijzer te wijzigen wanneer de gebruiker een cluster of pictogram op de kaart aanwijst. Voeg een click-gebeurtenis toe aan de clusterbellenlaag. Deze click-gebeurtenis zoomt twee niveaus in op de kaart en centreert de kaart op een cluster wanneer de gebruiker een cluster selecteert. Voeg een click-gebeurtenis toe aan de pictogramlaag. Deze click-gebeurtenis geeft een pop-upvenster met de details van een koffiebar weer wanneer een gebruiker een individueel locatiepictogram selecteert. Voeg een gebeurtenis toe aan de kaart om te controleren wanneer de kaart klaar is met bewegen. Wanneer deze gebeurtenis wordt geactiveerd, worden de items in het deelvenster met de lijst bijgewerkt.

    //Create a bubble layer to render clustered data points.
    var clusterBubbleLayer = new atlas.layer.BubbleLayer(datasource, null, {
        radius: 12,
        color: '#007faa',
        strokeColor: 'white',
        strokeWidth: 2,
        filter: ['has', 'point_count'] //Only render data points that have a point_count property; clusters have this property.
    });
    
    //Create a symbol layer to render the count of locations in a cluster.
    var clusterLabelLayer = new atlas.layer.SymbolLayer(datasource, null, {
        iconOptions: {
            image: 'none' //Hide the icon image.
        },
    
        textOptions: {
            textField: ['get', 'point_count_abbreviated'],
            size: 12,
            font: ['StandardFont-Bold'],
            offset: [0, 0.4],
            color: 'white'
        }
    });
    
    map.layers.add([clusterBubbleLayer, clusterLabelLayer]);
    
    //Load a custom image icon into the map resources.
    map.imageSprite.add('myCustomIcon', iconImageUrl).then(function() {
    
       //Create a layer to render a coffee cup symbol above each bubble for an individual location.
       iconLayer = new atlas.layer.SymbolLayer(datasource, null, {
           iconOptions: {
               //Pass in the ID of the custom icon that was loaded into the map resources.
               image: 'myCustomIcon',
    
               //Optionally, scale the size of the icon.
               font: ['SegoeUi-Bold'],
    
               //Anchor the center of the icon image to the coordinate.
               anchor: 'center',
    
               //Allow the icons to overlap.
               allowOverlap: true
           },
    
           filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
       });
    
       map.layers.add(iconLayer);
    
       //When the mouse is over the cluster and icon layers, change the cursor to a pointer.
       map.events.add('mouseover', [clusterBubbleLayer, iconLayer], function() {
           map.getCanvasContainer().style.cursor = 'pointer';
       });
    
       //When the mouse leaves the item on the cluster and icon layers, change the cursor back to the default (grab).
       map.events.add('mouseout', [clusterBubbleLayer, iconLayer], function() {
           map.getCanvasContainer().style.cursor = 'grab';
       });
    
       //Add a click event to the cluster layer. When the user selects a cluster, zoom into it by two levels.  
       map.events.add('click', clusterBubbleLayer, function(e) {
           map.setCamera({
               center: e.position,
               zoom: map.getCamera().zoom + 2
           });
       });
    
       //Add a click event to the icon layer and show the shape that was selected.
       map.events.add('click', iconLayer, function(e) {
           showPopup(e.shapes[0]);
       });
    
       //Add an event to monitor when the map has finished rendering.
       map.events.add('render', function() {
           //Update the data in the list.
           updateListItems();
       });
    });
    
  7. Wanneer de gegevensset van de koffiebar nodig is, moet deze eerst worden gedownload. Nadat het bestand is gedownload, moet het worden gesplitst in regels. De eerste regel bevat de headerinformatie. Om de code gemakkelijker te volgen te maken, parseren we de header in een object, dat we vervolgens kunnen gebruiken om de celindex van elke eigenschap op te zoeken. Na de eerste regel doorloopt u de resterende regels en maakt u een puntelement. Voeg het puntelement toe aan de gegevensbron. Werk tot slot het lijstvenster bij.

    function loadStoreData() {
    
    //Download the store location data.
    fetch(storeLocationDataUrl)
        .then(response => response.text())
        .then(function(text) {
    
            //Parse the tab-delimited file data into GeoJSON features.
            var features = [];
    
            //Split the lines of the file.
            var lines = text.split('\n');
    
            //Grab the header row.
            var row = lines[0].split('\t');
    
            //Parse the header row and index each column to make the code for parsing each row easier to follow.
            var header = {};
            var numColumns = row.length;
            for (var i = 0; i < row.length; i++) {
                header[row[i]] = i;
            }
    
            //Skip the header row and then parse each row into a GeoJSON feature.
            for (var i = 1; i < lines.length; i++) {
                row = lines[i].split('\t');
    
                //Ensure that the row has the correct number of columns.
                if (row.length >= numColumns) {
    
                    features.push(new atlas.data.Feature(new atlas.data.Point([parseFloat(row[header['Longitude']]), parseFloat(row[header['Latitude']])]), {
                        AddressLine: row[header['AddressLine']],
                        City: row[header['City']],
                        Municipality: row[header['Municipality']],
                        AdminDivision: row[header['AdminDivision']],
                        Country: row[header['Country']],
                        PostCode: row[header['PostCode']],
                        Phone: row[header['Phone']],
                        StoreType: row[header['StoreType']],
                        IsWiFiHotSpot: (row[header['IsWiFiHotSpot']].toLowerCase() === 'true') ? true : false,
                        IsWheelchairAccessible: (row[header['IsWheelchairAccessible']].toLowerCase() === 'true') ? true : false,
                        Opens: parseInt(row[header['Opens']]),
                        Closes: parseInt(row[header['Closes']])
                    }));
                }
            }
    
            //Add the features to the data source.
            datasource.add(new atlas.data.FeatureCollection(features));
    
            //Initially, update the list items.
            updateListItems();
        });
    }
    
  8. Wanneer de deelvensterlijst wordt bijgewerkt, wordt de afstand berekend. Deze afstand is van het midden van de kaart tot alle puntelementen in de huidige kaartweergave. De punten worden vervolgens gesorteerd op afstand. Er wordt HTML-code gegenereerd om elke locatie in het lijstvenster weer te geven.

    var listItemTemplate = '<div class="listItem" onclick="itemSelected(\'{id}\')"><div class="listItem-title">{title}</div>{city}<br />Open until {closes}<br />{distance} miles away</div>';
    
    function updateListItems() {
        //Hide the center marker.
        centerMarker.setOptions({
            visible: false
        });
    
        //Get the current camera and view information for the map.
        var camera = map.getCamera();
        var listPanel = document.getElementById('listPanel');
    
        //Check to see if the user is zoomed out a substantial distance. If they are, tell them to zoom in and to perform a search or select the My Location button.
        if (camera.zoom < maxClusterZoomLevel) {
            //Close the pop-up window; clusters might be displayed on the map.  
            popup.close(); 
            listPanel.innerHTML = '<div class="statusMessage">Search for a location, zoom the map, or select the My Location button to see individual locations.</div>';
        } else {
            //Update the location of the centerMarker property.
            centerMarker.setOptions({
                position: camera.center,
                visible: true
            });
    
            //List the ten closest locations in the side panel.
            var html = [], properties;
    
            /*
            Generating HTML for each item that looks like this:
            <div class="listItem" onclick="itemSelected('id')">
                <div class="listItem-title">1 Microsoft Way</div>
                Redmond, WA 98052<br />
                Open until 9:00 PM<br />
                0.7 miles away
            </div>
            */
    
            //Get all the shapes that have been rendered in the bubble layer. 
            var data = map.layers.getRenderedShapes(map.getCamera().bounds, [iconLayer]);
    
            //Create an index of the distances of each shape.
            var distances = {};
    
            data.forEach(function (shape) {
                if (shape instanceof atlas.Shape) {
    
                    //Calculate the distance from the center of the map to each shape and store in the index. Round to 2 decimals.
                    distances[shape.getId()] = Math.round(atlas.math.getDistanceTo(camera.center, shape.getCoordinates(), 'miles') * 100) / 100;
                }
            });
    
            //Sort the data by distance.
            data.sort(function (x, y) {
                return distances[x.getId()] - distances[y.getId()];
            });
    
            data.forEach(function(shape) {
                properties = shape.getProperties();
                html.push('<div class="listItem" onclick="itemSelected(\'', shape.getId(), '\')"><div class="listItem-title">',
                properties['AddressLine'],
                '</div>',
                //Get a formatted addressLine2 value that consists of City, Municipality, AdminDivision, and PostCode.
                getAddressLine2(properties),
                '<br />',
    
                //Convert the closing time to a format that is easier to read.
                getOpenTillTime(properties),
                '<br />',
    
                //Get the distance of the shape.
                distances[shape.getId()],
                ' miles away</div>');
            });
    
            listPanel.innerHTML = html.join('');
    
            //Scroll to the top of the list panel in case the user has scrolled down.
            listPanel.scrollTop = 0;
        }
    }
    
    //This converts a time that's in a 24-hour format to an AM/PM time or noon/midnight string.
    function getOpenTillTime(properties) {
        var time = properties['Closes'];
        var t = time / 100;
        var sTime;
    
        if (time === 1200) {
            sTime = 'noon';
        } else if (time === 0 || time === 2400) {
            sTime = 'midnight';
        } else {
            sTime = Math.round(t) + ':';
    
            //Get the minutes.
            t = (t - Math.round(t)) * 100;
    
            if (t === 0) {
                sTime += '00';
            } else if (t < 10) {
                sTime += '0' + t;
            } else {
                sTime += Math.round(t);
            }
    
            if (time < 1200) {
                sTime += ' AM';
            } else {
                sTime += ' PM';
            }
        }
    
        return 'Open until ' + sTime;
    }
    
    //Create an addressLine2 string that contains City, Municipality, AdminDivision, and PostCode.
    function getAddressLine2(properties) {
        var html = [properties['City']];
    
        if (properties['Municipality']) {
            html.push(', ', properties['Municipality']);
        }
    
        if (properties['AdminDivision']) {
            html.push(', ', properties['AdminDivision']);
        }
    
        if (properties['PostCode']) {
            html.push(' ', properties['PostCode']);
        }
    
        return html.join('');
    }
    
  9. Wanneer de gebruiker een item in het lijstvenster selecteert, wordt de vorm waaraan het item is gerelateerd, opgehaald uit de gegevensbron. Er wordt een pop-upvenster gegenereerd op basis van de eigenschapsgegevens die zijn opgeslagen in de vorm. De kaart centreert zich over de shape. Als de kaart minder dan 700 pixels breed is, wordt de kaartweergave verschoven zodat het pop-upvenster zichtbaar is.

    //When a user selects a result in the side panel, look up the shape by its ID value and display the pop-up window.
    function itemSelected(id) {
        //Get the shape from the data source by using its ID.  
        var shape = datasource.getShapeById(id);
        showPopup(shape);
    
        //Center the map over the shape on the map.
        var center = shape.getCoordinates();
        var offset;
    
        //If the map is fewer than 700 pixels wide, then the layout is set for small screens.
        if (map.getCanvas().width < 700) {
            //When the map is small, offset the center of the map relative to the shape so that there is room for the popup to appear.
            offset = [0, -80];
        }
    
        map.setCamera({
            center: center,
            centerOffset: offset
        });
    }
    
    function showPopup(shape) {
        var properties = shape.getProperties();
    
        /* Generating HTML for the pop-up window that looks like this:
    
            <div class="storePopup">
                <div class="popupTitle">
                    3159 Tongass Avenue
                    <div class="popupSubTitle">Ketchikan, AK 99901</div>
                </div>
                <div class="popupContent">
                    Open until 22:00 PM<br/>
                    <img title="Phone Icon" src="images/PhoneIcon.png">
                    <a href="tel:1-800-XXX-XXXX">1-800-XXX-XXXX</a>
                    <br>Amenities:
                    <img title="Wi-Fi Hotspot" src="images/WiFiIcon.png">
                    <img title="Wheelchair Accessible" src="images/WheelChair-small.png">
                </div>
            </div>
        */
    
         //Calculate the distance from the center of the map to the shape in miles, round to 2 decimals.
        var distance = Math.round(atlas.math.getDistanceTo(map.getCamera().center, shape.getCoordinates(), 'miles') * 100)/100;
    
        var html = ['<div class="storePopup">'];
        html.push('<div class="popupTitle">',
            properties['AddressLine'],
            '<div class="popupSubTitle">',
            getAddressLine2(properties),
            '</div></div><div class="popupContent">',
    
            //Convert the closing time to a format that's easier to read.
            getOpenTillTime(properties),
    
            //Add the distance information.  
            '<br/>', distance,
            ' miles away',
            '<br /><img src="images/PhoneIcon.png" title="Phone Icon"/><a href="tel:',
            properties['Phone'],
            '">',  
            properties['Phone'],
            '</a>'
        );
    
        if (properties['IsWiFiHotSpot'] || properties['IsWheelchairAccessible']) {
            html.push('<br/>Amenities: ');
    
            if (properties['IsWiFiHotSpot']) {
                html.push('<img src="images/WiFiIcon.png" title="Wi-Fi Hotspot"/>');
            }
    
            if (properties['IsWheelchairAccessible']) {
                html.push('<img src="images/WheelChair-small.png" title="Wheelchair Accessible"/>');
            }
        }
    
        html.push('</div></div>');
    
        //Update the content and position of the pop-up window for the specified shape information.
        popup.setOptions({
    
            //Create a table from the properties in the feature.
            content:  html.join(''),
            position: shape.getCoordinates()
        });
    
        //Open the pop-up window.
        popup.open(map);
    }
    

Nu hebt u een volledig functionele winkelzoeker. Open het index.html-bestand in een webbrowser. Wanneer de clusters op de kaart worden weergegeven, kunt u op een van de volgende manieren naar een locatie zoeken:

  1. Het zoekvak.
  2. De knop Mijn locatie selecteren
  3. Een cluster selecteren
  4. Zoom in op de kaart om afzonderlijke locaties weer te geven.

De eerste keer dat een gebruiker de knop Mijn locatie selecteert, geeft de browser een beveiligingswaarschuwing weer die om toestemming vraagt ​​voor toegang tot de locatie van de gebruiker. Als de gebruiker ermee instemt om zijn/haar locatie te delen, zoomt de kaart in op de locatie van de gebruiker en worden nabijgelegen koffiebars getoond.

Schermafbeelding van de vraag van de browser om toegang tot de locatie van de gebruiker

Wanneer u sterk genoeg inzoomt op een gebied met koffiebarlocaties, worden de clusters gescheiden in afzonderlijke locaties. Selecteer een van de pictogrammen op de kaart of selecteer een item in het zijpaneel om een ​​pop-upvenster te bekijken. In het pop-upvenster wordt informatie weergegeven voor de geselecteerde locatie.

Schermopname van de voltooide winkelzoeker.

Als u het formaat van het browservenster wijzigt in minder dan 700 pixels breed of de toepassing opent op een mobiel apparaat, is de indeling beter geschikt voor kleinere schermen.

Schermopname van de versie van de winkelzoeker voor kleine schermen

In deze zelfstudie hebt u geleerd hoe u een eenvoudige winkellocator kunt maken met Azure Maps. De winkelzoeker die u in deze zelfstudie maakt, heeft mogelijk alle functionaliteit die u nodig hebt. U kunt functies toevoegen aan uw winkelzoeker of meer geavanceerde functies gebruiken voor een meer aangepaste gebruikerservaring:

Aanvullende informatie

Volgende stappen

Voor meer voorbeelden van code en interactieve codering: