Crea un semplice Store locator con Google Maps Platform (JavaScript)

1. Prima di iniziare

Una delle funzionalità più comuni di un sito web è la visualizzazione di una mappa di Google che mette in evidenza una o più sedi di un'attività, un'organizzazione o un'altra entità con una presenza fisica. L'implementazione di queste mappe può variare notevolmente a seconda dei requisiti, ad esempio il numero di sedi e la frequenza con cui cambiano.

In questo codelab, esamini il caso d'uso più semplice: un numero ridotto di località che cambiano raramente, ad esempio un localizzatore di negozi per un'attività con una catena di negozi. In questo caso, puoi utilizzare un approccio relativamente semplice senza alcuna programmazione lato server. Ciò non significa che non puoi essere creativo. Puoi farlo sfruttando il formato dei dati GeoJSON per archiviare e visualizzare informazioni arbitrarie su ogni negozio sulla mappa, nonché personalizzare i marcatori e lo stile generale della mappa stessa.

Infine, come bonus aggiuntivo, utilizzi Cloud Shell per sviluppare e ospitare il localizzatore di negozi. Sebbene l'utilizzo di questo strumento non sia strettamente necessario, ti consente di sviluppare il localizzatore di negozi da qualsiasi dispositivo che esegue un browser web e di renderlo disponibile online al pubblico.

489628918395c3d0.png

Prerequisiti

  • Conoscenza di base di HTML e JavaScript

Attività previste

  • Visualizza una mappa con un insieme di sedi e informazioni del negozio memorizzate in formato GeoJSON.
  • Personalizza i segnaposto e la mappa stessa.
  • Mostra informazioni aggiuntive sul negozio quando viene fatto clic sul relativo indicatore.
  • Aggiungi una barra di ricerca del completamento automatico di Place alla pagina web.
  • Identifica la sede del negozio più vicina a un punto di partenza fornito dall'utente.

2. Configurazione

Nel passaggio 3 della sezione seguente, abilita le seguenti tre API per questo codelab:

  • API Maps JavaScript
  • API Places
  • API Distance Matrix

Inizia a utilizzare Google Maps Platform

Se non hai mai utilizzato Google Maps Platform, segui la guida Inizia a utilizzare Google Maps Platform o guarda la playlist Inizia a utilizzare Google Maps Platform per completare i seguenti passaggi:

  1. Crea un account di fatturazione.
  2. Creare un progetto.
  3. Abilita le API e gli SDK di Google Maps Platform (elencati nella sezione precedente).
  4. Genera una chiave API.

Attiva Cloud Shell

In questo codelab utilizzi Cloud Shell, un ambiente a riga di comando in esecuzione in Google Cloud che fornisce l'accesso a prodotti e risorse in esecuzione su Google Cloud, in modo da poter ospitare ed eseguire il progetto completamente dal browser web.

Per attivare Cloud Shell dalla console Google Cloud, fai clic su Attiva Cloud Shell 89665d8d348105cd.png (bastano pochi istanti per eseguire il provisioning e connettersi all'ambiente).

5f504766b9b3be17.png

Si apre una nuova shell nella parte inferiore del browser dopo aver mostrato un eventuale interstitial introduttivo.

d3bb67d514893d1f.png

Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sull'ID progetto selezionato durante la configurazione.

$ gcloud auth list
Credentialed Accounts:
ACTIVE  ACCOUNT
  *     <myaccount>@<mydomain>.com
$ gcloud config list project
[core]
project = <YOUR_PROJECT_ID>

Se per qualche motivo il progetto non è impostato, esegui il seguente comando:

$ gcloud config set project <YOUR_PROJECT_ID>

3. "Hello, World!" con una mappa

Inizia a sviluppare con una mappa

In Cloud Shell, inizia creando una pagina HTML che fungerà da base per il resto del codelab.

  1. Nella barra degli strumenti di Cloud Shell, fai clic su Avvia editor 996514928389de40.png per aprire un editor di codice in una nuova scheda.

Questo editor di codice basato sul web ti consente di modificare facilmente i file in Cloud Shell.

Screen Shot 2017-04-19 at 10.22.48 AM.png

  1. Crea una nuova directory store-locator per la tua app nell'editor di codice facendo clic su File > Nuova cartella.

NewFolder.png

  1. Assegna alla nuova cartella il nome store-locator.

A questo punto, crea una pagina web con una mappa.

  1. Crea un file denominato index.html nella directory store-locator.

3c257603da5ab524.png

  1. Inserisci i seguenti contenuti nel file index.html:

index.html

<html>

<head>
    <title>Store Locator</title>
    <style>
        #map {
            height: 100%;
        }
        
        html,
        body {
            height: 100%;
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <!-- The div to hold the map -->
    <div id="map"></div>

    <script src="app.js"></script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap&solution_channel=GMP_codelabs_simplestorelocator_v1_a">
    </script>
</body>

</html>

Questa è la pagina HTML che mostra la mappa. Contiene alcuni CSS per garantire che la mappa occupi visivamente l'intera pagina, un tag <div> per contenere la mappa e una coppia di tag <script>. Il primo tag script carica un file JavaScript denominato app.js, che contiene tutto il codice JavaScript. Il secondo tag script carica la chiave API, include l'utilizzo della libreria Places per la funzionalità di completamento automatico che aggiungerai in un secondo momento e specifica il nome della funzione JavaScript che viene eseguita una volta caricata l'API Maps JavaScript, ovvero initMap.

  1. Sostituisci il testo YOUR_API_KEY nello snippet di codice con la chiave API che hai generato in precedenza in questo codelab.
  2. Infine, crea un altro file denominato app.js con il seguente codice:

app.js

function initMap() {
   // Create the map.
    const map = new google.maps.Map(document.getElementById('map'), {
        zoom: 7,
        center: { lat: 52.632469, lng: -1.689423 },
    });

}

Questo è il codice minimo richiesto per creare una mappa. Passa un riferimento al tag <div> per contenere la mappa e specifica il centro e il livello di zoom.

Per testare questa app, puoi eseguire il semplice server HTTP Python in Cloud Shell.

  1. Vai a Cloud Shell e digita quanto segue:
$ cd store-locator
$ python3 -m http.server 8080

Vedi alcune righe di output del log che mostrano che stai effettivamente eseguendo il semplice server HTTP in Cloud Shell con l'app web in ascolto sulla porta 8080 di localhost.

  1. Apri una scheda del browser web in questa app facendo clic su Anteprima web 95e419ae763a1d48.pngnella barra degli strumenti di Cloud Console e selezionando Anteprima sulla porta 8080.

47b06e5169eb5add.png

bdab1f021a3b91d5.png

Se fai clic su questa voce di menu, si apre una nuova scheda nel browser web con i contenuti HTML forniti dal semplice server HTTP Python. Se tutto è andato a buon fine, dovresti vedere una mappa centrata su Londra, Inghilterra.

Per arrestare il semplice server HTTP, premi Control+C in Cloud Shell.

4. Popolare la mappa con GeoJSON

Ora dai un'occhiata ai dati dei negozi. GeoJSON è un formato di dati che rappresenta semplici elementi geografici, come punti, linee o poligoni su una mappa. Le funzionalità possono contenere anche dati arbitrari. Ciò rende GeoJSON un ottimo candidato per rappresentare i negozi, che sono essenzialmente punti su una mappa con alcuni dati aggiuntivi, come il nome del negozio, l'orario di apertura e il numero di telefono. Ancora più importante, GeoJSON è supportato in modo ottimale in Google Maps, il che significa che puoi inviare un documento GeoJSON a una mappa di Google e verrà visualizzato sulla mappa in modo appropriato.

  1. Crea un nuovo file denominato stores.json e incolla il seguente codice:

stores.json

{
    "type": "FeatureCollection",
    "features": [{
            "geometry": {
                "type": "Point",
                "coordinates": [-0.1428115,
                    51.5125168
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Modern twists on classic pastries. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Mayfair",
                "phone": "+44 20 1234 5678",
                "storeid": "01"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-2.579623,
                    51.452251
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Come and try our award-winning cakes and pastries. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Bristol",
                "phone": "+44 117 121 2121",
                "storeid": "02"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [
                    1.273459,
                    52.638072
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Whatever the occasion, whether it's a birthday or a wedding, Josie's Patisserie has the perfect treat for you. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Norwich",
                "phone": "+44 1603 123456",
                "storeid": "03"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-1.9912838,
                    50.8000418
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "A gourmet patisserie that will delight your senses. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Wimborne",
                "phone": "+44 1202 343434",
                "storeid": "04"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-2.985933,
                    53.408899
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Spoil yourself or someone special with our classic pastries. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Liverpool",
                "phone": "+44 151 444 4444",
                "storeid": "05"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-1.689423,
                    52.632469
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Come and feast your eyes and tastebuds on our delicious pastries and cakes. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Tamworth",
                "phone": "+44 5555 55555",
                "storeid": "06"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-3.155305,
                    51.479756
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Josie's Patisserie is family-owned, and our delectable pastries, cakes, and great coffee are renowed. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Cardiff",
                "phone": "+44 29 6666 6666",
                "storeid": "07"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-0.725019,
                    52.668891
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Oakham's favorite spot for fresh coffee and delicious cakes. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Oakham",
                "phone": "+44 7777 777777",
                "storeid": "08"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-2.477653,
                    53.735405
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Enjoy freshly brewed coffe, and home baked cakes in our homely cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Blackburn",
                "phone": "+44 8888 88888",
                "storeid": "09"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-0.211363,
                    51.108966
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "A delicious array of pastries with many flavours, and fresh coffee in an snug cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Crawley",
                "phone": "+44 1010 101010",
                "storeid": "10"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-0.123559,
                    50.832679
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Grab a freshly brewed coffee, a decadent cake and relax in our idyllic cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Brighton",
                "phone": "+44 1313 131313",
                "storeid": "11"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-3.319575,
                    52.517827
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Come in and unwind at this idyllic cafe with fresh coffee and home made cakes. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Newtown",
                "phone": "+44 1414 141414",
                "storeid": "12"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [
                    1.158167,
                    52.071634
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Fresh coffee and delicious cakes in an snug cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Ipswich",
                "phone": "+44 1717 17171",
                "storeid": "13"
            }
        }
    ]
}

Si tratta di molti dati, ma una volta esaminati, noterai che la struttura si ripete per ogni negozio. Ogni negozio è rappresentato come un Point GeoJSON insieme alle coordinate e ai dati aggiuntivi contenuti nella chiave properties. È interessante notare che GeoJSON consente l'inclusione di chiavi denominate arbitrariamente nella chiave properties. In questo codelab, queste chiavi sono category, hours, description, name e phone.

  1. Ora modifica app.js in modo che carichi il file GeoJSON in stores.js sulla mappa.

app.js

function initMap() {
  // Create the map.
  const map = new google.maps.Map(document.getElementById('map'), {
    zoom: 7,
    center: {lat: 52.632469, lng: -1.689423},
  });

  // Load the stores GeoJSON onto the map.
  map.data.loadGeoJson('stores.json', {idPropertyName: 'storeid'});

  const apiKey = 'YOUR_API_KEY';
  const infoWindow = new google.maps.InfoWindow();

  // Show the information for a store when its marker is clicked.
  map.data.addListener('click', (event) => {
    const category = event.feature.getProperty('category');
    const name = event.feature.getProperty('name');
    const description = event.feature.getProperty('description');
    const hours = event.feature.getProperty('hours');
    const phone = event.feature.getProperty('phone');
    const position = event.feature.getGeometry().get();
    const content = `
      <h2>${name}</h2><p>${description}</p>
      <p><b>Open:</b> ${hours}<br/><b>Phone:</b> ${phone}</p>
    `;

    infoWindow.setContent(content);
    infoWindow.setPosition(position);
    infoWindow.setOptions({pixelOffset: new google.maps.Size(0, -30)});
    infoWindow.open(map);
  });
}

Nell'esempio di codice, hai caricato il file GeoJSON sulla mappa chiamando loadGeoJson e passando il nome del file JSON. Hai anche definito una funzione da eseguire ogni volta che viene fatto clic su un indicatore. La funzione può quindi accedere ai dati aggiuntivi del negozio il cui indicatore è stato selezionato e utilizzare le informazioni in una finestra informativa visualizzata. Per testare questa app, puoi eseguire il semplice server HTTP Python utilizzando lo stesso comando di prima.

  1. Torna a Cloud Shell e digita quanto segue:
$ python3 -m http.server 8080
  1. Fai di nuovo clic su Anteprima web95e419ae763a1d48.png> Anteprima sulla porta 8080 e dovresti visualizzare una mappa piena di indicatori su cui puoi fare clic per visualizzare i dettagli di ogni negozio, come nell'esempio seguente. Avanzamento.

c4507f7d3ea18439.png

5. Personalizzare la mappa

Hai quasi finito. Hai una mappa con tutti i tuoi indicatori di negozio e informazioni aggiuntive visualizzate quando fai clic. Ma sembra una qualsiasi altra mappa di Google. Che noia! Rendi la tua mappa più interessante con uno stile personalizzato, indicatori, loghi e immagini Street View.

Ecco una nuova versione di app.js con l'aggiunta di stili personalizzati:

app.js

const mapStyle = [{
  'featureType': 'administrative',
  'elementType': 'all',
  'stylers': [{
    'visibility': 'on',
  },
  {
    'lightness': 33,
  },
  ],
},
{
  'featureType': 'landscape',
  'elementType': 'all',
  'stylers': [{
    'color': '#f2e5d4',
  }],
},
{
  'featureType': 'poi.park',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#c5dac6',
  }],
},
{
  'featureType': 'poi.park',
  'elementType': 'labels',
  'stylers': [{
    'visibility': 'on',
  },
  {
    'lightness': 20,
  },
  ],
},
{
  'featureType': 'road',
  'elementType': 'all',
  'stylers': [{
    'lightness': 20,
  }],
},
{
  'featureType': 'road.highway',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#c5c6c6',
  }],
},
{
  'featureType': 'road.arterial',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#e4d7c6',
  }],
},
{
  'featureType': 'road.local',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#fbfaf7',
  }],
},
{
  'featureType': 'water',
  'elementType': 'all',
  'stylers': [{
    'visibility': 'on',
  },
  {
    'color': '#acbcc9',
  },
  ],
},
];

function initMap() {
  // Create the map.
  const map = new google.maps.Map(document.getElementById('map'), {
    zoom: 7,
    center: {lat: 52.632469, lng: -1.689423},
    styles: mapStyle,
  });

  // Load the stores GeoJSON onto the map.
  map.data.loadGeoJson('stores.json', {idPropertyName: 'storeid'});

  // Define the custom marker icons, using the store's "category".
  map.data.setStyle((feature) => {
    return {
      icon: {
        url: `img/icon_${feature.getProperty('category')}.png`,
        scaledSize: new google.maps.Size(64, 64),
      },
    };
  });

  const apiKey = 'YOUR_API_KEY';
  const infoWindow = new google.maps.InfoWindow();

  // Show the information for a store when its marker is clicked.
  map.data.addListener('click', (event) => {
    const category = event.feature.getProperty('category');
    const name = event.feature.getProperty('name');
    const description = event.feature.getProperty('description');
    const hours = event.feature.getProperty('hours');
    const phone = event.feature.getProperty('phone');
    const position = event.feature.getGeometry().get();
    const content = `
      <img style="float:left; width:200px; margin-top:30px" src="img/logo_${category}.png">
      <div style="margin-left:220px; margin-bottom:20px;">
        <h2>${name}</h2><p>${description}</p>
        <p><b>Open:</b> ${hours}<br/><b>Phone:</b> ${phone}</p>
        <p><img src="https://maps.googleapis.com/maps/api/streetview?size=350x120&location=${position.lat()},${position.lng()}&key=${apiKey}&solution_channel=GMP_codelabs_simplestorelocator_v1_a"></p>
      </div>
      `;

    infoWindow.setContent(content);
    infoWindow.setPosition(position);
    infoWindow.setOptions({pixelOffset: new google.maps.Size(0, -30)});
    infoWindow.open(map);
  });

}

Ecco cosa hai aggiunto:

  • La variabile mapStyle contiene tutte le informazioni per lo stile della mappa. Come bonus aggiuntivo, puoi anche creare il tuo stile, se vuoi.
  • Utilizzando il metodo map.data.setStyle, hai applicato indicatori personalizzati, uno diverso per ogni category del GeoJSON.
  • Hai modificato la variabile content per includere un logo (utilizzando di nuovo category da GeoJSON) e un'immagine di Street View per la posizione del negozio.

Prima di eseguire il deployment, devi completare un paio di passaggi:

  1. Imposta il valore corretto per la variabile apiKey sostituendo la stringa 'YOUR_API_KEY' in app.js con la tua chiave API precedente (la stessa che hai incollato in index.html, lasciando intatte le virgolette).
  2. Esegui questi comandi in Cloud Shell per scaricare le grafiche del marcatore e del logo. Assicurati di trovarti nella directory store-locator. Utilizza Control+C per arrestare il semplice server HTTP se è in esecuzione.
$ mkdir -p img; cd img
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/icon_cafe.png
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/icon_patisserie.png
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/logo_cafe.png
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/logo_patisserie.png
  1. Visualizza l'anteprima dello Store locator completato eseguendo questo comando:
$ python3 -m http.server 8080

Quando ricarichi l'anteprima, dovresti visualizzare una mappa simile a questa con stile personalizzato, immagini dei marker personalizzate, formattazione della finestra informativa migliorata e un'immagine Street View per ogni località:

3d8d13da126021dd.png

6. Ottenere l'input dell'utente

Gli utenti dei localizzatori di negozi in genere vogliono sapere qual è il negozio più vicino a loro o a un indirizzo da cui intendono iniziare il loro viaggio. Aggiungi una barra di ricerca di Place Autocomplete per consentire all'utente di inserire facilmente un indirizzo di partenza. Place Autocomplete fornisce una funzionalità di completamento automatico simile a quella di altre barre di ricerca di Google, ma le previsioni riguardano solo i luoghi di Google Maps Platform.

  1. Torna a modificare index.html per aggiungere lo stile alla barra di ricerca del completamento automatico e al riquadro laterale dei risultati associato. Non dimenticare di sostituire la chiave API se hai incollato il vecchio codice.

index.html

<html>

<head>
  <title>Store Locator</title>
  <style>
    #map {
      height: 100%;
    }
    
    html,
    body {
      height: 100%;
      margin: 0;
      padding: 0;
    }

    /* Styling for Autocomplete search bar */
    #pac-card {
      background-color: #fff;
      border-radius: 2px 0 0 2px;
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
      box-sizing: border-box;
      font-family: Roboto;
      margin: 10px 10px 0 0;
      -moz-box-sizing: border-box;
      outline: none;
    }
    
    #pac-container {
      padding-top: 12px;
      padding-bottom: 12px;
      margin-right: 12px;
    }
    
    #pac-input {
      background-color: #fff;
      font-family: Roboto;
      font-size: 15px;
      font-weight: 300;
      margin-left: 12px;
      padding: 0 11px 0 13px;
      text-overflow: ellipsis;
      width: 400px;
    }
    
    #pac-input:focus {
      border-color: #4d90fe;
    }
    
    #title {
      color: #fff;
      background-color: #acbcc9;
      font-size: 18px;
      font-weight: 400;
      padding: 6px 12px;
    }
    
    .hidden {
      display: none;
    }

    /* Styling for an info pane that slides out from the left. 
     * Hidden by default. */
    #panel {
      height: 100%;
      width: null;
      background-color: white;
      position: fixed;
      z-index: 1;
      overflow-x: hidden;
      transition: all .2s ease-out;
    }
    
    .open {
      width: 250px;
    }
    
    .place {
      font-family: 'open sans', arial, sans-serif;
      font-size: 1.2em;
      font-weight: 500;
      margin-block-end: 0px;
      padding-left: 18px;
      padding-right: 18px;
    }
    
    .distanceText {
      color: silver;
      font-family: 'open sans', arial, sans-serif;
      font-size: 1em;
      font-weight: 400;
      margin-block-start: 0.25em;
      padding-left: 18px;
      padding-right: 18px;
    }
  </style>
</head>

<body>
  <!-- The div to hold the map -->
  <div id="map"></div>

  <script src="app.js"></script>
  <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap&solution_channel=GMP_codelabs_simplestorelocator_v1_a">
  </script>
</body>

</html>

Sia la barra di ricerca del completamento automatico sia il pannello a comparsa sono inizialmente nascosti finché non sono necessari.

  1. Ora aggiungi il widget di completamento automatico alla mappa alla fine della funzione initMap in app.js, appena prima della parentesi graffa chiusa.

app.js

  // Build and add the search bar
  const card = document.createElement('div');
  const titleBar = document.createElement('div');
  const title = document.createElement('div');
  const container = document.createElement('div');
  const input = document.createElement('input');
  const options = {
    types: ['address'],
    componentRestrictions: {country: 'gb'},
  };

  card.setAttribute('id', 'pac-card');
  title.setAttribute('id', 'title');
  title.textContent = 'Find the nearest store';
  titleBar.appendChild(title);
  container.setAttribute('id', 'pac-container');
  input.setAttribute('id', 'pac-input');
  input.setAttribute('type', 'text');
  input.setAttribute('placeholder', 'Enter an address');
  container.appendChild(input);
  card.appendChild(titleBar);
  card.appendChild(container);
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card);

  // Make the search bar into a Places Autocomplete search bar and select
  // which detail fields should be returned about the place that
  // the user selects from the suggestions.
  const autocomplete = new google.maps.places.Autocomplete(input, options);

  autocomplete.setFields(
      ['address_components', 'geometry', 'name']);

Il codice limita i suggerimenti di completamento automatico in modo che restituiscano solo indirizzi (perché il completamento automatico dei luoghi può trovare corrispondenze per nomi di attività e sedi amministrative) e limita gli indirizzi restituiti solo a quelli nel Regno Unito. L'aggiunta di queste specifiche facoltative ridurrà il numero di caratteri che l'utente deve inserire per restringere le previsioni e mostrare l'indirizzo che sta cercando. Poi, sposta il completamento automatico div che hai creato nell'angolo in alto a destra della mappa e specifica quali campi devono essere restituiti su ogni luogo nella risposta.

  1. Riavvia il server e aggiorna l'anteprima eseguendo questo comando:
$ python3 -m http.server 8080

Ora dovresti vedere un widget di completamento automatico nell'angolo in alto a destra della mappa, che mostra gli indirizzi del Regno Unito corrispondenti a ciò che digiti.

5163f34a03910187.png

Ora devi gestire il caso in cui l'utente seleziona una previsione dal widget Place Autocomplete e utilizzare quella posizione come base per calcolare le distanze dai tuoi negozi.

  1. Aggiungi il seguente codice alla fine di initMap in app.js dopo il codice appena incollato.

app.js

 // Set the origin point when the user selects an address
  const originMarker = new google.maps.Marker({map: map});
  originMarker.setVisible(false);
  let originLocation = map.getCenter();

  autocomplete.addListener('place_changed', async () => {
    originMarker.setVisible(false);
    originLocation = map.getCenter();
    const place = autocomplete.getPlace();

    if (!place.geometry) {
      // User entered the name of a Place that was not suggested and
      // pressed the Enter key, or the Place Details request failed.
      window.alert('No address available for input: \'' + place.name + '\'');
      return;
    }

    // Recenter the map to the selected address
    originLocation = place.geometry.location;
    map.setCenter(originLocation);
    map.setZoom(9);
    console.log(place);

    originMarker.setPosition(originLocation);
    originMarker.setVisible(true);

    // Use the selected address as the origin to calculate distances
    // to each of the store locations
    const rankedStores = await calculateDistances(map.data, originLocation);
    showStoresList(map.data, rankedStores);

    return;
  });

Il codice aggiunge un listener in modo che, quando l'utente fa clic su uno dei suggerimenti, la mappa si ricentri sull'indirizzo selezionato e imposti l'origine come base per i calcoli della distanza. Implementerai i calcoli della distanza nel passaggio successivo.

7. Elenca i negozi più vicini

L'API Directions funziona in modo molto simile all'esperienza di richiesta di indicazioni stradali nell'app Google Maps: inserisci un'unica origine e un'unica destinazione per ricevere un percorso tra le due. L'API Distance Matrix porta questo concetto oltre, identificando gli accoppiamenti ottimali tra più origini possibili e più destinazioni possibili in base ai tempi di percorrenza e alle distanze. In questo caso, per aiutare l'utente a trovare il negozio più vicino all'indirizzo selezionato, fornisci un'origine e un array di sedi dei negozi come destinazioni.

  1. Aggiungi una nuova funzione a app.js chiamata calculateDistances.

app.js

async function calculateDistances(data, origin) {
  const stores = [];
  const destinations = [];

  // Build parallel arrays for the store IDs and destinations
  data.forEach((store) => {
    const storeNum = store.getProperty('storeid');
    const storeLoc = store.getGeometry().get();

    stores.push(storeNum);
    destinations.push(storeLoc);
  });

  // Retrieve the distances of each store from the origin
  // The returned list will be in the same order as the destinations list
  const service = new google.maps.DistanceMatrixService();
  const getDistanceMatrix =
    (service, parameters) => new Promise((resolve, reject) => {
      service.getDistanceMatrix(parameters, (response, status) => {
        if (status != google.maps.DistanceMatrixStatus.OK) {
          reject(response);
        } else {
          const distances = [];
          const results = response.rows[0].elements;
          for (let j = 0; j < results.length; j++) {
            const element = results[j];
            const distanceText = element.distance.text;
            const distanceVal = element.distance.value;
            const distanceObject = {
              storeid: stores[j],
              distanceText: distanceText,
              distanceVal: distanceVal,
            };
            distances.push(distanceObject);
          }

          resolve(distances);
        }
      });
    });

  const distancesList = await getDistanceMatrix(service, {
    origins: [origin],
    destinations: destinations,
    travelMode: 'DRIVING',
    unitSystem: google.maps.UnitSystem.METRIC,
  });

  distancesList.sort((first, second) => {
    return first.distanceVal - second.distanceVal;
  });

  return distancesList;
}

La funzione chiama l'API Distance Matrix utilizzando l'origine passata come singola origine e le sedi del negozio come array di destinazioni. Poi crea un array di oggetti che memorizzano l'ID del negozio, la distanza espressa in una stringa leggibile, la distanza in metri come valore numerico e ordina l'array.

L'utente si aspetta di vedere un elenco dei negozi ordinati dal più vicino al più lontano. Compila un elenco nel riquadro laterale per ogni negozio utilizzando l'elenco restituito dalla funzione calculateDistances per determinare l'ordine di visualizzazione dei negozi.

  1. Aggiungi una nuova funzione a app.js chiamata showStoresList.

app.js

function showStoresList(data, stores) {
  if (stores.length == 0) {
    console.log('empty stores');
    return;
  }

  let panel = document.createElement('div');
  // If the panel already exists, use it. Else, create it and add to the page.
  if (document.getElementById('panel')) {
    panel = document.getElementById('panel');
    // If panel is already open, close it
    if (panel.classList.contains('open')) {
      panel.classList.remove('open');
    }
  } else {
    panel.setAttribute('id', 'panel');
    const body = document.body;
    body.insertBefore(panel, body.childNodes[0]);
  }


  // Clear the previous details
  while (panel.lastChild) {
    panel.removeChild(panel.lastChild);
  }

  stores.forEach((store) => {
    // Add store details with text formatting
    const name = document.createElement('p');
    name.classList.add('place');
    const currentStore = data.getFeatureById(store.storeid);
    name.textContent = currentStore.getProperty('name');
    panel.appendChild(name);
    const distanceText = document.createElement('p');
    distanceText.classList.add('distanceText');
    distanceText.textContent = store.distanceText;
    panel.appendChild(distanceText);
  });

  // Open the panel
  panel.classList.add('open');

  return;
}
  1. Riavvia il server e aggiorna l'anteprima eseguendo il seguente comando.
$ python3 -m http.server 8080
  1. Infine, inserisci un indirizzo nel Regno Unito nella barra di ricerca del completamento automatico e fai clic su uno dei suggerimenti.

La mappa dovrebbe centrare l'indirizzo e dovrebbe apparire una barra laterale che elenca le sedi dei negozi in ordine di distanza dall'indirizzo selezionato. Un esempio è illustrato di seguito:

489628918395c3d0.png

8. (Facoltativo) Ospitare la pagina web

Fino a questo punto, visualizzi la mappa solo quando esegui attivamente il server HTTP Python. Per visualizzare la mappa oltre la sessione Cloud Shell attiva o per poter condividere l'URL della mappa con altri, valuta la possibilità di utilizzare Cloud Storage per ospitare la pagina web. Cloud Storage è un servizio web di archiviazione di file online per l'archiviazione e l'accesso ai dati sull'infrastruttura di Google. Il servizio combina le prestazioni e la scalabilità di Google Cloud con funzionalità avanzate di sicurezza e condivisione. Offre anche un livello senza costi, il che lo rende ideale per ospitare il tuo semplice localizzatore di negozi.

Con Cloud Storage, i file vengono archiviati in bucket, che sono simili alle directory del computer. Per ospitare la tua pagina web, devi prima creare un bucket. Devi scegliere un nome univoco per il bucket, ad esempio utilizzando il tuo nome come parte del nome del bucket.

  1. Una volta scelto un nome, esegui questo comando in Cloud Shell:
$ gsutil mb gs://yourname-store-locator

gsutil è lo strumento per interagire con Cloud Storage. Il comando mb sta per "make bucket". Per saperne di più su tutti i comandi disponibili, inclusi quelli che utilizzi, consulta Strumento gsutil.

Per impostazione predefinita, i bucket e i file ospitati su Cloud Storage sono privati. Per il localizzatore di negozi, invece, vuoi che tutti i file siano pubblici in modo che siano accessibili a tutti su internet. Potresti rendere pubblico ogni file dopo averlo caricato, ma sarebbe noioso. In alternativa, puoi semplicemente impostare il livello di accesso predefinito per il bucket che hai creato e tutti i file che carichi al suo interno erediteranno questo livello di accesso.

  1. Esegui il comando seguente, sostituendo yourname-store-locator con il nome scelto per il bucket:
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
  1. Ora puoi caricare tutti i file nella directory corrente (attualmente solo i file index.html e app.js) con il seguente comando:
$ gsutil -h "Cache-Control:no-cache" cp * gs://yourname-store-locator

Ora dovresti avere una pagina web con una mappa online. L'URL per visualizzarlo sarà http://storage.googleapis.com/yourname-store-locator/index.html, con la parte yourname-store-locator sostituita dal nome del bucket scelto in precedenza.

Pulisci

Il modo più semplice per eliminare tutte le risorse create in questo progetto è chiudere il progetto Google Cloud che hai creato all'inizio di questo tutorial:

  • Apri la pagina Impostazioni in Cloud Console.
  • Fai clic su Seleziona un progetto.
  • Seleziona il progetto che hai creato all'inizio di questo tutorial e fai clic su Apri.
  • Inserisci l'ID progetto e fai clic su Chiudi.

9. Complimenti

Complimenti! Hai completato questo codelab.

Che cosa hai imparato

Scopri di più

Quali altri codelab vorresti vedere?

Visualizzazione dei dati sulle mappe Scopri di più sulla personalizzazione dello stile delle mie mappe Creare edifici per interazioni 3D nelle mappe

Il codelab che ti interessa non è elencato sopra? Richiedilo con un nuovo problema qui.

Se vuoi approfondire il codice, dai un'occhiata al repository del codice sorgente all'indirizzo https://github.com/googlecodelabs/google-maps-simple-store-locator.