Creare un semplice store locator con Google Maps Platform (JavaScript)

1. Prima di iniziare

Una delle funzionalità più comuni di un sito web è mostrare una mappa di Google che evidenzia una o più sedi di un'attività, un'azienda o altre entità con presenza fisica. Il modo in cui vengono implementate queste mappe può variare notevolmente in base ai requisiti, come il numero di località e la frequenza con cui vengono modificate.

In questo codelab, esaminerai il caso d'uso più semplice, ovvero un numero ridotto di sedi che cambiano raramente, ad esempio uno store locator per un'attività con una catena di negozi. In questo caso, puoi utilizzare un approccio relativamente basso nella tecnologia senza alcuna programmazione lato server. Tuttavia, questo non significa che tu non possa essere creativo e che tu lo utilizzi sfruttando il formato dei dati GeoJSON per memorizzare e visualizzare informazioni arbitrarie su ogni negozio sulla tua mappa, nonché personalizzare gli indicatori e lo stile generale della mappa stessa.

Infine, come bonus aggiuntivo, utilizzi Cloud Shell per sviluppare e ospitare il tuo store locator. L'utilizzo di questo strumento non è severamente obbligatorio, ma consente di sviluppare lo store locator da qualsiasi dispositivo con un browser web e di renderlo disponibile online per il pubblico.

489628918395c3d0.png

Prerequisiti

  • Conoscenza di base di HTML e JavaScript

Attività previste

  • Visualizza una mappa con un insieme di sedi dei negozi e le informazioni memorizzate in formato GeoJSON.
  • Personalizza gli indicatori e la mappa stessa.
  • Mostra informazioni aggiuntive sul negozio quando viene fatto clic sull'indicatore.
  • Aggiungi una barra di ricerca Place Autocomplete alla pagina web.
  • Identifica la sede più vicina a un punto di partenza fornito dall'utente.

2. Configura

Nel passaggio 3 della sezione seguente, attiva le tre API seguenti 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 introduttiva a Google Maps Platform o guarda la playlist Introduzione a Google Maps Platform per completare la seguente procedura:

  1. Crea un account di fatturazione.
  2. Crea 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 accesso ai prodotti e alle risorse in esecuzione su Google Cloud, per consentirti di ospitare ed eseguire completamente il tuo progetto dal browser web.

Per attivare Cloud Shell da Cloud Console, fai clic su Attiva Cloud Shell 89665d8d348105cd.png (il provisioning e la connessione all'ambiente dovrebbero richiedere solo pochi minuti).

5f504766b9b3be17.png

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

d3bb67d514893d1f.png

Una volta connesso a Cloud Shell, dovresti vedere che hai già eseguito l'autenticazione e che 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 è configurato, esegui il comando seguente:

$ gcloud config set project <YOUR_PROJECT_ID>

3. "Ciao, mondo!" con una mappa

Inizia a sviluppare con una mappa

In Cloud Shell, inizia creando una pagina HTML che sarà la 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 consente di modificare facilmente i file in Cloud Shell.

Screen Shot 2017-04-19 alle 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.

NuovaCartella.png

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

Poi, crei una pagina web con una mappa.

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

3c257603da5ab524.png

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

indice.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 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 chiamato 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 da eseguire dopo il caricamento dell'API Maps JavaScript, ossia initMap.

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

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. Passi un riferimento al tag <div> per contenere la mappa, specificando 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

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

  1. Apri una scheda del browser web in questa app facendo clic su Anteprima web95e419ae763a1d48.png nella 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 dell'HTML pubblicato dal semplice server HTTP Python. Se tutto è andato bene, dovresti vedere una mappa centrata su Londra, Inghilterra.

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

4. Completa la mappa con GeoJSON

Ora, dai un'occhiata ai dati relativi ai negozi. GeoJSON è un formato di dati che rappresenta semplici elementi geografici, come punti, linee o poligoni su una mappa. Le funzionalità possono anche contenere dati arbitrari. Questo rende GeoJSON un eccellente candidato per rappresentare i negozi, che sono essenzialmente punti su una mappa con alcuni dati aggiuntivi, come il nome del negozio, gli orari di apertura e il numero di telefono. Soprattutto, GeoJSON dispone di un supporto di prima classe in Google Maps, il che significa che puoi inviare un documento GeoJSON a una mappa Google e la visualizzerà correttamente sulla mappa.

  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 una quantità di dati sufficiente, ma, una volta analizzata, vedrai la stessa struttura ripetuta per ogni negozio. Ogni negozio è rappresentato come un GeoJSON Point insieme alle sue coordinate e ai dati aggiuntivi contenuti nella chiave properties. È interessante notare che GeoJSON consente l'inclusione di chiavi con nome arbitrario nella chiave properties. In questo codelab, le chiavi sono category, hours, description, name e phone.

  1. Ora modifica app.js in modo che carichi il 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 trasmettendo il nome del file JSON. Hai inoltre definito una funzione da eseguire ogni volta che viene fatto clic su un indicatore. La funzione può quindi accedere ai dati aggiuntivi relativi al negozio su cui è stato fatto clic sull'indicatore e utilizzare le informazioni in una finestra che viene 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 clic su Anteprima web 95e419ae763a1d48.png > Visualizza di nuovo l'anteprima sulla porta 8080 e dovresti vedere una mappa ricca 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

Ci sei quasi. Quando fai clic, viene visualizzata una mappa con tutti gli indicatori di negozio e informazioni aggiuntive. Ma sembra che tutte le altre mappe di Google siano già disponibili. Che noia! Rinfrescati con lo stile mappa personalizzato, gli indicatori, i loghi e le immagini di Street View.

Ecco una nuova versione di app.js con stile personalizzato aggiunto:

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 relative allo stile della mappa. Come bonus extra, puoi anche creare il tuo stile, se preferisci.
  • Con il metodo map.data.setStyle, hai applicato indicatori personalizzati, uno diverso per ogni category dal GeoJSON.
  • Hai modificato la variabile content per includere di nuovo un logo (utilizzando ancora category dal GeoJSON) e un'immagine di Street View per la sede 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 fornita in precedenza (la stessa incollata in index.html, lasciando le virgolette inalterate).
  2. Esegui i comandi seguenti in Cloud Shell per scaricare la grafica e l'indicatore. Assicurati di essere nella directory di store-locator. Utilizza Control+C per arrestare il server HTTP semplice 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 vedere una mappa con uno stile personalizzato, immagini di indicatori personalizzati, una migliore formattazione della finestra informativa e un'immagine di Street View per ogni sede:

3d8d13da126021gg.png

6. Ricevi l'input utente

Gli utenti che si trovano nei negozi di solito vogliono sapere quale negozio è più vicino a loro o un indirizzo da cui pianificano il loro percorso. Aggiungi una barra di ricerca con completamento automatico del luogo per consentire all'utente di inserire facilmente un indirizzo di partenza. La funzionalità di completamento automatico del luogo fornisce una funzionalità di digitazione simile al funzionamento di completamento automatico in altre barre di ricerca di Google, ma le previsioni sono tutte luoghi di Google Maps Platform.

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

indice.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 con completamento automatico sia il riquadro a scorrimento vengono inizialmente nascosti finché non sono necessari.

  1. A questo punto, aggiungi il widget Autocomplete alla mappa alla fine della funzione initMap in app.js, subito prima della parentesi graffa di chiusura.

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 ai soli indirizzi di restituzione (perché il completamento automatico di luoghi può corrispondere ai nomi delle strutture e alle sedi amministrative) e limita gli indirizzi restituiti solo a quelli del Regno Unito. L'aggiunta di queste specifiche facoltative ridurrà il numero di caratteri che l'utente dovrà inserire per restringere le previsioni in modo da mostrare l'indirizzo che sta cercando. Sposta poi il div di completamento automatico che hai creato nell'angolo in alto a destra della mappa e specifica quali campi devono essere restituiti per ogni luogo della risposta.

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

Dovresti visualizzare un widget di completamento automatico nell'angolo in alto a destra della mappa, che mostra gli indirizzi nel Regno Unito che corrispondono a ciò che digiti.

5163f34a03910187.png

Ora devi gestire il momento in cui l'utente seleziona una previsione dal widget Autocomplete e utilizzare la località come base per il calcolo delle distanze dai tuoi negozi.

  1. Aggiungi il seguente codice alla fine di initMap in app.js dopo il codice che hai 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 un suggerimento, la mappa venga aggiornata con l'indirizzo selezionato e imposti l'origine come base per i calcoli delle distanze. 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, inserendo un'unica origine e una singola destinazione per ricevere un percorso tra i due. L'API Distance Matrix porta ulteriormente questo concetto per identificare 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, devi fornire un'origine e un array di punti vendita 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 a cui viene trasmessa come singola origine e le località dei negozi come array di destinazioni. In seguito, 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 visualizzare un elenco dei negozi ordinati dal più vicino al più lontano. Compila una scheda del riquadro laterale per ogni negozio utilizzando l'elenco restituito dalla funzione calculateDistances per definire 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 comando seguente.
$ 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 essere centrata su quell'indirizzo e dovrebbe apparire una barra laterale che elenca i punti vendita in ordine di distanza dall'indirizzo selezionato. Un esempio è il seguente:

489628918395c3d0.png

8. Facoltativo: hosting della pagina web

Fino a questo momento, visualizzi la mappa solo quando esegui attivamente il server HTTP Python. Per visualizzare la tua mappa oltre la sessione di Cloud Shell attiva o per poter condividere l'URL della mappa con altri utenti, puoi utilizzare Cloud Storage per ospitare la tua pagina web. Cloud Storage è un servizio web di archiviazione di file online per l'archiviazione e l'accesso ai dati nell'infrastruttura di Google. Il servizio combina le prestazioni e la scalabilità di Google Cloud con funzionalità avanzate di sicurezza e condivisione. Inoltre, offre un livello senza costi, che lo rende ottimo per l'hosting del tuo semplice store locator.

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

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

gsutil è lo strumento per interagire con Cloud Storage. Il comando mb è l'acronimo creativo di "make bucket." Per maggiori informazioni su tutti i comandi disponibili, inclusi quelli che utilizzi, vedi lo strumento gsutil.

Per impostazione predefinita, i bucket e i file ospitati su Cloud Storage sono privati. Tuttavia, vuoi che tutti i file siano pubblici per lo store locator in modo che siano accessibili a tutti su Internet. ogni file può essere reso pubblico dopo il suo caricamento, ma sarebbe noioso. Puoi semplicemente impostare il livello di accesso predefinito per il bucket che hai creato e tutti i file che caricherai erediteranno tale livello di accesso.

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

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

Pulizia

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

  • Apri la pagina Impostazioni in Cloud Console
  • Fai clic su Seleziona un progetto.
  • Seleziona il progetto creato all'inizio del tutorial e fai clic su Apri.
  • Inserisci l'ID progetto e fai clic su Arresta.

9. Complimenti

Complimenti! Hai completato questo codelab.

Che cosa hai imparato

Scopri di più

Quali altri codelab vorresti vedere?

Visualizzazione dei dati sulle mappe Ulteriori informazioni sullo stile delle mappe Creazione di interazioni 3D nelle mappe

Il codelab non vuoi vedere sopra? Richiedilo qui con un nuovo problema.

Se vuoi approfondire l'argomento, consulta il repository del codice sorgente all'indirizzo https://github.com/googlecodelabs/google-maps-simple-store-locator.