Samouczek: tworzenie lokalizatora sklepów przy użyciu usługi Azure Maps
Ten samouczek przeprowadzi Cię przez proces tworzenia prostego lokalizatora sklepów przy użyciu usługi Azure Maps.
Z tego samouczka dowiesz się, jak wykonywać następujące czynności:
- Tworzenie nowej strony internetowej przy użyciu interfejsu API kontrolki mapy platformy Azure.
- Ładowanie danych niestandardowych z pliku i wyświetlanie ich na mapie.
- Używanie usługi wyszukiwania usługi Azure Maps w celu znalezienia adresu lub wprowadzenia zapytania.
- Uzyskiwanie lokalizacji użytkownika z przeglądarki i wyświetlanie jej na mapie.
- Łączenie wielu warstw w celu utworzenia symboli niestandardowych na mapie.
- Punkty danych klastra.
- Dodawanie kontrolek powiększenia do mapy.
Wymagania wstępne
- Program Visual Studio Code jest zalecany w tym samouczku, ale można użyć dowolnego odpowiedniego zintegrowanego środowiska projektowego (IDE).
- Konto usługi Azure Maps
- Klucz subskrypcji
Uwaga
Aby uzyskać więcej informacji na temat uwierzytelniania w usłudze Azure Maps, zobacz Zarządzanie uwierzytelnianiem w usłudze Azure Maps.
Przykładowy kod
W tym samouczku pokazano, jak utworzyć lokalizator sklepów dla fikcyjnej firmy o nazwie Contoso Coffee, wraz z poradami dotyczącymi rozszerzania lokalizatora sklepów z dodatkowymi funkcjami.
Aby zobaczyć na żywo przykład tego, co tworzysz w tym samouczku, zobacz Prosty lokalizator sklepów w witrynie Przykłady kodu usługi Azure Maps.
Aby ułatwić śledzenie i angażowanie się w ten samouczek, pobierz następujące zasoby:
- Kod źródłowy lokalizatora prostego sklepu .
- Przechowywanie danych lokalizacji używanych do importowania do zestawu danych lokalizatora sklepów.
- Obrazy mapy.
Funkcje lokalizatora sklepów
W tej sekcji wymieniono funkcje usługi Azure Maps, które przedstawiono w aplikacji lokalizatora sklepów Contoso Coffee utworzonej w tym samouczku.
Funkcje interfejsu użytkownika
- Logo sklepu w nagłówku
- Mapa, która obsługuje przesuwanie i powiększanie
- Przycisk Moja lokalizacja do wyszukiwania w bieżącej lokalizacji użytkownika.
- Układ strony, który dostosowuje się na podstawie szerokości ekranu urządzeń
- Pole wyszukiwania i przycisk wyszukiwania
Funkcje
- Zdarzenie
keypress
dodane do pola wyszukiwania wyzwala wyszukiwanie po naciśnięciu Enter przez użytkownika. - Gdy mapa zostanie przeniesiona, odległość do każdej lokalizacji z środka mapy zostanie ponownie obliczona. Lista wyników zostanie zaktualizowana, aby wyświetlić najbliższe lokalizacje w górnej części mapy.
- Gdy użytkownik wybierze wynik na liście wyników, mapa zostanie wyśrodkowana nad wybraną lokalizacją, a informacje o lokalizacji pojawią się w oknie podręcznym.
- Gdy użytkownik wybierze określoną lokalizację, mapa wyzwoli okno podręczne.
- Gdy użytkownik pomniejsza mapę, lokalizacje są grupowane w klastrach. Każdy klaster jest reprezentowany przez okrąg z liczbą wewnątrz okręgu. Klastry tworzą się i rozdzielają, gdy użytkownik zmienia poziom powiększenia.
- Wybranie klastra powiększa dwa poziomy na mapie i koncentruje się na lokalizacji klastra.
Projekt lokalizatora sklepów
Poniższy zrzut ekranu przedstawia ogólny układ aplikacji lokalizatora sklepu do kawy Contoso. Aby wyświetlić przykład na żywo i korzystać z niej, zobacz przykładową aplikację Simple Store Locator w witrynie Przykłady kodu usługi Azure Maps.
Aby zmaksymalizować przydatność tego lokalizatora sklepów, dołączymy układ dynamiczny, który dostosowuje się, jeśli szerokość ekranu użytkownika jest mniejsza niż 700 pikseli. Układ dynamiczny ułatwia używanie lokalizatora sklepów na małym ekranie, na przykład na urządzeniu przenośnym. Oto zrzut ekranu przedstawiający przykład układu małego ekranu:
Tworzenie zestawu danych lokalizacji sklepów
W tej sekcji opisano sposób tworzenia zestawu danych magazynów, które mają być wyświetlane na mapie. Zestaw danych lokalizatora kawy Firmy Contoso jest tworzony wewnątrz skoroszytu programu Excel. Zestaw danych zawiera 10 213 lokalizacji kawiarni Contoso Coffee rozmieszczonych w dziewięciu krajach lub regionach: Stany Zjednoczone, Kanada, Wielka Brytania, Francja, Niemcy, Włochy, Holandia, Dania i Hiszpania. Oto zrzut ekranu przedstawiający te dane:
Pobierz plik programu Excel zawierający pełny zestaw danych przykładowej aplikacji lokalizatora kawy Firmy Contoso z folderu danych repozytorium przykładów kodu usługi Azure Maps w usłudze GitHub.
Na powyższym zrzucie ekranu danych możemy wykonać następujące obserwacje:
- Informacje o lokalizacji są przechowywane w następujących sześciu kolumnach: AddressLine, City, Municipality (county), AdminDivision (stan/prowincja), PostCode (kod pocztowy) i Country ( Kraj).
- Kolumny Szerokość geograficzna i Długość geograficzna zawierają współrzędne dla każdej lokalizacji contoso Coffee. Jeśli nie masz informacji o współrzędnych, możesz użyć usługa wyszukiwania do określenia współrzędnych lokalizacji.
- Niektóre inne kolumny zawierają metadane powiązane z kawiarniami: numer telefonu, kolumny logiczne oraz czasy otwierania i zamykania sklepu w formacie 24-godzinnym. Kolumny logiczne dotyczą ułatwień dostępu do sieci Wi-Fi i wózków inwalidzkich. Możesz utworzyć własne kolumny zawierające metadane, które są bardziej istotne dla danych lokalizacji.
Uwaga
Usługa Azure Maps renderuje dane w projekcji Spherical Mercator "EPSG:3857", ale odczytuje dane w formacie "EPSG:4326", które używają dat WGS84.
Ładowanie zestawu danych lokalizatora kawiarni Contoso
Zestaw danych lokalizatora kawiarni Contoso jest mały, więc można go przekonwertować na plik tekstowy rozdzielany tabulatorami, który przeglądarka pobiera podczas ładowania aplikacji.
Napiwek
Jeśli zestaw danych jest zbyt duży w przypadku pobierania klienta lub jest często aktualizowany, możesz rozważyć przechowywanie zestawu danych w bazie danych. Po załadowaniu danych do bazy danych możesz skonfigurować usługę internetową, która akceptuje zapytania dotyczące danych, a następnie wysyła wyniki do przeglądarki użytkownika.
Konwertowanie danych na plik tekstowy rozdzielany tabulatorami
Aby przekonwertować dane lokalizacji sklepu kawowego Contoso ze skoroszytu programu Excel na plik tekstowy rozdzielany tabulatorami:
Pobierz skoroszyt programu Excel ContosoCoffee.xlsx i otwórz go w programie Excel.
Wybierz pozycję Plik > Zapisz jako....
Na liście rozwijanej Zapisz jako typ wybierz pozycję Tekst (rozdzielany znakami tabulacji)(*.txt).
Nadaj plikowi nazwę ContosoCoffee.
Jeśli otworzysz plik tekstowy w Notatniku, będzie on podobny do następującego tekstu:
Konfigurowanie projektu
Otwórz program Visual Studio Code lub wybrane środowiska programistyczne.
Wybierz pozycję Plik > Otwórz obszar roboczy....
Utwórz nowy folder o nazwie ContosoCoffee.
Wybierz pozycję ContosoCoffee w eksploratorze.
Utwórz następujące trzy pliki, które definiują układ, styl i logikę dla aplikacji:
- index.html
- index.css
- index.js
Utwórz folder o nazwie data.
Dodaj plik ContosoCoffee.txt utworzony wcześniej ze skoroszytu programu Excel ContosoCoffee.xlsx do folderu danych.
Utwórz inny folder o nazwie images (obrazy).
Jeśli jeszcze tego nie zrobiono, pobierz 10 obrazów mapy z katalogu images w repozytorium GitHub i dodaj je do folderu images .
Folder obszaru roboczego powinien teraz wyglądać podobnie do poniższego zrzutu ekranu:
Tworzenie kodu HTML
Aby utworzyć kod HTML:
Dodaj następujące
meta
tagi dohead
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">
Dodaj odwołania do plików JavaScript i CSS kontrolki internetowej usługi Azure Maps:
<!-- 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>
Dodaj odwołania do index.js i 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>
W treści dokumentu dodaj tag
header
. Wewnątrz taguheader
dodaj nazwę i logo firmy.<header> <img src="images/Logo.png" /> <span>Contoso Coffee</span> </header>
Dodaj tag
main
i utwórz panel wyszukiwania, który zawiera pole tekstowe i przycisk wyszukiwania. Dodaj także odwołaniadiv
dla mapy, panelu listy i przycisku GPS Moja lokalizacja.<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 zakończeniu index.html powinny wyglądać podobnie do Locator.html Simple Store w przykładowym kodzie samouczka.
Definiowanie stylów CSS
Następnym krokiem jest zdefiniowanie stylów CSS. Style CSS definiują układ składników aplikacji i wygląd aplikacji.
Otwórz index.css.
Dodaj następujący kod css:
Uwaga
Styl
@media
definiuje alternatywne opcje stylu do użycia, gdy szerokość ekranu jest mniejsza niż 700 pikseli.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); } }
Jeśli uruchomisz aplikację w tym momencie, zostanie wyświetlony nagłówek, pole wyszukiwania i przycisk wyszukiwania. Jednak mapa nie jest widoczna, ponieważ nie została jeszcze załadowana. Jeśli spróbujesz wykonać wyszukiwanie, nic się nie stanie. W następnej sekcji opisano dodawanie logiki Języka JavaScript potrzebnej do uzyskania dostępu do wszystkich funkcji lokalizatora sklepów.
Dodaj kod JavaScript
Kod JavaScript w aplikacji lokalizatora kawiarni Contoso umożliwia wykonywanie następujących procesów:
Dodaje odbiornik zdarzeń o nazwie
ready
, aby poczekać, aż strona zakończy proces ładowania. Po zakończeniu ładowania strony program obsługi zdarzeń tworzy więcej odbiorników zdarzeń w celu monitorowania ładowania mapy i nadawania funkcjonalności przyciskom wyszukiwania i Lokalizacji .Gdy użytkownik wybierze przycisk wyszukiwania lub wpisze lokalizację w polu wyszukiwania, a następnie naciska Enter, rozpoczyna się wyszukiwanie rozmyte względem zapytania użytkownika. Kod przekazuje tablicę wartości ISO 2 kraju/regionu do
countrySet
opcji ograniczenia wyników wyszukiwania do tych krajów/regionów. Ograniczenie krajów/regionów do wyszukiwania pomaga zwiększyć dokładność zwracanych wyników.Po zakończeniu wyszukiwania pierwszy wynik lokalizacji jest używany jako fokus centrum mapy. Gdy użytkownik wybierze przycisk Moja lokalizacja, kod pobiera lokalizację użytkownika przy użyciu interfejsu API geolokalizacji HTML5 wbudowanego w przeglądarkę. Po pobraniu lokalizacji kod koncentruje mapę na lokalizacji użytkownika.
Aby dodać kod JavaScript:
Otwórz index.js.
Dodaj opcje globalne, aby ułatwić aktualizowanie ustawień. Zdefiniuj zmienne dla mapy, okna podręcznego, źródła danych, warstwy ikon i znacznika HTML. Ustaw znacznik HTML, aby wskazać środek obszaru wyszukiwania. Zdefiniuj wystąpienie klienta usługi wyszukiwania Usługi Azure Maps.
//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>';
Dodaj następujący kod inicjowania. Pamiętaj, aby zastąpić
<Your Azure Maps Key>
ciąg kluczem subskrypcji usługi Azure Maps.Napiwek
W przypadku wyskakujących okienek najlepiej utworzyć jedno wystąpienie
Popup
i używać go ponownie, aktualizując jego zawartość i położenie. Dla każdego wystąpieniaPopup
dodawanego do kodu do strony dodawanych jest wiele elementów DOM. Im więcej elementów DOM na stronie, tym więcej obiektów musi śledzić przeglądarka. W przypadku zbyt wielu elementów przeglądarka może zacząć wolno działać.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;
W procedurze obsługi zdarzeń mapy
ready
dodaj kontrolkę powiększenia i znacznik HTML, aby wyświetlić środek obszaru wyszukiwania.//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);
W procedurze obsługi zdarzeń mapy
ready
dodaj źródło danych. Następnie wykonaj wywołanie, aby załadować i przeanalizować zestaw danych. Włącz klastrowanie na źródle danych. Klastrowanie na źródle danych grupuje nakładające się punkty w klastrze. W miarę powiększania przez użytkownika klastry oddzielają się od poszczególnych punktów. To zachowanie zapewnia lepsze środowisko użytkownika i poprawia wydajność.//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();
Po załadowaniu zestawu danych w procedurze obsługi zdarzeń mapy
ready
zdefiniuj zestaw warstw do renderowania danych. Warstwa bąbelkowa renderuje klastrowane punkty danych. Warstwa symboli renderuje liczbę punktów w każdym klastrze powyżej warstwy bąbelkowej. Druga warstwa symboli renderuje ikonę niestandardową dla poszczególnych lokalizacji na mapie.Dodaj zdarzenia
mouseover
imouseout
do warstwy bąbelkowej i warstwy ikon, aby zmieniać kursor myszy, gdy użytkownik umieści go na klastrze lub ikonie na mapie. Dodaj zdarzenieclick
do bąbelkowej warstwy klastra. Toclick
zdarzenie powiększa mapę na dwóch poziomach i wyśrodkuje mapę w klastrze, gdy użytkownik wybierze dowolny klaster. Dodaj zdarzenieclick
do warstwy ikon. Zdarzenieclick
wyświetla okno podręczne, które pokazuje szczegółowe informacje o kawiarni, kiedy użytkownik wybierze ikonę określonej lokalizacji. Dodaj do mapy zdarzenie, które monitoruje, kiedy mapa przestanie być przesuwana. Gdy to zdarzenie zostanie wyzwolone, zaktualizuj elementy na panelu listy.//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(); }); });
Gdy potrzebny jest zestaw danych kawiarni, należy go najpierw pobrać. Po pobraniu plik musi być podzielony na wiersze. Pierwszy wiersz zawiera informacje nagłówka. Aby ułatwić śledzenie kodu, analizujemy nagłówek jako obiekt, który następnie możemy użyć w celu wyszukania indeksu komórki każdej właściwości. Pozostałe wiersze, oprócz pierwszego, przetwórz w pętli i utwórz lokalizację w formie punktu. Dodaj lokalizację w formie punktu do źródła danych. Na koniec zaktualizuj panel listy.
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(); }); }
Po zaktualizowaniu panelu listy jest obliczana odległość. Ta odległość jest od środka mapy do wszystkich funkcji punktów w bieżącym widoku mapy. Lokalizacje są następnie sortowane według odległości. Generowany jest kod HTML w celu wyświetlania każdej lokalizacji na panelu listy.
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(''); }
Kiedy użytkownik wybiera element na panelu listy, kształt, z którym jest powiązany ten element, jest pobierany ze źródła danych. Generowane jest okno podręczne, które opiera się na informacjach o właściwości przechowywanych w danym kształcie. Mapa koncentruje się na kształcie. Jeśli mapa jest mniejsza niż 700 pikseli szerokości, widok mapy jest przesunięty, więc okno podręczne jest widoczne.
//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); }
Teraz masz w pełni funkcjonalny lokalizator sklepów. Otwórz plik index.html w przeglądarce internetowej. Gdy klastry są wyświetlane na mapie, możesz wyszukać lokalizację przy użyciu dowolnej z następujących metod:
- Pole wyszukiwania.
- Wybieranie przycisku Moja lokalizacja
- Wybieranie klastra
- Powiększ mapę, aby wyświetlić poszczególne lokalizacje.
Gdy użytkownik po raz pierwszy wybierze przycisk Moja lokalizacja, w przeglądarce zostanie wyświetlone ostrzeżenie o zabezpieczeniach z prośbą o pozwolenie na dostęp do lokalizacji użytkownika. Jeśli użytkownik wyrazi zgodę na udostępnienie swojej lokalizacji, mapa zostanie powiększona na obszarze lokalizacji użytkownika i zostaną wyświetlone pobliskie kawiarnie.
Po zastosowaniu wystarczająco dużego powiększenia obszaru zawierającego lokalizacje kawiarni klastry zostaną rozdzielone na poszczególne lokalizacje. Wybierz jedną z ikon na mapie lub wybierz element na panelu bocznym, aby wyświetlić okno podręczne. Wyskakujące okienko zawiera informacje o wybranej lokalizacji.
Jeśli zmienisz rozmiar okna przeglądarki na mniej niż 700 pikseli lub otworzysz aplikację na urządzeniu przenośnym, układ zmieni się, aby lepiej pasował do mniejszych ekranów.
W tym samouczku przedstawiono sposób tworzenia podstawowego lokalizatora sklepów przy użyciu usługi Azure Maps. Lokalizator sklepu utworzony w tym samouczku może mieć wszystkie funkcje, których potrzebujesz. Możesz dodać funkcje do swojego lokalizatora sklepów lub użyć bardziej zaawansowanych funkcji w celu uzyskania bardziej niestandardowego środowiska użytkownika:
- Włącz sugestie podczas pisania w polu wyszukiwania.
- Dodaj obsługę wielu języków.
- Umożliw użytkownikom filtrowanie lokalizacji wzdłuż trasy.
- Dodaj możliwość ustawienia filtrów.
- Dodaj obsługę określania początkowej wartości wyszukiwania przy użyciu ciągu zapytania. Po dołączeniu tej opcji do lokalizatora sklepów użytkownicy będą mogli tworzyć zakładki i udostępniać wyszukiwania. Zapewnia ona także prostą metodę przekazywania wyszukiwań do tej strony z innej strony.
- Wdróż swój lokalizator sklepów jako aplikację internetową usługi Azure App Service.
- Przechowuj dane w bazie danych i wyszukuj pobliskie lokalizacje. Aby dowiedzieć się więcej, zobacz omówienie typów danych przestrzennych programu SQL Server i artykuł na temat wysyłania zapytań względem danych przestrzennych dla najbliższego otoczenia.
Dodatkowe informacje
- Aby uzyskać ukończony kod, zobacz samouczek lokalizatora prostego sklepu w witrynie GitHub.
- Aby wyświetlić ten przykład na żywo, zobacz Simple Store Locator (Lokalizator prostych sklepów) w witrynie Przykłady kodu usługi Azure Maps.
- Dowiedz się więcej na temat pokrycia i możliwości usługi Azure Maps przy użyciu poziomów powiększenia i siatki kafelków.
- Możesz również użyć wyrażeń stylu opartych na danych, aby zastosować je do logiki biznesowej.
Następne kroki
Aby uzyskać dodatkowe przykłady kodu i zapoznać się z interaktywnym środowiskiem kodowania: