Criar um localizador de lojas simples com a Plataforma Google Maps (JavaScript)

1. Antes de começar

Um dos recursos mais comuns em um site é exibir um mapa do Google que destaca um ou mais locais de uma empresa, estabelecimento ou outra entidade com presença física. A forma como esses mapas são implementados pode variar muito dependendo dos requisitos, como o número de locais e a frequência com que eles mudam.

Neste codelab, você verá o caso de uso mais simples em que há poucos locais que raramente mudam, como um localizador para uma empresa com rede de lojas. Nesse caso, é possível usar uma abordagem fácil de configurar sem necessidade de programação no lado do servidor. Mas isso não quer dizer que você não pode usar a criatividade. Aproveite o formato de dados GeoJSON para armazenar e renderizar informações arbitrárias sobre cada loja no seu mapa, além de personalizar os marcadores e o estilo dele.

Você ainda pode usar o Cloud Shell para desenvolver e hospedar seu localizador de lojas. Não é obrigatório utilizar essa ferramenta, mas isso permite que você desenvolva o localizador de lojas em qualquer dispositivo com um navegador da Web e disponibilize esse recurso on-line.

489628918395c3d0.png

Pré-requisitos

  • Conhecimento básico de HTML e JavaScript

Atividades deste codelab

  • Exibir um mapa com um conjunto de lojas e informações armazenadas no formato GeoJSON
  • Personalizar marcadores e o próprio mapa
  • Exibir mais informações sobre a loja ao clicar em um marcador
  • Adicionar uma barra de pesquisa do Place Autocomplete à página da Web
  • Identificar a loja mais próxima ao ponto de partida do usuário

2. Começar a configuração

Na etapa 3 da próxima seção, ative as três APIs a seguir para este codelab:

  • API Maps JavaScript
  • API Places
  • API Distance Matrix

Primeiros passos com a Plataforma Google Maps

Se você nunca usou a Plataforma Google Maps, siga o guia Primeiros passos com a Plataforma Google Maps ou assista à playlist Primeiros passos na Plataforma Google Maps para concluir as seguintes etapas:

  1. Criar uma conta de faturamento
  2. Criar um projeto
  3. Ativar as APIs e os SDKs da Plataforma Google Maps (listados na seção anterior)
  4. Gerar uma chave de API

Ativar o Cloud Shell

Neste codelab, você usa o Cloud Shell, um ambiente de linha de comando executado no Google Cloud que oferece acesso a produtos e recursos que funcionam no Google Cloud. Com isso, é possível hospedar e executar seu projeto completamente pelo navegador da Web.

Para ativar o Cloud Shell no Console do Cloud, clique em Ativar o Cloud Shell 89665d8d348105cd.png. O provisionamento e a conexão ao ambiente levarão apenas alguns instantes para serem concluídos.

5f504766b9b3be17.png

Isso abre um novo shell na parte inferior do navegador depois de, talvez, exibir um intersticial de introdução.

d3bb67d514893d1f.png

Depois que você se conectar ao Cloud Shell, sua autenticação já terá sido feita, e projeto estará definido com o ID selecionado durante a configuração.

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

Se, por algum motivo, o projeto não estiver definido, execute o seguinte comando:

$ gcloud config set project <YOUR_PROJECT_ID>

3. "Hello World" com um mapa

Começar a desenvolver com um mapa

No Cloud Shell, a primeira etapa é criar uma página HTML que servirá como base para o restante do codelab.

  1. Na barra de ferramentas do Cloud Shell, clique em Iniciar editor 996514928389de40.png para abrir um editor de código em uma nova guia.

Com esse editor de código baseado na Web, você pode editar facilmente arquivos no Cloud Shell.

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

  1. Crie um novo diretório store-locator para seu app no editor de código clicando em Arquivo > Nova pasta.

NewFolder.png

  1. Nomeie a nova pasta como store-locator.

Em seguida, crie uma página da Web com um mapa.

  1. Crie um arquivo no diretório store-locator chamado index.html.

3c257603da5ab524.png

  1. Coloque o seguinte conteúdo no arquivo 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>

Essa é a página HTML que exibe o mapa. Ela contém códigos CSS para garantir que o mapa ocupe visualmente a página inteira, uma tag <div> para conter o mapa e um par de tags <script>. A primeira tag de script carrega um arquivo JavaScript chamado app.js, que contém todo o código JavaScript. A segunda tag de script carrega a chave de API, inclui o uso da biblioteca Places para o recurso de preenchimento automático que você vai adicionar depois e especifica o nome da função JavaScript a ser executada quando a API Maps JavaScript for carregada, ou seja, initMap.

  1. Substitua o texto YOUR_API_KEY no snippet de código pela chave de API que você gerou anteriormente neste codelab.
  2. Por fim, crie outro arquivo chamado app.js com o seguinte código:

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

}

Esse é o código mínimo exigido para criar um mapa. Transmita uma referência à tag <div> para conter o mapa e especifique o ponto central e o nível do zoom.

Para testar esse app, execute o servidor HTTP simples do Python no Cloud Shell.

  1. Acesse o Cloud Shell e digite o seguinte:
$ cd store-locator
$ python -m SimpleHTTPServer 8080

Você verá algumas linhas de saída de registro que mostram que o servidor HTTP simples está sendo executado no Cloud Shell e que o app da Web está usando a porta localhost 8080.

  1. Para abrir uma guia do navegador da Web neste app, clique em Visualização na Web 95e419ae763a1d48.png na barra de ferramentas do Console do Cloud e selecione Visualizar na porta 8080.

47b06e5169eb5add.png

bdab1f021a3b91d5.png

Ao clicar nesse item de menu, você abre uma nova guia no seu navegador da Web com o conteúdo do HTML exibido pelo servidor HTTP simples do Python. Se tudo correu bem, você verá um mapa centralizado de Londres, Inglaterra.

Para interromper o servidor HTTP simples, pressione Control+C no Cloud Shell.

4. Preencher o mapa com GeoJSON

Agora, veja os dados das lojas. O GeoJSON é um formato de dados que representa características geográficas simples, como pontos, linhas ou polígonos, em um mapa. As características também podem conter dados arbitrários. Isso torna o GeoJSON ideal para representar as lojas, que são essencialmente pontos em um mapa com alguns dados adicionais, como nome, horário de funcionamento e número de telefone. Mais importante, o GeoJSON tem suporte primário no Google Maps, o que significa que você pode enviar um documento GeoJSON a um mapa do Google e renderizá-lo no mapa adequadamente.

  1. Crie um novo arquivo com o nome stores.json e cole no seguinte código:

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

São muitos dados, mas depois você verá que a estrutura se repete para cada loja. Cada loja é representada por um GeoJSON Point com as coordenadas e os outros dados contidos na chave properties. O GeoJSON permite a inclusão de chaves nomeadas arbitrariamente na chave properties. Neste codelab, elas são category, hours, description, name e phone.

  1. Agora edite app.js para que ele carregue o GeoJSON no stores.js no seu mapa.

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

No exemplo de código, você carregou o GeoJSON no mapa chamando loadGeoJson e transmitindo o nome do arquivo JSON. Você também definiu uma função a ser executada sempre que houver um clique no marcador. Agora, a função pode acessar os dados extras da loja cujo marcador recebeu um clique e usar as informações em uma janela exibida. Para testar esse app, você pode executar o servidor HTTP simples do Python usando o mesmo comando de antes.

  1. Volte para o Cloud Shell e digite o seguinte:
$ python -m SimpleHTTPServer 8080
  1. Clique em Visualização na Web 95e419ae763a1d48.png > Visualizar na porta 8080 novamente. Você verá um mapa com vários marcadores que mostram os detalhes de cada loja após o clique, como o exemplo abaixo. Continue o processo.

c4507f7d3ea18439.png

5. Personalizar o mapa

Está quase tudo pronto. Você tem um mapa com todos os marcadores de loja e informações adicionais que poderão ser exibidas com um clique. Mas ele se parece com vários outros mapas do Google. Que entediante! Saiba mais como personalizar o estilo do mapa, marcadores, logotipos e imagens do Street View.

Veja uma nova versão do app.js com estilo personalizado:

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

}

Veja o que você adicionou:

  • A variável mapStyle contém todas as informações para estilizar o mapa. Também é possível criar seu próprio estilo, se você quiser.
  • Usando o método map.data.setStyle, você aplicou marcadores personalizados (um para cada category do GeoJSON).
  • Você modificou a variável content incluindo um logotipo (novamente usando o category do GeoJSON) e uma imagem do Street View no local da loja.

Antes de implantar essas mudanças, faça o seguinte:

  1. Defina o valor correto para a variável apiKey substituindo a string 'YOUR_API_KEY' no app.js pela sua chave de API anterior (aquela que você colou no index.html). Não remova as aspas.
  2. Execute os comandos a seguir no Cloud Shell para fazer o download dos gráficos de marcadores e logotipos. Verifique se você está no diretório store-locator. Use Control+C para interromper o servidor HTTP simples se ele estiver em execução.
$ 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. Veja o localizador de lojas concluído executando o seguinte comando:
$ python -m SimpleHTTPServer 8080

Ao recarregar a visualização, você verá um mapa semelhante ao mapa abaixo com estilo e imagens de marcadores personalizados, formatação aprimorada da janela de informações e uma imagem do Street View para cada local:

3d8d13da126021dd.png

6. Receber entradas do usuário

Os usuários dos localizadores de lojas geralmente querem saber que loja está mais próxima ou o endereço de onde planejam iniciar o trajeto. Adicione uma barra de pesquisa do Place Autocomplete para que o usuário possa inserir facilmente um endereço de partida. O Place Autocomplete funciona de maneira semelhante ao preenchimento automático em outras barras de pesquisa do Google, mas as estimativas são decorrentes de lugares na Plataforma Google Maps.

  1. Volte para editar o index.html e adicionar estilo à barra de pesquisa de preenchimento automático e ao painel lateral de resultados. Lembre-se de substituir a chave de API se você tiver colado seu código antigo.

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>

Tanto a barra de pesquisa de preenchimento automático quanto o painel deslizante ficam ocultos, conforme a necessidade.

  1. Agora adicione o widget de preenchimento automático ao mapa no fim da função initMap no app.js, antes da chave de fechamento.

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

O código restringe as sugestões de preenchimento automático para retornar apenas endereços porque o Place Autocomplete pode corresponder nomes de estabelecimentos e locais administrativos e retorna apenas endereços do Reino Unido. Adicionar essas especificações opcionais reduzirá o número de caracteres que o usuário precisa inserir para limitar as estimativas e mostrar o endereço que ele está procurando. Em seguida, o div de preenchimento automático que você criou é movido para o canto superior direito do mapa e os campos que precisam ser retornados sobre cada lugar são especificados.

  1. Reinicie o servidor e atualize a visualização executando o seguinte comando:
$ python -m SimpleHTTPServer 8080

Você verá um widget de preenchimento automático no canto superior direito do mapa, que mostra os endereços do Reino Unido que correspondem aos valores digitados.

5163f34a03910187.png

Agora você precisa definir quando o usuário seleciona uma estimativa do widget de preenchimento automático e usar esse local como base para calcular as distâncias até suas lojas.

  1. Adicione o seguinte código no fim de initMap no app.js depois do código que você acabou de colar.

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

O código adiciona um listener para que, quando o usuário clicar em uma das sugestões, o mapa seja atualizado novamente no endereço selecionado e defina a origem como base dos seus cálculos de distância. Implemente esses cálculos na próxima etapa.

7. Listar as lojas mais próximas

A API Directions funciona como a experiência de solicitar rotas no app Google Maps. Insira uma única origem e um único destino para receber um trajeto entre os dois. A API Distance Matrix usa esse conceito para identificar os pares ideais entre várias origens e destinos possíveis com base nos tempos e distâncias do trajeto. Nesse caso, para ajudar o usuário a encontrar a loja mais próxima ao endereço selecionado, indique uma origem e uma matriz de locais de lojas como destinos.

  1. Adicione uma nova função calculateDistances ao app.js.

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

A função ativa a API Distance Matrix usando a origem que recebeu como uma origem simples e os locais das lojas como uma matriz de destinos. Em seguida, ela cria uma matriz de objetos que armazenam o ID da loja, a distância expressa em uma string legível e a distância em metros como um valor numérico e classifica a matriz.

O usuário quer ver uma lista de lojas ordenadas da mais próxima à mais distante. Preencha uma lista de painel lateral para cada loja usando aquela retornada da função calculateDistances para informar a ordem de exibição das lojas.

  1. Adicione uma nova função showStoresList ao app.js.

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. Reinicie o servidor e atualize a visualização executando o seguinte comando:
$ python -m SimpleHTTPServer 8080
  1. Por fim, digite um endereço do Reino Unido na barra de pesquisa de preenchimento automático e clique em uma das sugestões.

O mapa terá esse endereço como centro, e uma barra lateral aparecerá listando as localizações de lojas em ordem de distância do endereço selecionado. Veja um exemplo:

489628918395c3d0.png

8. Opcional: hospedar sua página da Web

Até esse ponto, você só vê o mapa quando está executando ativamente o servidor HTTP do Python. Para ver o mapa fora da sessão ativa do Cloud Shell ou compartilhar o URL do mapa com outras pessoas, consulte Como usar o Cloud Storage para hospedar sua página da Web. O Cloud Storage é um serviço on-line de armazenamento de arquivos para armazenar e acessar dados na infraestrutura do Google. O serviço combina o desempenho e a escalonabilidade do Google Cloud com recursos avançados de segurança e compartilhamento. Ele também oferece um nível gratuito para hospedar seu localizador de lojas simples.

Com o Cloud Storage, os arquivos são armazenados em buckets, semelhantes a diretórios no seu computador. Para hospedar sua página da Web, primeiro é necessário criar um bucket. É preciso escolher um nome exclusivo para ele. Você pode usar seu próprio nome como parte dele.

  1. Depois, execute o seguinte comando no Cloud Shell:
$ gsutil mb gs://yourname-store-locator

A gsutil é a ferramenta para interagir com o Cloud Storage. O comando mb significa "criar bucket". Para mais informações sobre todos os comandos disponíveis, inclusive aqueles que você usará, consulte a ferramenta gsutil.

Por padrão, seus buckets e arquivos hospedados no Cloud Storage são privados. No entanto, para o localizador de lojas, o ideal é que todos os arquivos sejam públicos para que possam ser acessados por todos na Internet. Você pode tornar seus arquivos públicos depois de enviá-los, mas isso seria trabalhoso. Em vez disso, basta definir o nível de acesso padrão para o bucket criado. Assim, todos os arquivos enviados serão herdados.

  1. Execute o comando a seguir substituindo yourname-store-locator pelo nome do bucket:
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
  1. Agora você pode fazer o upload de todos os seus arquivos no diretório existente (no momento, apenas os arquivos index.html e app.js) com o seguinte comando:
$ gsutil -h "Cache-Control:no-cache" cp * gs://yourname-store-locator

Agora você tem uma página da Web com um mapa on-line. O URL para visualização será http://storage.googleapis.com/seunome-localizador-lojas/index.html, em que seunome-localizador-lojas será substituído pelo nome do bucket que você escolheu.

Limpeza

A maneira mais fácil de limpar todos os recursos criados neste projeto é encerrar o projeto do Google Cloud que você criou no início deste tutorial:

  • Abra a página de Configurações no Console do Cloud.
  • Clique em Selecione um projeto.
  • Selecione o projeto que você criou no início deste tutorial e clique em Abrir.
  • Digite a ID do projeto e clique em Encerrar.

9. Parabéns

Parabéns! Você concluiu este codelab.

O que você aprendeu

Saiba mais

Quais outros codelabs você quer ver?

Visualização de dados em mapas Como personalizar o estilo dos meus mapas Como criar interações 3D em mapas

O codelab que você quer ver não está listado acima? Solicite-o aqui.

Se você quiser saber mais sobre códigos, confira o repositório de códigos-fonte em https://github.com/googlecodelabs/google-maps-simple-store-locator.