Kurz: Vytvoření lokátoru úložiště pomocí Azure Mapy

Tento kurz vás provede procesem vytvoření jednoduchého lokátoru úložiště pomocí Azure Mapy.

V tomto kurzu se naučíte:

  • Vytvořte novou webovou stránku pomocí rozhraní API pro mapové ovládací prvky Azure.
  • Načtěte vlastní data ze souboru a zobrazte je na mapě.
  • Pomocí azure Mapy Search vyhledejte adresu nebo zadejte dotaz.
  • Získejte polohu uživatele z prohlížeče a zobrazte ho na mapě.
  • Zkombinujte více vrstev a vytvořte na mapě vlastní symboly.
  • Datové body clusteru
  • Přidejte do mapy ovládací prvky lupy.

Požadavky

Poznámka:

Další informace o ověřování v Azure Mapy najdete v tématu správa ověřování v Azure Mapy.

Ukázkový kód

Tento kurz ukazuje, jak vytvořit lokátor obchodu pro fiktivní společnost Contoso Coffee spolu s tipy pro rozšíření lokátoru obchodu o další funkce.

Pokud se chcete podívat na živou ukázku toho, co vytváříte v tomto kurzu, podívejte se na téma Simple Store Locator na webu s ukázkami kódu v Azure Mapy.

Pokud chcete lépe postupovat a zapojit se do tohoto kurzu, stáhněte si následující zdroje informací:

Funkce lokátoru storu

Tato část obsahuje seznam funkcí Azure Mapy, které jsou ukázané v aplikaci lokátoru Contoso Coffee Store vytvořené v tomto kurzu.

Funkce uživatelského rozhraní

  • Logo obchodu v záhlaví
  • Mapa, která podporuje posouvání a přiblížení
  • Tlačítko Moje umístění pro vyhledání aktuálního umístění uživatele
  • Rozložení stránky, které se upravuje na základě šířky obrazovky zařízení
  • Vyhledávací pole a tlačítko hledání

Funkce

  • Událost keypress přidaná do vyhledávacího pole aktivuje hledání, když uživatel stiskne klávesu Enter.
  • Když se mapa přesune, vzdálenost k jednotlivým místům od středu mapy se přepočítá. Seznam výsledků se aktualizuje, aby se zobrazila nejbližší umístění v horní části mapy.
  • Když uživatel vybere výsledek v seznamu výsledků, mapa se zacentruje nad vybraným umístěním a informace o umístění se zobrazí v automaticky otevíraných otevíraných oknech.
  • Když uživatel vybere konkrétní umístění, mapa aktivuje automaticky otevírané okno.
  • Když se uživatel oddálit, umístění se seskupí do clusterů. Každý shluk je reprezentován kruhem s číslem uvnitř kruhu. Clustery se formují a oddělují, protože uživatel změní úroveň přiblížení.
  • Výběr clusteru se zvětšuje ve dvou úrovních na mapě a zacentruje umístění clusteru.

Návrh lokátoru storu

Následující snímek obrazovky ukazuje obecné rozložení aplikace lokátoru Contoso Coffee Store. Pokud chcete zobrazit živou ukázku a pracovat s ní, podívejte se na ukázkovou aplikaci Simple Store Locator na webu s ukázkami kódu Azure Mapy.

A screenshot showing the Contoso Coffee store locator Azure Maps sample application.

Abychom maximalizovali užitečnost tohoto lokátoru obchodu, zahrneme responzivní rozložení, které se upraví, když je šířka obrazovky uživatele menší než 700 pixelů. Responzivní rozložení usnadňuje použití lokátoru obchodu na malé obrazovce, například na mobilním zařízení. Tady je snímek obrazovky znázorňující ukázku rozložení s malou obrazovkou:

A screenshot showing what the Contoso Coffee store locator application looks like on a mobile device.

Vytvoření datové sady umístění úložiště

Tato část popisuje, jak vytvořit datovou sadu úložišť, která chcete zobrazit na mapě. Datová sada pro lokátor Contoso Coffee se vytvoří v excelovém sešitu. Datová sada obsahuje 10 213 lokalit kávovaru Contoso Coffee, která se nacházejí v devíti zemích nebo oblastech: USA, Kanadě, Spojeném království, Francii, Německu, Itálii, Nizozemsku, Dánsku a Španělsku. Tady je snímek obrazovky s tím, jak data vypadají:

Screenshot of the store locator data in an Excel workbook.

Stáhněte si excelový soubor obsahující úplnou datovou sadu ukázkové aplikace Contoso Coffee locator ze složky s daty úložiště ukázek kódu Azure Mapy na GitHubu.

Na výše uvedeném snímku obrazovky s daty můžeme provést následující pozorování:

  • Informace o poloze jsou uloženy v následujících šesti sloupcích: AddressLine, City, Municipality (county), Správa Division (state/province), PostCode (PSČ) a Country.
  • Sloupce Zeměpisná šířka a Zeměpisná délka obsahují souřadnice pro každé umístění contoso Coffee. Pokud nemáte souřadnicové informace, můžete pomocí Search určit souřadnice umístění.
  • Některé další sloupce obsahují metadata, která souvisí s kávovary: telefonní číslo, logické sloupce a otevírání a zavírání obchodů ve 24hodinovém formátu. Logické sloupce jsou určené pro Wi-Fi a bezbariérový přístup. Můžete vytvořit vlastní sloupce, které obsahují metadata, která jsou pro vaše data o poloze relevantnější.

Poznámka:

Azure Mapy vykresluje data v projekci "EPSG:3857", ale čte data v EPSG:4326, která používají datum WGS84.

Načtení datové sady lokátoru lokátoru coffee shopu Contoso

Datová sada lokátoru lokátoru Contoso Coffee shop je malá, takže ji můžete převést na textový soubor oddělený tabulátorem, který prohlížeč stáhne při načítání aplikace.

Tip

Pokud je vaše datová sada příliš velká pro stahování klienta nebo se často aktualizuje, můžete zvážit uložení datové sady do databáze. Po načtení dat do databáze můžete nastavit webovou službu, která přijímá dotazy na data, a pak výsledky odešle do prohlížeče uživatele.

Převod dat na textový soubor oddělený tabulátorem

Převod dat o poloze v obchodě Contoso Coffee shop z excelového sešitu na textový soubor oddělený tabulátorem:

  1. Stáhněte si excelový sešit ContosoCoffee.xlsx a otevřete ho v Excelu.

  2. Vyberte Soubor > uložit jako....

  3. V rozevíracím seznamu Uložit jako typ vyberte Text (oddělený tabulátorem)(*.txt).

  4. Pojmenujte soubor ContosoCoffee.

Screenshot of the Save as type dialog box.

Pokud textový soubor otevřete v Poznámkový blok, bude vypadat podobně jako v následujícím textu:

Screenshot of a Notepad file that shows a tab-delimited dataset.

Nastavení projektu

  1. Otevřete Visual Studio Code nebo výběr vývojových prostředí.

  2. Vyberte Soubor > otevřít pracovní prostor....

  3. Vytvořte novou složku s názvem ContosoCoffee.

  4. V průzkumníku vyberte ContosoCoffee .

  5. Vytvořte následující tři soubory, které definují rozložení, styl a logiku pro aplikaci:

    • index.html
    • index.css
    • index.js
  6. Vytvořte složku s názvem data.

  7. Do datové složky přidejte soubor ContosoCoffee.txt, který jste předtím vytvořili z excelového sešitu ContosoCoffee.xlsx.

  8. Vytvořte další složku s názvem obrázky.

  9. Pokud jste to ještě neudělali, stáhněte si 10 obrázků z adresáře obrázků v úložišti GitHub a přidejte je do složky obrázků .

    Složka pracovního prostoru by teď měla vypadat jako na následujícím snímku obrazovky:

    Screenshot of the images folder in the Contoso Coffee directory.

Vytvoření KÓDU HTML

Vytvoření kódu HTML:

  1. Do souboru index.html přidejte následující meta značky head:

    <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. Přidejte odkazy na soubory JavaScriptu a CSS webového ovládacího prvku Azure Mapy:

    <!-- 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. Dále přidejte odkaz na modul Azure Mapy Services. Tento modul je javascriptová knihovna, která zabalí služby Azure Mapy REST, což usnadňuje jejich použití v JavaScriptu. Modul Služby je užitečný pro výkon funkcí vyhledávání.

    <!-- Add a reference to the Azure Maps Services Module JavaScript file. -->
    <script src="https://atlas.microsoft.com/sdk/javascript/service/2/atlas-service.min.js"></script>
    
  4. Přidejte odkazy na index.js a 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>
    
  5. Do textu dokumentu přidejte header značku. Do značky header přidejte logo a název společnosti.

    <header>
        <img src="images/Logo.png" />
        <span>Contoso Coffee</span>
    </header>
    
  6. main Přidejte značku a vytvořte vyhledávací panel s textovým polem a tlačítkem hledat. Můžete také přidat div odkazy na mapu, seznam panelu a tlačítko Moje poloha GPS.

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

Po dokončení by index.html měl v ukázkovém kódu kurzu vypadat jako Simple Store Locator.html .

Definování stylů CSS

Dalším krokem je definování stylů CSS. Styly CSS definují, jak jsou součásti aplikace rozloženy a jak vypadá aplikace.

  1. Otevřete index.css.

  2. Přidejte následující kód css:

    Poznámka:

    Styl @media definuje alternativní možnosti stylu, které se mají použít, když je šířka obrazovky menší než 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);
         }
     }
    

Pokud aplikaci spustíte v tomto okamžiku, zobrazí se záhlaví, vyhledávací pole a tlačítko hledání. Mapa ale není viditelná, protože ještě nebyla načtena. Pokud se pokusíte provést hledání, nic se nestane. Další část popisuje přidání logiky JavaScriptu potřebné pro přístup ke všem funkcím lokátoru úložiště.

Přidání kódu JavaScript

Kód JavaScriptu v aplikaci lokátoru Contoso Coffee shop umožňuje následující procesy:

  1. Přidá naslouchací proces události, který volá, ready aby čekal, dokud stránka nedokončí proces načítání. Po dokončení načítání stránky obslužná rutina události vytvoří další naslouchací procesy událostí, které budou monitorovat načítání mapy, a poskytovat funkce tlačítkům hledání a umístění .

  2. Když uživatel vybere tlačítko hledání nebo zadá do vyhledávacího pole umístění a pak stiskne enter, začne vyhledávání přibližných shod s dotazem uživatele. Kód předá pole hodnot ISO 2 země/oblasti do countrySet možnosti omezit výsledky hledání na tyto země/oblasti. Omezení zemí nebo oblastí na vyhledávání pomáhá zvýšit přesnost vrácených výsledků.

  3. Po dokončení hledání se první výsledek umístění použije jako střed mapy. Když uživatel vybere tlačítko Moje místo, kód načte umístění uživatele pomocí rozhraní API geografické polohy HTML5, které je integrované v prohlížeči. Jakmile se umístění načte, kód zacentruje mapu přes umístění uživatele.

Přidání JavaScriptu:

  1. Otevřete index.js.

  2. Přidejte globální možnosti, které usnadňují aktualizaci nastavení. Definujte proměnné pro mapu, automaticky otevírané okno, zdroj dat, vrstvu ikon a značku HTML. Nastavte značku HTML tak, aby označí střed vyhledávací oblasti. Definujte instanci klienta vyhledávací služby Azure Mapy.

    //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, searchURL;
    
    // 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. Přidejte následující inicializační kód. Nezapomeňte nahradit <Your Azure Maps Key> klíčem předplatného Azure Mapy.

    Tip

    Při použití automaticky otevíraných oken je nejlepší vytvořit jednu Popup instanci a znovu ji znovu použít aktualizací jejího obsahu a pozice. Pro každou Popupinstanci, kterou přidáte do kódu, se na stránku přidá několik prvků DOM. Čím více prvků DOM je na stránce, tím více toho musí prohlížeč sledovat. Pokud existuje příliš mnoho položek, může se prohlížeč zpomalit.

    
    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();
    
        //Use MapControlCredential to share authentication between a map control and the service module.
        var pipeline = atlas.service.MapsURL.newPipeline(new atlas.service.MapControlCredential(map));
    
        //Create an instance of the SearchURL client.
        searchURL = new atlas.service.SearchURL(pipeline);
    
        //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;
    
        //Perform a fuzzy search on the users query.
        searchURL.searchFuzzy(atlas.service.Aborter.timeout(3000), query, {
            //Pass in the array of country/region ISO2 for which we want to limit the search to.
            countrySet: countrySet,
            view: 'Auto'
        }).then(results => {
            //Parse the response into GeoJSON so that the map can understand.
            var data = results.geojson.getFeatures();
    
            if (data.features.length > 0) {
                //Set the camera to the bounds of the results.
                map.setCamera({
                    bounds: data.features[0].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. Do obslužné rutiny události mapy ready přidejte ovládací prvek lupy a značku HTML, aby se zobrazil střed vyhledávací oblasti.

    //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. Do obslužné rutiny ready události mapy přidejte zdroj dat. Potom proveďte volání pro načtení a parsování datové sady. Povolte clustering ve zdroji dat. Clustering ve skupinách zdrojů dat překrývající se body v clusteru Když se uživatel přibližuje, clustery se oddělují do jednotlivých bodů. Toto chování poskytuje lepší uživatelské prostředí a zlepšuje výkon.

    //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. Po načtení datové sady do obslužné rutiny události mapy ready definujte sadu vrstev pro vykreslení dat. Bublinová vrstva vykresluje skupinové datové body. Vrstva symbolů vykreslí počet bodů v každém clusteru nad vrstvou bubliny. Druhá vrstva symbolů vykreslí vlastní ikonu pro jednotlivá umístění na mapě.

    Přidejte mouseover do vrstev bublin a ikon události a mouseout změňte kurzor myši, když uživatel najede myší na cluster nebo ikonu na mapě. click Přidejte událost do vrstvy bublin clusteru. Tato click událost se přiblíží na mapě dvěma úrovněmi a vycentruje mapu přes cluster, když uživatel vybere libovolný cluster. click Přidejte událost do vrstvy ikon. Tato click událost zobrazí automaticky otevírané okno s podrobnostmi o kavárně, když uživatel vybere ikonu místa. Přidejte do mapy událost, která se má monitorovat, až se mapa dokončí. Když se tato událost aktivuje, aktualizujte položky na panelu seznamu.

    //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. Pokud je potřeba datová sada v kavárně, musí se nejdřív stáhnout. Po stažení musí být soubor rozdělený na řádky. První řádek obsahuje informace o záhlaví. Abychom usnadnili sledování kódu, parsujeme záhlaví do objektu, který pak můžeme použít k vyhledání indexu buňky každé vlastnosti. Za prvním řádkem projděte zbývající čáry a vytvořte funkci bodu. Přidejte funkci bodu do zdroje dat. Nakonec aktualizujte panel seznamu.

    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. Po aktualizaci panelu seznamu se vypočítá vzdálenost. Tato vzdálenost je od středu mapy až po všechny funkce bodů v aktuálním zobrazení mapy. Funkce se pak seřadí podle vzdálenosti. Html se vygeneruje pro zobrazení jednotlivých umístění na panelu seznamu.

    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. Když uživatel vybere položku na panelu seznamu, obrazec, ke kterému položka souvisí, se načte ze zdroje dat. Automaticky otevírané okno se vygeneruje na základě informací o vlastnosti uložených v obrazci. Mapa se zacentruje nad obrazcem. Pokud je mapa menší než 700 pixelů široká, je zobrazení mapy posunuté, aby se zobrazilo automaticky otevírané okno.

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

Teď máte plně funkční lokátor úložiště. Otevřete soubor index.html ve webovém prohlížeči. Když se clustery zobrazí na mapě, můžete vyhledat umístění pomocí některé z následujících metod:

  1. Vyhledávací pole.
  2. Výběr tlačítka Moje umístění
  3. Výběr clusteru
  4. Přiblížením na mapě zobrazíte jednotlivá umístění.

Když uživatel poprvé vybere tlačítko Moje místo, zobrazí se v prohlížeči upozornění zabezpečení, které požádá o oprávnění pro přístup k umístění uživatele. Pokud uživatel souhlasí se sdílením své polohy, mapa se přiblíží na místě uživatele a zobrazí se blízké kavárny.

Screenshot of the browser's request to access the user's location

Když přiblížíte dostatečně blízko v oblasti s umístěními v kavárně, clustery se rozdělí na jednotlivá místa. Výběrem jedné z ikon na mapě nebo výběrem položky na bočním panelu zobrazíte automaticky otevírané okno. Automaticky otevírané okno zobrazí informace o vybraném umístění.

Screenshot of the finished store locator.

Pokud změníte velikost okna prohlížeče na méně než 700 pixelů na šířku nebo otevřete aplikaci na mobilním zařízení, změní se rozložení, které bude vhodnější pro menší obrazovky.

Screenshot of the small-screen version of the store locator

V tomto kurzu jste zjistili, jak vytvořit základní lokátor úložiště pomocí Azure Mapy. Lokátor obchodu, který vytvoříte v tomto kurzu, může mít všechny funkce, které potřebujete. Do lokátoru obchodu můžete přidat funkce nebo použít pokročilejší funkce pro vlastní uživatelské prostředí:

Další informace

Další kroky

Další příklady kódu a prostředí pro interaktivní psaní kódu: