Condividi tramite


Esercitazione: Usare Mappe di Azure per creare un localizzatore di punti vendita

Questa esercitazione illustra il processo di creazione di un localizzatore di punti vendita semplice usando Mappe di Azure.

Questa esercitazione illustra come:

  • Creare una nuova pagina Web con l'API Controllo mappa di Azure.
  • Caricare dati personalizzati da un file e visualizzarli in una mappa.
  • Usare il servizio Ricerca di Mappe di Azure per trovare un indirizzo o immettere una query.
  • Ottenere la posizione dell'utente dal browser e visualizzarla sulla mappa.
  • Combinare più livelli per creare simboli personalizzati sulla mappa.
  • Creare cluster di punti dati.
  • Aggiungere controlli zoom alla mappa.

Prerequisiti

Nota

Per altre informazioni sull'autenticazione in Mappe di Azure, vedere Gestire l'autenticazione in Mappe di Azure.

Codice di esempio

Questa esercitazione illustra come creare un localizzatore di punti vendita per una società fittizia denominata Contoso Coffee, insieme ai suggerimenti per estendere il localizzatore di punti vendita con funzionalità aggiuntive.

Per un esempio live di ciò che si sta creando in questa esercitazione, vedere Simple Store Locator (Localizzatore di punti vendita semplice) nel sito degli esempi di codice Mappe di Azure.

Per seguire e coinvolgere più facilmente questa esercitazione, scaricare le risorse seguenti:

Funzionalità del localizzatore di punti vendita

Questa sezione elenca le funzionalità di Mappe di Azure illustrate nell'applicazione localizzatore di punti vendita Contoso Coffee creata in questa esercitazione.

Funzionalità dell'interfaccia utente

  • Logo dello Store nell'intestazione
  • Mappa che supporta la panoramica e lo zoom
  • Pulsante My Location (Posizione personale) in cui eseguire la ricerca sulla posizione corrente dell'utente.
  • Layout di pagina che si adatta in base alla larghezza dello schermo dei dispositivi
  • Una casella di ricerca e un pulsante di ricerca

Funzionalità

  • Un keypress evento aggiunto alla casella di ricerca attiva una ricerca quando l'utente preme INVIO.
  • Quando la mappa si sposta, la distanza di ogni posizione dal centro della mappa viene ricalcolata. L'elenco dei risultati viene aggiornato per visualizzare le posizioni più vicine nella parte superiore della mappa.
  • Quando l'utente seleziona un risultato nell'elenco dei risultati, la mappa viene allineata al centro sulla posizione selezionata e le informazioni sulla posizione vengono visualizzate in una finestra popup.
  • Quando l'utente seleziona una posizione specifica, la mappa attiva una finestra popup.
  • Quando l'utente fa zoom indietro, le posizioni vengono raggruppate in cluster. Ogni cluster è rappresentato da un cerchio con un numero all'interno del cerchio. I cluster si formano e si separano quando l'utente modifica il livello di zoom.
  • La selezione di un cluster esegue lo zoom in due livelli sulla mappa e si centra sulla posizione del cluster.

Progettazione del localizzatore di punti vendita

Lo screenshot seguente mostra il layout generale dell'applicazione localizzatore di punti vendita Contoso Coffee. Per visualizzare e interagire con l'esempio live, vedere l'applicazione di esempio Simple Store Locator nel sito degli esempi di codice Mappe di Azure.

Screenshot che mostra il localizzatore di punti vendita Contoso Coffee Mappe di Azure'applicazione di esempio.

Per massimizzare l'utilità di questo localizzatore di punti vendita, includeremo un layout reattivo che si adatta quando la larghezza dello schermo di un utente è inferiore a 700 pixel. Un layout reattivo semplifica l'uso del localizzatore di punti vendita su un piccolo schermo, ad esempio in un dispositivo mobile. Di seguito è riportato uno screenshot che mostra un esempio del layout con schermo ridotto:

Screenshot che mostra l'aspetto dell'applicazione localizzatore di punti vendita Contoso Coffee in un dispositivo mobile.

Creare il set di dati relativo alla posizione dei punti vendita

Questa sezione descrive come creare un set di dati degli archivi da visualizzare sulla mappa. Il set di dati per il localizzatore Contoso Coffee viene creato all'interno di una cartella di lavoro di Excel. Il set di dati contiene 10.213 località di Contoso Coffee coffee shop distribuite in nove paesi o aree geografiche: il Stati Uniti, il Canada, il Regno Unito, Francia, Germania, Italia, Paesi Bassi, Danimarca e Spagna. Ecco uno screenshot dell'aspetto dei dati:

Screenshot dei dati del localizzatore di archivi in una cartella di lavoro di Excel.

Scaricare il file di Excel contenente il set di dati completo per l'applicazione di esempio localizzatore Contoso Coffee dalla cartella dei dati del repository degli esempi di codice Mappe di Azure in GitHub.

Nella schermata precedente dei dati è possibile effettuare le osservazioni seguenti:

  • Le informazioni sulla posizione vengono archiviate nelle sei colonne seguenti: AddressLine, City, Municipality (county), Amministrazione Division (stato/provincia), PostCode (codice postale) e Country.
  • Le colonne Latitudine e Longitudine contengono le coordinate per ogni posizione contoso Coffee. Se non si dispone di informazioni sulle coordinate, è possibile usare il servizio di ricerca per determinare le coordinate della posizione.
  • Alcune altre colonne contengono metadati correlati ai bar: un numero di telefono, colonne booleane e orari di apertura e chiusura dell'archivio in formato 24 ore. Le colonne booleane contengono i dati per la disponibilità del Wi-Fi e l'accessibilità per disabili. È possibile creare colonne personalizzate contenenti i metadati più pertinenti per i dati di posizione specifici.

Nota

Mappe di Azure esegue il rendering dei dati inProiezione Sferica mercator "EPSG:3857", ma legge i dati in "EPSG:4326" che usano il datum WGS84.

Caricare il set di dati del localizzatore di Contoso Coffee shop

Il set di dati del localizzatore contoso Coffee shop è di piccole dimensioni, quindi può essere convertito in un file di testo delimitato da tabulazioni scaricato dal browser al caricamento dell'applicazione.

Suggerimento

Se il set di dati è troppo grande per il download del client o viene aggiornato di frequente, è consigliabile archiviare il set di dati in un database. Dopo aver caricato i dati in un database, è quindi possibile configurare un servizio Web che accetta query per i dati, quindi invia i risultati al browser dell'utente.

Convertire i dati in un file di testo delimitato da tabulazioni

Per convertire i dati della posizione di Contoso Coffee shop da una cartella di lavoro di Excel in un file di testo delimitato da tabulazioni:

  1. Scaricare la cartella di lavoro di Excel ContosoCoffee.xlsx e aprirla in Excel.

  2. Selezionare Salva > file con nome....

  3. Nell'elenco a discesa Salva come selezionare Testo (delimitato da tabulazione)(*.txt).

  4. Denominare il file ContosoCoffee.

Screenshot della finestra di dialogo Salva con nome.

Se si apre il file di testo in Blocco note, è simile al testo seguente:

Screenshot di un file Blocco note che mostra un set di dati delimitato da tabulazioni.

Configurare il progetto

  1. Aprire Visual Studio Code o scegliere gli ambienti di sviluppo.

  2. Selezionare File > Apri area di lavoro....

  3. Creare una nuova cartella denominata ContosoCoffee.

  4. Selezionare ContosoCoffee nell'elenco delle cartelle.

  5. Creare i tre file seguenti che definiscono il layout, lo stile e la logica per l'applicazione:

    • index.html
    • index.css
    • index.js
  6. Creare una cartella denominata data.

  7. Aggiungere il file ContosoCoffee.txt creato in precedenza dalla cartella di lavoro di Excel ContosoCoffee.xlsx alla cartella di dati .

  8. Creare un'altra cartella denominata images.

  9. Se non è già stato fatto, scaricare le 10 immagini di mapping dalla directory images nel repository GitHub e aggiungerle alla cartella images .

    La cartella dell'area di lavoro dovrebbe ora essere simile alla schermata seguente:

    Screenshot della cartella images nella directory Contoso Coffee.

Creare il codice HTML

Per creare il codice HTML:

  1. Aggiungere i tag seguenti meta all'oggetto head di 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. Aggiungere riferimenti al controllo Web dei file JavaScript e CSS di Mappe di Azure:

    <!-- 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. Aggiungere riferimenti a index.js e 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. Nel corpo del documento aggiungere un tag header. All'interno del tag header aggiungere il logo e il nome della società.

    <header>
        <img src="images/Logo.png" />
        <span>Contoso Coffee</span>
    </header>
    
  5. Aggiungere il tag main e creare un pannello di ricerca che include una casella di testo e un pulsante di ricerca. Aggiungere anche riferimenti div per la mappa, il pannello elenchi e il pulsante GPS My Location.

    <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>
    

Al termine, index.html dovrebbe essere simile a Simple Store Locator.html nel codice di esempio dell'esercitazione.

Definire gli stili CSS

Il passaggio successivo consiste nel definire gli stili CSS. Gli stili CSS consentono di definire il modo in cui i componenti dell'applicazione vengono disposti e l'aspetto dell'applicazione.

  1. Aprire index.css.

  2. Aggiungere il codice CSS seguente:

    Nota

    Lo stile @media definisce le opzioni di stile alternative da usare quando la larghezza dello schermo è inferiore a 700 pixel.

     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);
         }
     }
    

Se si esegue l'applicazione a questo punto, viene visualizzata l'intestazione, la casella di ricerca e il pulsante di ricerca. Tuttavia, la mappa non è visibile perché non è ancora stata caricata. Se si prova a eseguire una ricerca, non si ottiene alcun risultato. La sezione successiva descrive l'aggiunta della logica JavaScript necessaria per accedere a tutte le funzionalità del localizzatore di punti vendita.

Aggiungere il codice JavaScript

Il codice JavaScript nell'app localizzatore Contoso Coffee shop consente i processi seguenti:

  1. Aggiunge un listener di eventi chiamato ready per attendere il completamento del processo di caricamento della pagina. Al termine del caricamento della pagina, il gestore eventi crea più listener di eventi per monitorare il caricamento della mappa e fornire funzionalità ai pulsanti di ricerca e Posizione personale.

  2. Quando l'utente seleziona il pulsante di ricerca o digita un percorso nella casella di ricerca e quindi preme INVIO, inizia una ricerca fuzzy sulla query dell'utente. Il codice passa una matrice di valori ISO 2 paese all'opzione countrySet per limitare i risultati della ricerca a tali paesi/aree geografiche. La limitazione dei paesi/aree geografiche in cui eseguire la ricerca consente di migliorare la precisione dei risultati restituiti.

  3. Al termine della ricerca, il primo risultato della posizione viene usato come stato attivo centrale della mappa. Quando l'utente seleziona il pulsante My Location (Posizione personale), il codice recupera la posizione dell'utente usando l'API georilevazione HTML5 integrata nel browser. Una volta recuperata la posizione, il codice centra la mappa sulla posizione dell'utente.

Per aggiungere JavaScript:

  1. Aprire index.js.

  2. Aggiungere opzioni globali per semplificare l'aggiornamento delle impostazioni. Definire le variabili per la mappa, la finestra popup, l'origine dati, il livello icona e l'indicatore HTML. Impostare l'indicatore HTML affinché indichi il centro di un'area di ricerca e definire un'istanza del client del servizio di ricerca di Mappe di Azure.

    //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. Aggiungere il codice di inizializzazione seguente. Assicurarsi di sostituire <Your Azure Maps Key> con la chiave di sottoscrizione Mappe di Azure.

    Suggerimento

    Quando si usano finestre popup, è consigliabile creare una singola istanza di Popup e riutilizzare l'istanza caricandone il contenuto e la posizione. Per ogni istanza di Popup aggiunta al codice, più elementi DOM vengono aggiunti alla pagina. Maggiore il numero di elementi DOM presenti nella pagina, maggiore sarà il numero di elementi di cui il browser deve tenere traccia. Se il numero di elementi è troppo elevato, è possibile che il browser risulti lento.

    
    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. Nel gestore eventi della ready mappa aggiungere un controllo zoom e un indicatore HTML per visualizzare il centro di un'area di ricerca.

    //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. Nel gestore eventi della ready mappa aggiungere un'origine dati. Effettuare quindi una chiamata per il caricamento e l'analisi del set di dati. Abilitare il clustering sull'origine dati. Il clustering sull'origine dati raggruppa i punti sovrapposti in un cluster. Quando l'utente esegue lo zoom avanti, i cluster si separano in singoli punti. Questo comportamento offre un'esperienza utente ottimale e migliora le prestazioni.

    //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. Dopo il caricamento del set di dati nel gestore eventi della ready mappa, definire un set di livelli per il rendering dei dati. Un livello bolla esegue il rendering dei punti dati raggruppati. Un livello simbolo esegue il rendering del numero di punti in ogni cluster sopra il livello bolla. Un secondo livello di simboli esegue il rendering di un'icona personalizzata per singole posizioni sulla mappa.

    Aggiungere gli eventi mouseover e mouseout ai livelli a bolle e di icone per modificare il cursore del mouse quando l'utente lo posiziona sopra un cluster o un'icona sulla mappa. Aggiungere un evento click al livello a bolle del cluster. Questo evento click ingrandisce la mappa di due livelli e la centra rispetto a un cluster quando l'utente seleziona un cluster. Aggiungere un evento click al livello di icone. Questo evento click mostra una finestra popup che visualizza i dettagli di un bar quando un utente seleziona un'icona relativa a una singola posizione. Aggiungere un evento alla mappa per monitorare il momento in cui viene completato lo spostamento della mappa. Quando questo evento viene generato, aggiornare gli elementi nel pannello elenchi.

    //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. Quando è necessario il set di dati del negozio di caffè, è necessario prima scaricarlo. Una volta scaricato, il file deve essere suddiviso in righe. La prima riga contiene le informazioni dell'intestazione. Per semplificare la lettura del codice, l'intestazione viene analizzata in un oggetto, che può essere quindi usato per cercare l'indice delle celle di ogni proprietà. Dopo la prima riga, eseguire il ciclo delle righe rimanenti e creare una funzionalità punto. Aggiungere la funzionalità punto all'origine dati. Aggiornare infine il pannello elenchi.

    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. Quando viene aggiornato il pannello elenchi, viene calcolata la distanza. Si tratta della distanza dal centro della mappa a tutte le funzionalità punto della visualizzazione mappa corrente. Le funzionalità vengono quindi ordinate in base alla distanza. Viene generato codice HTML per visualizzare ogni posizione nel pannello elenchi.

    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. Quando l'utente seleziona un elemento dal pannello elenchi, la forma correlata all'elemento viene recuperata dall'origine dati. Viene generata una finestra popup basata sulle informazioni relative alle proprietà archiviate nella forma. La mappa si allinea al centro della forma. Se la mappa ha una larghezza inferiore a 700 pixel, la visualizzazione mappa viene spostata in modo che la finestra popup sia visibile.

    //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);
    }
    

È ora disponibile un localizzatore di punti vendita completamente funzionante. Aprire il file index.html in un Web browser. Quando i cluster vengono visualizzati sulla mappa, è possibile cercare una posizione usando uno dei metodi seguenti:

  1. Casella di ricerca.
  2. Selezione del pulsante My Location (Posizione personale)
  3. Selezione di un cluster
  4. Zoom avanti sulla mappa per visualizzare le singole posizioni.

Quando un utente seleziona il pulsante My Location per la prima volta, il browser mostra un avviso di sicurezza che richiede l'autorizzazione per accedere alla sua posizione. Se l'utente accetta di condividere la propria posizione, la mappa viene ingrandita per visualizzarla e vengono mostrati i bar presenti nelle vicinanze.

Screenshot della richiesta del browser di accedere alla posizione dell'utente

Quando si ingrandisce a sufficienza un'area che include posizioni dei bar, i cluster si separano in singole posizioni. Selezionare una delle icone sulla mappa oppure selezionare un elemento nel pannello laterale per visualizzare una finestra popup che mostra le informazioni per tale posizione.

Screenshot del localizzatore di punti vendita completato.

Se si ridimensiona la finestra del browser a una larghezza inferiore a 700 pixel o si apre l'applicazione in un dispositivo mobile, il layout diventa più adatto per schermi più piccoli.

Screenshot della versione del localizzatore di punti vendita per schermi di piccole dimensioni

In questa esercitazione è stato illustrato come creare un localizzatore di punti vendita di base con Mappe di Azure. Il localizzatore di punti vendita creato in questa esercitazione può includere tutte le funzionalità necessarie. È possibile aggiungere funzionalità al localizzatore di punti vendita o usare funzionalità più avanzate per ottenere un'esperienza utente più personalizzata:

Informazioni aggiuntive

Passaggi successivi

Per altri esempi di codice e un'esperienza di codifica interattiva: