Tworzenie planera tras za pomocą interfejsów Place Autocomplete API i Routes API

1. Przegląd

Niezależnie od tego, czy wybierasz się w długą podróż, planujesz codzienne dojazdy do pracy, czy poruszasz się po tętniącym życiem mieście, dotarcie z punktu A do punktu B to coś więcej niż tylko wiedza o tym, dokąd chcesz się udać. Niezbędne jest niezawodne narzędzie do wyznaczania tras.

Google Maps Platform umożliwia dodawanie do aplikacji dynamicznej mapy, szybkie wpisywanie lokalizacji przez użytkowników dzięki autouzupełnianiu oraz wyświetlanie tras na mapie.

Te ćwiczenia z programowania pokazują, jak utworzyć aplikację internetową za pomocą Maps JavaScript API, autouzupełniania miejsc i interfejsu Routes API. Z tego samouczka dowiesz się, jak zintegrować wiele interfejsów API Google Maps Platform.

Co utworzysz

W tym ćwiczeniu w Codelabs dowiesz się, jak utworzyć aplikację internetową za pomocą HTML, CSS, JavaScript i backendu Node.js.

Architektura aplikacji internetowej do planowania trasy

Aplikacja internetowa do planowania trasy

Czego się nauczysz

  • Jak włączyć interfejsy API Google Maps Platform
  • Jak zintegrować mapę dynamiczną z aplikacją internetową
  • Jak zintegrować usługę autouzupełniania Miejsc
  • Jak poprosić o wyznaczenie trasy za pomocą interfejsu Routes API
  • Wyświetlanie trasy na mapie dynamicznej
  • Jak utworzyć identyfikator mapy
  • Dodawanie zaawansowanych znaczników do mapy dynamicznej

Czego potrzebujesz

Przykładowy kod

Pełne rozwiązanie i szczegółowy kod znajdziesz na GitHub. Kod nie zawiera wymaganych pakietów Node. Zanim wykonasz kod, zainstaluj niezbędne zależności. Szczegółowe informacje o wymaganych pakietach znajdziesz w pliku package.json(wyjaśnionym w kroku 3).

2. Konfigurowanie projektu i włączanie interfejsów API

Aby to zrobić, musisz włączyć interfejsy JavaScript API, Autouzupełnianie miejscRoutes API.

Konfigurowanie Google Maps Platform

Jeśli nie masz jeszcze konta Google Cloud Platform i projektu z włączonymi płatnościami, zapoznaj się z przewodnikiem Pierwsze kroki z Google Maps Platform, aby utworzyć konto rozliczeniowe i projekt.

  1. W konsoli Google Cloud kliknij menu projektu i wybierz projekt, którego chcesz użyć w tym samouczku. Wybierz projekt
  2. Włącz interfejsy API Google Maps Platform wymagane w tym laboratorium na stronie biblioteki interfejsów API Map Google. Aby to zrobić, wykonaj czynności opisane w tym filmie lub w tej dokumentacji.
  3. Wygeneruj klucz interfejsu API na stronie Dane logowania w konsoli Cloud. Możesz wykonać czynności opisane w tym filmie lub w tej dokumentacji. Wszystkie żądania wysyłane do Google Maps Platform wymagają klucza interfejsu API.

3. Konfigurowanie projektu Node.js

W tym module użyjemy Node.js, aby pobrać z internetu miejsce początkowe i docelowe oraz poprosić o wyznaczenie trasy za pomocą interfejsu Routes API.

Zakładając, że masz już zainstalowany Node.js, utwórz katalog, w którym będziesz uruchamiać ten projekt:

$ mkdir ac_routes
$ cd ac_routes

Zainicjuj nowy pakiet Node.js w katalogu aplikacji:

$ npm init

To polecenie wyświetli prośbę o podanie kilku informacji, takich jak nazwa i wersja aplikacji. Na razie możesz po prostu nacisnąć klawisz RETURN, aby zaakceptować domyślne ustawienia większości z nich. Domyślnym punktem wejścia jest plik index.js, ale możesz go zmienić na swój główny plik. W tym module głównym plikiem jest function/server.js(więcej informacji znajdziesz w kroku 6).

Możesz też zainstalować preferowane platformy i moduły. W tym module używane są platforma internetowa(Express) i analizator treści(body-parser). Więcej informacji znajdziesz w pliku package.json.

4. Tworzenie mapy dynamicznej

Mamy już backend Node.js, więc przyjrzyjmy się teraz niezbędnym czynnościom po stronie klienta.

  • Tworzenie strony HTML aplikacji
  • Tworzenie pliku CSS do stylizacji
  • Wczytywanie interfejsu Maps JavaScript API w stronie HTML
  • Wklej klucz interfejsu API do tagu skryptu, aby uwierzytelnić aplikację.
  • Utwórz plik JavaScript do obsługi funkcji aplikacji.

Tworzenie strony HTML

  1. Utwórz nowy katalog w folderze projektu(w tym przypadku ac_routes).
     $ mkdir public
     $ cd public
    
  2. W katalogu publicznym utwórz plik index.html.
  3. Skopiuj ten kod do pliku index.html
     <!DOCTYPE html>
     <html>
     <head>
       <title>GMP Autocomplete + Routes</title>
       <meta charset="utf-8">
       <link rel="stylesheet" type="text/css" href="style.css">
     </head>
     <body>
       <div class="container">
         <!-- Start of the container for map -->
         <div class="main">
           <div id="map"></div>
         </div>
         <!-- End of the container for map -->
       </div>
       </body>
     </html>
    

Tworzenie pliku CSS

  1. Utwórz plik style.css w katalogu publicznym
  2. Skopiuj ten kod do pliku style.css:
     html, body {height: 100%;}
     body {
       background: #fff;
       font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
       font-style: normal;
       font-weight: normal;
       font-size:16px;
       line-height: 1.5;
       margin: 0;
       padding: 0;
     }
     .container {display:flex; width:90%; padding:100px 0; margin:0 auto;}
     .main {width:70%; height:800px;}
      #map {height:100%; border-radius:20px;}
    

Wczytywanie Maps JavaScript API

W tym module użyjemy dynamicznego importowania bibliotek, aby wczytać interfejs Maps JavaScript API. Więcej informacji znajdziesz tutaj.

W pliku index.html skopiuj ten kod przed tagiem zamykającym treść. Zastąp „YOUR_API_KEY” swoim kluczem API.

<script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({key: "YOUR_API_KEY", v: "weekly"});</script>

Tworzenie pliku JavaScript

  1. W katalogu publicznym utwórz plik app.js.
  2. Skopiuj poniższy kod do pliku app.js
     (function(){
       let map;
    
       async function initMap() {
           const { Map } = await google.maps.importLibrary('maps');
           map = new Map(document.getElementById('map'), {
               center: { lat: -34.397, lng: 150.644 },
               zoom: 8,
               mapId: 'DEMO_MAP_ID'
           });
       }
    
       initMap();
     }());
    

DEMO_MAP_ID to identyfikator, którego można używać w przykładach kodu wymagających identyfikatora Map Google. Ten identyfikator nie jest przeznaczony do użytku w aplikacjach produkcyjnych i nie można go używać w przypadku funkcji wymagających stylów w chmurze. W tym module w dalszej części będziemy potrzebować identyfikatora mapy do zaawansowanych znaczników. Więcej informacji o tworzeniu identyfikatora mapy dla aplikacji znajdziesz w tym artykule.

W pliku index.html połącz app.js przed tagiem zamykającym treść i po tagu skryptu wczytującym interfejs Maps JavaScript API.

<script type="text/JavaScript" src="app.js"></script>

Pełny przykładowy kod

Pełny kod do tego momentu jest dostępny na GitHubie:step1_createDynamicMap

5. Wpisz adresy miejsca początkowego i docelowego.

  • Dodaj w pliku index.html 2 pola tekstowe do wprowadzania punktu początkowego i miejsca docelowego.
  • Importowanie biblioteki Autouzupełnianie
  • Powiąż usługę autouzupełniania z polami tekstowymi miejsca docelowego i miejsca pochodzenia.

Dodawanie pól tekstowych

W pliku index.html dodaj ten kod jako pierwszy element podrzędny elementu div z klasą container.

<div class="aside">
  <div class="inputgroup">
    <label for="origin">Start</label>
    <input type="text" id="origin" name="origin" class="input-location" placeholder="Enter an address">
  </div>
  <div class="inputgroup">
    <label for="origin">End</label>
    <input type="text" id="destination" name="destination" class="input-location" placeholder="Enter an address">
  </div>
</div>

Importowanie i włączanie autouzupełniania

google.maps.places.Autocomplete to widżet, który na podstawie tekstu wpisanego przez użytkownika podaje prognozy dotyczące miejsc. Jest on dołączony do elementu wejściowego typu tekst i nasłuchuje wpisywania tekstu w tym polu. Lista prognoz jest wyświetlana jako lista rozwijana i aktualizowana w miarę wpisywania tekstu.

W pliku app.js dodaj ten kod po zainicjowaniu mapy:

let placeIds = [];
async function initPlace() {
  const { Autocomplete } = await google.maps.importLibrary('places');
  let autocomplete = [];
  let locationFields = Array.from(document.getElementsByClassName('input-location'));
  //Enable autocomplete for input fields
  locationFields.forEach((elem,i) => {
      autocomplete[i] = new Autocomplete(elem);
      google.maps.event.addListener(autocomplete[i],"place_changed", () => {
          let place = autocomplete[i].getPlace();
          if(Object.keys(place).length > 0){
              if (place.place_id){
                  placeIds[i] = place.place_id; //We use Place Id in this example
              } else {
                  placeIds.splice(i,1); //If no place is selected or no place is found, remove the previous value from the placeIds.
                  window.alert(`No details available for input: ${place.name}`);
                  return;
              }
          }
      });
  });
}
initPlace();

Gdy użytkownik wybierze miejsce z listy prognoz autouzupełniania, szczegóły wyniku miejsca można pobrać za pomocą metody getPlace(). Wynik wyszukiwania miejsca zawiera wiele informacji o miejscu. W tym module użyjemy identyfikatora miejsca do określenia wybranego miejsca. Identyfikatory miejsc jednoznacznie identyfikują miejsce w bazie danych Miejsc Google i w Mapach Google. Dowiedz się więcej o identyfikatorach miejsc.

Dodawanie odpowiednich stylów

W pliku style.css dodaj ten kod:

.aside {width:30%; padding:20px;}
.inputgroup {margin-bottom:30px;}
.aside label {display:block; padding:0 10px; margin-bottom:10px; font-size:18px; color:#666565;}
.aside input[type=text] {width:90%;padding:10px; font-size:16px; border:1px solid #e6e8e6; border-radius:10px;}

Pełny przykładowy kod

Pełny kod do tego momentu jest dostępny na GitHubie:step2_inputAddress

6. Poproś o wyznaczenie trasy

  • Dodaj do pliku index.html przycisk „Wyznacz trasę”, aby zainicjować prośbę o wyznaczenie trasy.
  • Ten przycisk powoduje wysłanie danych o miejscu docelowym i źródle do usługi Node.js.
  • Usługa Node.js wysyła żądanie do interfejsu Routes API.
  • Odpowiedź interfejsu API jest zwracana do klienta w celu wyświetlenia.

Po ustawieniu miejsca wyjazdu i miejsca docelowego oraz przygotowaniu dynamicznej mapy możesz wyznaczyć trasę. Z pomocą przychodzi Routes API, czyli nowa generacja zoptymalizowanej pod kątem wydajności wersji usługi Trasy dojazdu i Macierz odległości. W tym module użyjemy Node.js, aby pobrać z internetu miejsce początkowe i docelowe oraz poprosić o wyznaczenie trasy za pomocą interfejsu Routes API.

W pliku index.html dodaj przycisk „Get a route” (Wyznacz trasę) przed tagiem zamykającym div z klasą aside :

<div class="inputgroup">
  <button id="btn-getroute">Get a route</button>
</div>

W pliku style.css dodaj ten wiersz:

.aside button {padding:20px 30px; font-size:16px; border:none; border-radius:50px; background-color:#1a73e8; color:#fff;}

W pliku app.js dodaj ten kod, aby wysyłać dane o miejscu docelowym i początkowym do usługi Node.js:

function requestRoute(){
  let btn = document.getElementById('btn-getroute');
  btn.addEventListener('click', () => {
    //In this example, we will extract the Place IDs from the Autocomplete response
    //and use the Place ID for origin and destination
    if(placeIds.length == 2){
        let reqBody = {
            "origin": {
                "placeId": placeIds[0]
            },
            "destination": {
                "placeId": placeIds[1]
            }
        }

        fetch("/request-route", {
            method: 'POST',
            body: JSON.stringify(reqBody),
            headers: {
                "Content-Type": "application/json"
            }
        }).then((response) => {
            return response.json();
        }).then((data) => {
            //Draw the route on the map
            //Details will be covered in next step
            renderRoutes(data);
        }).catch((error) => {
            console.log(error);
        });
    } else {
        window.alert('Location must be set');
        return;
    }
  });
}

requestRoute();

renderRoutes() to funkcja, której użyjemy do narysowania trasy na mapie. Szczegóły omówimy w następnym kroku.

Tworzenie serwera

W katalogu projektu(w tym przypadku ac_routes) utwórz nowy folder o nazwie function. W tym folderze utwórz plik o nazwie server.js. Plik ten pełni funkcję punktu wejścia projektu, który jest konfigurowany podczas zakładania projektu Node.js i obsługuje 3 kluczowe funkcje:

  1. Zbieranie danych z klienta internetowego
  2. Wysyłanie żądań do interfejsu Routes API
  3. Zwracanie odpowiedzi interfejsu API po stronie klienta

Skopiuj ten kod do pliku server.js. Zastąp „YOUR_API_KEY” własnym kluczem API. Aby zwiększyć bezpieczeństwo klucza interfejsu API, zdecydowanie zalecamy używanie osobnego klucza dla backendu. Zobacz wskazówki dotyczące bezpieczeństwa.

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

const port  = 8080;
const urlencodedParser = bodyParser.urlencoded({extended:true}); 

function main() {
  app.use('/', express.static('public'));
  app.use(urlencodedParser);
  app.use(express.json());

  app.post('/request-route', (req,res) => {    
    fetch("https://routes.googleapis.com/directions/v2:computeRoutes", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Goog-Api-Key": "YOUR_API_KEY",
        "X-Goog-FieldMask": "*"
      },
      body: JSON.stringify(req.body)
    }).then((response) => {
      return response.json();
    }).then((data) => {
      if('error' in data){
        console.log(data.error);
      } else if(!data.hasOwnProperty("routes")){
        console.log("No route round");
      } else {
        res.end(JSON.stringify(data));
      }
    }).catch((error) => {
      console.log(error)
    });
  });

  app.listen(port, () => {
      console.log('App listening on port ${port}: ' + port);
      console.log('Press Ctrl+C to quit.');
  });
}

main();

Więcej informacji o interfejsie Routes API znajdziesz w artykule Pobieranie trasy za pomocą interfejsu Routes API.

Uruchom kod

Uruchom w wierszu poleceń ten kod:

$ node function/server.js

Otwórz przeglądarkę i wejdź na stronę http://127.0.0.1:8080/index.html. Powinna pojawić się strona aplikacji. Na tym etapie odpowiedź interfejsu API jest zwracana do klienta internetowego. W następnym kroku dowiesz się, jak wyświetlić trasę na mapie.

Pełny przykładowy kod

Pełny kod do tego momentu jest dostępny na GitHubie:step3_requestRoute

7. Wyświetlanie trasy na mapie

W poprzednim kroku odwołujemy się do renderRoutes(), gdy otrzymamy odpowiedź z usługi Node.js. Teraz dodajmy kod, który wyświetli trasę na mapie.

W pliku app.js dodaj ten kod:

let paths = [];
async function renderRoutes(data) {
  const { encoding } = await google.maps.importLibrary("geometry");
  let routes = data.routes;
  let decodedPaths = [];

  ///Display routes and markers
  routes.forEach((route,i) => {
      if(route.hasOwnProperty('polyline')){
        //Decode the encoded polyline
        decodedPaths.push(encoding.decodePath(route.polyline.encodedPolyline));

        //Draw polyline on the map
        for(let i = decodedPaths.length - 1; i >= 0; i--){
            let polyline = new google.maps.Polyline({
                map: map,
                path: decodedPaths[i],
                strokeColor: "#4285f4",
                strokeOpacity: 1,
                strokeWeight: 5
            });
            paths.push(polyline);
        }
        
        //Add markers for origin/destination
        addMarker(route.legs[0].startLocation.latLng,"A");
        addMarker(route.legs[0].endLocation.latLng,"B");
        //Set the viewport
        setViewport(route.viewport);
      } else {
        console.log("Route cannot be found");
      }
  });
}

Interfejs API Trasy zwraca linię łamaną w formacie zakodowanej linii łamanej(domyślnie) lub geoJsonLinestring. W tym module użyjemy formatu encodedPolyline i zdekodujemy go za pomocą biblioteki geometrii interfejsu Maps JavaScript API.

Użyjemy symbolu addMarker(), aby dodać zaawansowane znaczniki miejsca początkowego i docelowego. W pliku app.js dodaj ten kod:

let markers = [];
async function addMarker(pos,label){
  const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
  const { PinElement } = await google.maps.importLibrary("marker");
  const { LatLng } = await google.maps.importLibrary("core");
  let pinGlyph = new PinElement({
      glyphColor: "#fff",
      glyph: label
  });
  let marker = new AdvancedMarkerElement({
      position: new LatLng({lat:pos.latitude,lng:pos.longitude}),
      gmpDraggable: false,
      content: pinGlyph.element,
      map: map
  });
  markers.push(marker);
}

Tworzymy tu 2 zaawansowane markery – A dla punktu początkowego i B dla punktu docelowego. Więcej informacji o zaawansowanych znacznikach

Następnie wyśrodkujemy widok mapy na pobranej trasie, korzystając z wygodnych informacji o widoku udostępnianych przez interfejs Routes API. W pliku app.js dodaj ten kod:

async function setViewport(viewPort) {
  const { LatLng } = await google.maps.importLibrary("core");
  const { LatLngBounds } = await google.maps.importLibrary("core");
  let sw = new LatLng({lat:viewPort.low.latitude,lng:viewPort.low.longitude});
  let ne = new LatLng({lat:viewPort.high.latitude,lng:viewPort.high.longitude});
  map.fitBounds(new LatLngBounds(sw,ne));
}

Pełny przykładowy kod Pełny kod do tego momentu jest dostępny na GitHubie:step4_displayRoute

8. Usuwanie elementów z mapy

Chcemy pójść o krok dalej. Aby uniknąć bałaganu, wyczyśćmy mapę przed narysowaniem nowych znaczników i tras.

W pliku app.js dodajmy jeszcze jedną funkcję:

function clearUIElem(obj,type) {
  if(obj.length > 0){
      if(type == 'advMarker'){
          obj.forEach(function(item){
              item.map = null;
          });
      } else {
          obj.forEach(function(item){
              item.setMap(null);
          });
      }
  }
}

Na początku sekcji renderRoutes() dodaj ten wiersz:

clearUIElem(paths,'polyline');

Na początku sekcji addMarker() dodaj ten wiersz:

clearUIElem(markers,'advMarker');

Pełny przykładowy kod

Pełny kod do tego momentu jest dostępny na GitHubie:step5_removeElements

9. Gratulacje

Udało Ci się zbudować urządzenie.

Czego się dowiedziałeś

  • Włączanie interfejsów API Google Maps Platform
  • Wczytywanie interfejsu Maps JavaScript API w stronie HTML
  • Importowanie biblioteki Miejsc, Maps JavaScript API
  • Powiąż usługę autouzupełniania miejsc z polami tekstowymi.
  • Żądanie trasy za pomocą interfejsu Routes API
  • Wyświetlanie trasy na mapie dynamicznej
  • Tworzenie identyfikatora mapy
  • Tworzenie znaczników zaawansowanych

Więcej informacji

Jakie inne codelaby chcesz zobaczyć?

Wizualizacja danych na mapach Więcej informacji o dostosowywaniu stylu map Tworzenie interakcji 3D na mapach

Nie możesz znaleźć na liście powyżej interesujących Cię warsztatów? Zgłoś problem tutaj