Créer un outil de localisation de magasins simple avec Google Maps Platform (JavaScript)

1. Avant de commencer

Très souvent, les sites Web affichent une carte Google Maps mettant en évidence un ou plusieurs lieux appartenant à une entreprise, un établissement ou toute autre entité qui dispose d'un site physique. L'implémentation de ces cartes peut varier considérablement en fonction des besoins (nombre d'établissements, fréquence à laquelle ils changent, etc.).

Dans cet atelier de programmation, nous nous intéresserons au cas d'utilisation le plus simple : un outil de localisation de magasins pour un petit nombre d'établissements qui changent rarement, par exemple une entreprise possédant une chaîne de magasins. Ce cas est compatible avec une approche relativement simple en termes de technique, sans aucune programmation côté serveur. Toutefois, cela ne veut pas dire que vous n'avez pas le droit d'être créatif. C'est même le contraire avec le format de données GeoJSON, que vous pouvez utiliser pour stocker et afficher des informations arbitraires sur chaque magasin de votre carte, ainsi que pour personnaliser les repères et le style général de la carte elle-même.

Et cerise sur le gâteau, vous pouvez utiliser Cloud Shell pour développer et héberger votre outil de localisation de magasins. Cet outil n'est pas indispensable, mais il vous permet de créer un outil de localisation de magasins à partir de n'importe quel appareil équipé d'un navigateur Web et de le mettre en ligne.

489628918395c3d0.png

Conditions préalables

  • Connaissances de base en HTML et JavaScript

Objectifs de l'atelier

  • Afficher une carte avec un ensemble d'emplacements de magasins et d'informations stockées au format GeoJSON.
  • Personnaliser les repères et la carte elle-même.
  • Afficher des informations supplémentaires sur le magasin lorsqu'un internaute clique sur son repère.
  • Ajouter une barre de recherche Place Autocomplete à la page Web.
  • Identifier l'adresse du magasin le plus proche du point de départ fourni par l'utilisateur.

2. Configuration

À l'étape 3 de la section suivante, activez les trois API suivantes pour cet atelier de programmation :

  • API Maps JavaScript
  • API Places
  • API Distance Matrix

Premiers pas avec Google Maps Platform

Si vous n'avez jamais utilisé Google Maps Platform, suivez le guide Premiers pas avec Google Maps Platform ou regardez la playlist de démarrage avec Google Maps Platform pour effectuer les étapes suivantes :

  1. Créer un compte de facturation
  2. Créer un projet
  3. Activer les SDK et les API Google Maps Platform (listés dans la section précédente)
  4. Générer une clé API

Activer Cloud Shell

Dans cet atelier de programmation, vous allez utiliser Cloud Shell, un environnement de ligne de commande fonctionnant dans Google Cloud qui donne accès aux produits et aux ressources exécutés dans ce même service. Vous pourrez ainsi héberger et exécuter votre projet entièrement à partir de votre navigateur Web.

Pour activer Cloud Shell à partir de la Cloud Console, cliquez sur Activer Cloud Shell 89665d8d348105cd.png. Le provisionnement de l'environnement et la connexion ne devraient pas prendre plus de quelques minutes.

5f504766b9b3be17.png

Une nouvelle interface système s'ouvre au bas de votre navigateur après l'affichage éventuel d'un interstitiel d'introduction.

d3bb67d514893d1f.png

En principe, une fois que vous êtes connecté à Cloud Shell, vous êtes authentifié et le projet est défini sur l'ID de projet sélectionné lors de la configuration.

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

Si, pour une raison quelconque, le projet n'est pas défini, exécutez la commande suivante :

$ gcloud config set project <YOUR_PROJECT_ID>

3. Afficher "Hello, World!" sur une carte

Commencer le développement avec une carte

Dans Cloud Shell, commencez par créer une page HTML qui servira de base pour le reste de l'atelier.

  1. Dans la barre d'outils de Cloud Shell, cliquez sur Lancer l'éditeur 996514928389de40.png pour ouvrir un éditeur de code dans un nouvel onglet.

Cet éditeur de code Web vous permet de modifier facilement des fichiers dans Cloud Shell.

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

  1. Dans l'éditeur de code, créez un répertoire store-locator pour votre application en cliquant sur Fichier > Nouveau dossier.

NewFolder.png

  1. Nommez le nouveau dossier store-locator.

Créez ensuite une page Web avec une carte.

  1. Dans le répertoire store-locator, créez un fichier nommé index.html.

3c257603da5ab524.png

  1. Insérez le contenu suivant dans le fichier 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>

Ce code correspond à la page HTML qui affiche la carte. Elle contient quelques éléments CSS pour permettre à la carte d'occuper la totalité de la page, ainsi qu'une balise <div> pour contenir la carte et une paire de balises <script>. La première balise de script charge un fichier JavaScript appelé app.js, qui contient tout le code JavaScript. La deuxième charge la clé API, permet d'utiliser la bibliothèque Places pour la fonctionnalité de saisie semi-automatique que vous ajouterez ultérieurement et spécifie le nom de la fonction JavaScript qui s'exécute une fois que l'API Maps JavaScript est chargée (à savoir initMap).

  1. Remplacez le texte YOUR_API_KEY de l'extrait de code par la clé API générée précédemment au cours cet atelier.
  2. Enfin, créez un autre fichier nommé app.js avec le code suivant :

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

}

Il s'agit du minimum requis pour créer une carte. Vous transmettez une référence à votre balise <div> qui doit contenir la carte, et indiquez le centre et le niveau de zoom.

Pour tester cette application, vous pouvez exécuter le serveur HTTP simple de Python dans Cloud Shell.

  1. Accédez à Cloud Shell et saisissez la commande suivante :
$ cd store-locator
$ python -m SimpleHTTPServer 8080

Des lignes de sortie dans le journal vous indiquent que vous exécutez bien le serveur HTTP simple dans Cloud Shell avec l'application Web qui écoute le port localhost 8080.

  1. Ouvrez un onglet de navigateur Web sur cette application en cliquant sur Aperçu sur le Web95e419ae763a1d48.png dans la barre d'outils de Cloud Console et en sélectionnant Prévisualiser sur le port 8080.

47b06e5169eb5add.png

bdab1f021a3b91d5.png

Lorsque vous cliquez sur cet élément de menu, vous ouvrez un nouvel onglet dans votre navigateur Web avec le contenu HTML diffusé à partir du serveur HTTP simple de Python. Normalement, vous devriez voir une carte centrée sur Londres, en Angleterre.

Pour arrêter le serveur HTTP simple, appuyez sur Control+C dans Cloud Shell.

4. Remplir la carte avec les données GeoJSON

Intéressez-vous maintenant aux données des magasins. GeoJSON est un format de données qui représente des éléments géographiques simples tels que des points, des lignes ou des polygones sur une carte. Ces éléments peuvent également contenir des données arbitraires. GeoJSON est le candidat idéal pour représenter les magasins sur une carte. Il les signale sous forme de points accompagnés de données supplémentaires, comme le nom du magasin, ses horaires d'ouverture et son numéro de téléphone. Plus important encore, GeoJSON offre une compatibilité de premier ordre avec Google Maps. Vous pouvez donc envoyer un document GeoJSON vers une carte Google Maps, et il s'affichera correctement sur celle-ci.

  1. Créez un fichier nommé stores.json et collez-y le code suivant :

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"
            }
        }
    ]
}

Il y a beaucoup de données, mais en vous familiarisant avec ce code, vous pourrez constater qu'il s'agit de la même structure qui se répète pour chaque magasin. Chaque magasin est représenté par un Point GeoJSON qui inclut ses coordonnées ainsi que les données supplémentaires contenues dans la clé properties. Fait intéressant, GeoJSON permet d'inclure des clés nommées de manière arbitraire sous la clé properties. Dans cet atelier de programmation, ces clés seront les suivantes : category, hours, description, name et phone.

  1. Modifiez ensuite app.js afin qu'il charge les données GeoJSON dans le fichier stores.js sur votre carte.

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

Dans l'exemple de code, vous avez chargé vos données GeoJSON sur la carte en appelant loadGeoJson et en transmettant le nom du fichier JSON. Vous avez également défini une fonction qui s'exécute chaque fois qu'un internaute clique sur un repère. La fonction peut ensuite accéder aux données supplémentaires du magasin dont le repère a été sélectionné et utiliser les informations dans la fenêtre qui s'affiche. Pour tester cette application, vous pouvez exécuter le serveur HTTP simple de Python avec la même commande que celle utilisée précédemment.

  1. Revenez à Cloud Shell et saisissez la commande suivante :
$ python -m SimpleHTTPServer 8080
  1. Cliquez de nouveau sur Aperçu Web95e419ae763a1d48.png > Prévisualiser sur le port 8080. Vous devriez voir une carte contenant des repères que vous pouvez sélectionner pour afficher des détails de chaque magasin, comme illustré ci-dessous. Progression !

c4507f7d3ea18439.png

5. Personnaliser la carte

Vous y êtes presque ! Une carte contenant tous les repères de vos magasins, accompagnés de leurs informations supplémentaires, s'affiche lorsque l'utilisateur clique dessus. Mais elle ressemble à toutes les autres cartes Google Maps. Cela manque d'originalité. Vous pouvez lui donner plus de caractère en ajoutant un style de carte personnalisé, des repères, des logos et des images Street View.

Voici une nouvelle version de app.js, qui affiche un style personnalisé :

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

}

Voici ce que vous avez ajouté :

  • La variable mapStyle contient toutes les informations permettant d'appliquer un style à la carte. Vous pouvez même créer votre propre style, si vous le souhaitez.
  • À l'aide de la méthode map.data.setStyle, vous avez appliqué des repères personnalisés (un pour chaque category à partir des données GeoJSON).
  • Vous avez modifié la variable content pour inclure un logo (à l'aide de la valeur category de GeoJSON) et ajouté une image Street View pour l'emplacement du magasin.

Avant de déployer cette solution, vous devez effectuer les étapes suivantes :

  1. Définissez la valeur correcte pour la variable apiKey en remplaçant la chaîne 'YOUR_API_KEY' du fichier app.js par votre propre clé API utilisée précédemment (celle que vous avez collée dans index.html, en conservant les guillemets intacts).
  2. Exécutez les commandes suivantes dans Cloud Shell pour télécharger les images du repère et du logo. Assurez-vous que vous vous trouvez dans le répertoire store-locator. Utilisez Control+C pour arrêter le serveur HTTP simple s'il est en cours d'exécution.
$ 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. Prévisualisez l'outil de localisation de magasins terminé en exécutant la commande suivante :
$ python -m SimpleHTTPServer 8080

Lorsque vous actualisez l'aperçu, vous voyez normalement une carte semblable à celle-ci, avec des styles et des images de repères personnalisés, une mise en forme améliorée de la fenêtre d'informations et une image Street View pour chaque emplacement :

3d8d13da126021dd.png

6. Recevoir les entrées utilisateur

En général, l'utilisateur d'un outil de localisation de magasins souhaite identifier le magasin le plus proche ou connaître une adresse au moment de planifier un trajet. Ajoutez une barre de recherche Place Autocomplete pour permettre à l'utilisateur de saisir facilement une adresse de départ. Celle-ci offre des fonctions de saisie semblables à celles de la saisie semi-automatique des autres barres de recherche Google, mais les prédictions sont toutes des adresses de Google Maps Platform.

  1. Revenez au fichier index.html pour ajouter un style à la barre de recherche de saisie semi-automatique et au panneau latéral de résultats. N'oubliez pas de remplacer votre clé API si vous avez collé votre ancien code.

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>

La barre de saisie semi-automatique et le panneau coulissant sont masqués jusqu'à ce que vous en ayez besoin.

  1. Ajoutez maintenant le widget Autocomplete à la carte, à la fin de la fonction initMap dans le fichier app.js, juste avant l'accolade fermante.

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']);

Le code restreint les suggestions de la saisie semi-automatique pour ne renvoyer que des adresses (car Place Autocomplete peut aussi renvoyer des noms d'établissements et des lieux administratifs), et limite ces adresses au Royaume-Uni. L'ajout de ces spécifications facultatives réduit le nombre de caractères nécessaires pour affiner les prédictions et afficher l'adresse recherchée. Elles permettent aussi de déplacer le conteneur de saisie semi-automatique div que vous avez créé en haut à droite de la carte et spécifient les champs à renvoyer pour chaque adresse dans la réponse.

  1. Redémarrez votre serveur et actualisez l'aperçu en exécutant la commande suivante :
$ python -m SimpleHTTPServer 8080

Un widget Autocomplete s'affiche maintenant en haut à droite de votre carte et indique les adresses britanniques correspondant à votre saisie.

5163f34a03910187.png

À présent, vous devez gérer ce qui doit se passer quand l'utilisateur sélectionne une prédiction à partir du widget Autocomplete et utiliser l'adresse choisie comme base pour calculer la distance entre lui et vos magasins.

  1. Ajoutez le code suivant à la fin de initMap dans le fichier app.js, juste après celui que vous venez de coller.

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

Le code ajoute un écouteur, de sorte que lorsque l'utilisateur clique sur l'une des suggestions, la carte se recentre sur l'adresse sélectionnée et définit le point de départ comme base pour votre calcul de distance. L'étape suivante consiste à calculer les distances.

7. Lister les magasins les plus proches

L'API Directions fonctionne de la même manière que le traitement des requêtes d'itinéraire dans l'application Google Maps : vous pouvez saisir un point de départ et une destination pour connaître l'itinéraire entre les deux. L'API Distance Matrix pousse le concept plus loin et identifie les meilleures combinaisons entre plusieurs points de départ et plusieurs destinations possibles, en fonction du temps de trajet et de la distance. Dans ce cas, pour aider l'utilisateur à trouver le magasin le plus proche de l'adresse sélectionnée, vous allez indiquer un point de départ unique et plusieurs adresses de destination.

  1. Ajoutez au fichier app.js une fonction appelée 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 fonction appelle l'API Distance Matrix en se basant sur l'adresse qui lui est transmise en tant que point de départ unique ainsi que sur les emplacements des magasins qui sont donnés en tant que destinations. Elle crée ensuite un tableau d'objets incluant l'ID du magasin, la distance exprimée sous la forme d'une chaîne lisible ainsi que la distance exprimée sous la forme d'une valeur numérique (en mètres), et elle organise ce tableau.

L'utilisateur s'attend à voir une liste de magasins, des plus proches aux plus éloignés. Dans le panneau latéral, complétez votre liste pour chaque magasin à l'aide des données renvoyées par la fonction calculateDistances, qui indique l'ordre d'affichage des magasins.

  1. Ajoutez au fichier app.js une fonction appelée 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. Redémarrez votre serveur et actualisez l'aperçu en exécutant la commande suivante :
$ python -m SimpleHTTPServer 8080
  1. Pour finir, saisissez une adresse britannique dans la barre de recherche de la saisie semi-automatique, puis cliquez sur l'une des suggestions.

La carte doit être centrée sur cette adresse, et la barre latérale doit s'afficher pour indiquer l'emplacement des magasins, en commençant par celui qui est le plus proche de l'adresse sélectionnée. Voici un exemple :

489628918395c3d0.png

8. Facultatif : Héberger votre page Web

Jusqu'à présent, vous n'affichez votre carte que lorsque vous exécutez activement votre serveur HTTP Python. Pour afficher votre carte au-delà de votre session Cloud Shell active ou partager l'URL de votre carte avec d'autres internautes, hébergez votre page Web avec Cloud Storage. Cloud Storage est un service Web de stockage de fichiers en ligne permettant de stocker des données sur l'infrastructure de Google et d'y accéder. Ce service associe les performances et l'évolutivité de Google Cloud à des fonctionnalités avancées de sécurité et de partage. Il propose aussi un niveau gratuit idéal pour héberger votre outil simple de localisation de magasins.

Avec Cloud Storage, les fichiers sont stockés dans des buckets semblables aux répertoires de votre ordinateur. Pour héberger votre page Web, vous devez d'abord créer un bucket. Vous devez choisir un nom unique pour votre bucket, qui peut inclure votre propre nom.

  1. Une fois que vous avez choisi le nom, exécutez la commande suivante dans Cloud Shell :
$ gsutil mb gs://yourname-store-locator

gsutil est l'outil permettant d'interagir avec Cloud Storage. La commande mb signifie "make bucket" (créer un bucket). Pour plus d'informations sur toutes les commandes disponibles, y compris celles que vous utilisez, consultez la page consacrée à l'outil gsutil.

Par défaut, vos buckets et vos fichiers hébergés sur Cloud Storage sont privés. Cependant, pour l'outil de localisation de magasins, tous les fichiers doivent être publics pour que chacun puisse y accéder sur Internet. Vous pouvez rendre chaque fichier public après l'avoir importé, mais c'est une opération fastidieuse. Choisissez plutôt de définir le niveau d'accès par défaut pour le bucket que vous avez créé. Tous les fichiers que vous importez dans ce bucket en hériteront.

  1. Exécutez la commande suivante en remplaçant yourname-store-locator par le nom que vous avez choisi pour votre bucket :
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
  1. Vous pouvez maintenant importer tous vos fichiers dans le répertoire actuel (pour le moment, il s'agit uniquement de vos fichiers index.html et app.js) à l'aide de la commande suivante :
$ gsutil -h "Cache-Control:no-cache" cp * gs://yourname-store-locator

Vous devriez maintenant disposer d'une page Web avec une carte en ligne. L'URL permettant de l'afficher est la suivante : http://storage.googleapis.com/yourname-store-locator/index.html. Nous vous rappelons que la partie "yourname-store-locator" sera remplacée par le nom que vous avez attribué au bucket.

Effectuer un nettoyage

Le moyen le plus simple de nettoyer toutes les ressources créées dans ce projet consiste à mettre fin au projet Google Cloud que vous avez créé au début de ce tutoriel :

  • Ouvrez la page Paramètres dans Cloud Console.
  • Cliquez sur Sélectionner un projet.
  • Sélectionnez le projet que vous avez créé au début de ce tutoriel, puis cliquez sur Ouvrir.
  • Saisissez l'ID du projet, puis cliquez sur Arrêter.

9. Félicitations

Félicitations ! Vous avez terminé cet atelier de programmation.

Ce que vous avez appris

En savoir plus

Quels autres ateliers de programmation souhaiteriez-vous voir ?

Visualisation des données sur les cartes En savoir plus sur la personnalisation du style de mes cartes Concevoir des interactions 3D dans Google Maps

L'atelier de programmation que vous souhaitez suivre ne figure pas dans la liste ci-dessus ? Demandez-le en créant un problème ici.

Si vous souhaitez en savoir plus sur le code que vous avez expérimenté, consultez le dépôt de code source sur https://github.com/googlecodelabs/google-maps-simple-store-locator.