1. Antes de começar
Neste codelab, você vai aprender tudo o que precisa para começar a usar a biblioteca vis.gl/react-google-map
(link em inglês) da API Google Maps JavaScript, que permite adicionar um mapa do Google a um app do React. Também vamos mostrar como configurar e carregar a API Maps JavaScript, mostrar seu primeiro mapa, trabalhar com marcadores e o clustering de marcadores, fazer desenhos em mapas e lidar com interações dos usuários.
Pré-requisitos
- Conhecimento básico de JavaScript, HTML e CSS
O que você vai aprender
- Como começar a usar a biblioteca
vis.gl/react-google-map
(link em inglês) para a Plataforma Google Maps. - Como carregar a API Maps JavaScript de forma declarativa.
- Como carregar um mapa em um app do React.
- Como usar marcadores comuns/personalizados e o clustering de marcadores.
- Como trabalhar com o sistema de eventos da API Maps JavaScript nas interações com os usuários.
- Como controlar o mapa de forma dinâmica.
- Como fazer desenhos no mapa.
O que é necessário
- Uma conta do Google Cloud com o faturamento ativado.
- Uma chave de API da Plataforma Google Maps com a API Maps JavaScript ativada.
- Node.js instalado no seu computador.
- Um editor de texto ou ambiente de desenvolvimento integrado.
- A biblioteca
vis.gl/react-google-map
(link em inglês) para a API Google Maps JavaScript. - A biblioteca
googlemaps/markerclusterer
.
Configurar a Plataforma Google Maps
Caso você ainda não tenha uma conta do Google Cloud Platform e um projeto com faturamento ativado, veja como criá-los no guia da Plataforma Google Maps.
- No Console do Cloud, clique no menu suspenso do projeto e selecione o projeto que você quer usar neste codelab.
- Ative as APIs e os SDKs da Plataforma Google Maps necessários para este codelab no Google Cloud Marketplace. Para fazer isso, siga as etapas descritas neste vídeo ou nesta documentação.
- Gere uma chave de API na página Credenciais do Console do Cloud. Siga as etapas indicadas neste vídeo ou nesta documentação. Todas as solicitações feitas à Plataforma Google Maps precisam de uma chave de API.
2. Começar a configuração
Baixar o projeto inicial
Para baixar o modelo do projeto inicial e o código da solução:
- Baixe ou crie uma ramificação do repositório do GitHub (link em inglês). O projeto inicial está no diretório
/starter
e inclui a estrutura básica de arquivos necessária para concluir o codelab. Você vai trabalhar apenas no diretório/starter/src
.
git clone https://github.com/googlemaps-samples/codelab-maps-platform-101-react-js.git
Se preferir, clique no botão abaixo para baixar o código-fonte.
- Acesse o diretório
/starter
e instale o npm para ter todas as dependências necessárias que constam no arquivopackage.json
.
cd starter && npm install
- Ainda no diretório
/starter
:
npm start
O projeto inicial foi configurado para você usar o servidor de desenvolvimento do Vite, que compila e executa o código escrito localmente. Esse servidor também recarrega automaticamente o app no navegador sempre que você altera o código. Se você acessar o link fornecido no fim do processo de build, o resultado será uma página da Web com as palavras: "Hello World!"
- Para executar o código completo da solução, vá até o diretório
/solution
e siga as mesmas etapas de configuração.
3. Carregar a API Maps JavaScript
A base de uso da Plataforma Google Maps na Web é a API Maps JavaScript. Essa API disponibiliza uma interface JavaScript para usar todos os recursos da
Plataforma Google Maps, incluindo mapa, marcadores, ferramentas de desenho e outros serviços da Plataforma Google Maps, como o Places.
Para carregar a API Maps JavaScript com a estrutura do React, você precisa usar o componente APIProvider
que faz parte da biblioteca vis.gl/react-google-map
. É possível adicionar o componente em qualquer nível do app (geralmente fica próximo à parte de cima), e ele renderiza todos os componentes secundários sem modificações. Além de trabalhar com o carregamento da API Maps JavaScript, ele dá informações contextuais e funções para outros componentes e hooks da biblioteca. O componente APIProvider
já está incluído na biblioteca vis.gl/react-google-map
, então foi instalado quando você executou o npm install
(links em inglês).
Para usar o componente APIProvider
:
- Abra o arquivo
/src/app.tsx
. É com ele que você vai trabalhar neste codelab. - Na parte de cima do arquivo, importe a classe
APIProvider
da biblioteca@
vis.gl/react-google-maps
(link em inglês):
import {APIProvider} from '@vis.gl/react-google-maps';
- Na definição da função do
App
, defina o parâmetroapiKey
do componenteAPIProvider
usando a chave de API criada na etapa anterior, além da propriedadeonLoad
com uma mensagem de registro do console:
<APIProvider apiKey={'Your API key here'} onLoad={() => console.log('Maps API has loaded.')}>
O componente APIProvider
usa várias propriedades que especificam opções para carregar a API Maps JavaScript, incluindo sua chave de API da Plataforma Google Maps, a versão da API que você quer carregar e qualquer outra biblioteca fornecida pela API Maps JavaScript que você também quer carregar.
A chave de API do Google Maps é a única propriedade necessária para que o APIProvider
funcione. Incluímos a propriedade onLoad
apenas como exemplo. Para mais informações, confira Componente <APIProvider>
(link em inglês):
O arquivo app.tsx
vai ficar assim:
import React from 'react';
import {createRoot} from "react-dom/client";
import {APIProvider} from '@vis.gl/react-google-maps';
const App = () => (
<APIProvider apiKey={'Your API key here'} onLoad={() => console.log('Maps API has loaded.')}>
<h1>Hello, world!</h1>
</APIProvider>
);
const root = createRoot(document.getElementById('app'));
root.render(<App />);
export default App;
Se tudo der certo, você vai ver a instrução console.log
no console do navegador. Agora que a API Maps JavaScript está carregada, você pode renderizar o mapa dinâmico na próxima etapa.
4. Mostrar um mapa
É hora de mostrar seu primeiro mapa.
A parte mais usada da API Maps JavaScript é o google.maps.Map
, que é a classe que permite criar e manipular instâncias de mapas. A biblioteca vis.gl/react-google-map
(link em inglês) une essa classe no componente Map
. Importe as classes Map
e MapCameraChangedEvent
.
import {APIProvider, Map, MapCameraChangedEvent} from '@vis.gl/react-google-maps';
O componente Map
aceita várias configurações do mapa. Neste codelab, vamos usar as configurações a seguir:
defaultCenter
: define a latitude e a longitude para o centro do mapa.defaultZoom
: define o nível de zoom inicial do mapa.- Para mostrar um mapa, insira o código abaixo entre as tags do componente
APIProvider
para centralizar em Sidney (Austrália) e insira o nível de zoom13
, que é o ideal para exibir o centro da cidade:
<Map
defaultZoom={13}
defaultCenter={ { lat: -33.860664, lng: 151.208138 } }
onCameraChanged={ (ev: MapCameraChangedEvent) =>
console.log('camera changed:', ev.detail.center, 'zoom:', ev.detail.zoom)
}>
</Map>
O resultado será um mapa de Sydney no navegador:
Nesta seção, você mostrou um mapa com o componente <Map>
e definiu o status inicial dele com propriedades, além de ter usado eventos para capturar mudanças na câmera.
O arquivo app.tsx
vai ficar assim:
import React from 'react';
import {createRoot} from "react-dom/client";
import {APIProvider, Map, MapCameraChangedEvent} from '@vis.gl/react-google-maps';
const App = () => (
<APIProvider apiKey={'Your API key here'} onLoad={() => console.log('Maps API has loaded.')}>
<Map
defaultZoom={13}
defaultCenter={ { lat: -33.860664, lng: 151.208138 } }
onCameraChanged={ (ev: MapCameraChangedEvent) =>
console.log('camera changed:', ev.detail.center, 'zoom:', ev.detail.zoom)
}>
</Map>
</APIProvider>
);
const root = createRoot(document.getElementById('app'));
root.render(<App />);
export default App;
5. Adicionar a Estilização de mapas baseada na nuvem
Essa estilização é necessária para usar os Marcadores Avançados, que servem para escolher pontos de interesse no seu mapa de Sidney.
Com a Estilização de mapas baseada na nuvem, você pode personalizar o estilo do mapa.
Criar um ID do mapa
Se você ainda não criou um ID do mapa com um estilo associado a ele, consulte o guia de IDs do mapa para concluir as seguintes etapas:
- Crie um ID do mapa.
- Associe um ID do mapa a um estilo.
Se quiser usar o ID do mapa que você criou, defina a propriedade mapId
do componente <Map>
:
<Map
defaultZoom={13}
defaultCenter={ { lat: -33.860664, lng: 151.208138 } }
mapId='YOUR_MAP_ID'
onCameraChanged={ (ev: MapCameraChangedEvent) =>
console.log('camera changed:', ev.detail.center, 'zoom:', ev.detail.zoom)
}>
</Map>
O estilo que você selecionou no mapa vai aparecer.
6. Adicionar marcadores ao mapa
A API Maps JavaScript oferece inúmeras possibilidades para os desenvolvedores, e a ação mais comum é colocar marcadores no mapa. Eles mostram pontos específicos e são elementos comuns da interface para lidar com as interações dos usuários. Quem já usou o Google Maps conhece o marcador padrão, que tem esta aparência:
Para usar o componente AdvancedMarker
e colocar marcadores no mapa:
- Crie uma lista de objetos que representem pontos de interesse na região de Sidney e coloque logo abaixo das importações, fora da definição do
App
:
type Poi ={ key: string, location: google.maps.LatLngLiteral }
const locations: Poi[] = [
{key: 'operaHouse', location: { lat: -33.8567844, lng: 151.213108 }},
{key: 'tarongaZoo', location: { lat: -33.8472767, lng: 151.2188164 }},
{key: 'manlyBeach', location: { lat: -33.8209738, lng: 151.2563253 }},
{key: 'hyderPark', location: { lat: -33.8690081, lng: 151.2052393 }},
{key: 'theRocks', location: { lat: -33.8587568, lng: 151.2058246 }},
{key: 'circularQuay', location: { lat: -33.858761, lng: 151.2055688 }},
{key: 'harbourBridge', location: { lat: -33.852228, lng: 151.2038374 }},
{key: 'kingsCross', location: { lat: -33.8737375, lng: 151.222569 }},
{key: 'botanicGardens', location: { lat: -33.864167, lng: 151.216387 }},
{key: 'museumOfSydney', location: { lat: -33.8636005, lng: 151.2092542 }},
{key: 'maritimeMuseum', location: { lat: -33.869395, lng: 151.198648 }},
{key: 'kingStreetWharf', location: { lat: -33.8665445, lng: 151.1989808 }},
{key: 'aquarium', location: { lat: -33.869627, lng: 151.202146 }},
{key: 'darlingHarbour', location: { lat: -33.87488, lng: 151.1987113 }},
{key: 'barangaroo', location: { lat: - 33.8605523, lng: 151.1972205 }},
];
const App = () => (
...
);
- Personalize os alfinetes com o elemento
<Pin>
:
<Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
- Crie um componente personalizado para renderizar sua lista com Marcadores Avançados e insira abaixo da definição do
App
:
const App = () => (
...
);
const PoiMarkers = (props: {pois: Poi[]}) => {
return (
<>
{props.pois.map( (poi: Poi) => (
<AdvancedMarker
key={poi.key}
position={poi.location}>
<Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
</AdvancedMarker>
))}
</>
);
};
- Adicione o componente
PoiMarkers
como um elemento filho do componenteMap
:
<Map
... map properties ...
>
<PoiMarkers pois={locations} />
</Map>
- Adicione
Pin
eAdvancedMarker
às importações.
import {
APIProvider,
Map,
AdvancedMarker,
MapCameraChangedEvent,
Pin
} from '@vis.gl/react-google-maps';
Os Marcadores Avançados personalizados vão aparecer no mapa:
7. Ativar o clustering de marcadores
Ao usar muitos marcadores próximos entre em si ou em grandes quantidades, eles podem ficar sobrepostos ou muito perto uns dos outros, o que causa uma experiência negativa para o usuário. Por exemplo, depois de criar os marcadores na última etapa, isto pode ter acontecido:
A solução neste caso é o clustering de marcadores, outro recurso comum que agrupa marcadores próximos em um único ícone e muda dependendo do nível de zoom. Veja como:
O algoritmo para clustering de marcadores divide a área visível do mapa em uma grade e agrupa os ícones que estão na mesma célula. Felizmente, você não precisa se preocupar com isso porque a equipe da Plataforma Google Maps criou uma biblioteca muito conveniente de utilitários de código aberto, a MarkerClustererPlus
, que faz tudo de forma automática. Você pode consultar a origem da biblioteca MarkerClustererPlus
no GitHub (link em inglês).
Para ativar o clustering de marcadores:
- Na parte de cima do arquivo
app.tsx
, vamos atualizar e incluir na biblioteca os tipos compatíveis e as importações.
import React, {useEffect, useState, useRef, useCallback} from 'react';
import {createRoot} from "react-dom/client";
import {
APIProvider,
Map,
AdvancedMarker,
MapCameraChangedEvent,
useMap,
Pin
} from '@vis.gl/react-google-maps';
import {MarkerClusterer} from '@googlemaps/markerclusterer';
import type {Marker} from '@googlemaps/markerclusterer';
Para o projeto modelo deste codelab, a biblioteca de utilitários do MarkerClustererPlus
já está incluída nas dependências declaradas no arquivo package.json
, ou seja, você já a instalou quando executou npm install
no início do codelab.
- Crie variáveis para o
MarkerClusterer
e os elementos compatíveis no componentePoiMarkers
.
Você precisa que uma instância do mapa inicialize o MarkerClusterer
. Faça isso no hook do useMap()
:
const map = useMap();
- Crie uma lista de marcadores armazenados em uma variável de estado:
const [markers, setMarkers] = useState<{[key: string]: Marker}>({});
- Armazene o clusterer como uma referência:
const clusterer = useRef<MarkerClusterer | null>(null);
- No componente
PoiMarkers
, crie uma instância doMarkerClusterer
e transfira a instância do componenteMap
onde você quer mostrar o clustering de marcadores:
useEffect(() => {
if (!map) return;
if (!clusterer.current) {
clusterer.current = new MarkerClusterer({map});
}
}, [map]);
- Crie um efeito que atualize o clusterer assim que a lista de marcadores mudar:
useEffect(() => {
clusterer.current?.clearMarkers();
clusterer.current?.addMarkers(Object.values(markers));
}, [markers]);
- Crie uma função para cunhar as referências dos marcadores novos:
const setMarkerRef = (marker: Marker | null, key: string) => {
if (marker && markers[key]) return;
if (!marker && !markers[key]) return;
setMarkers(prev => {
if (marker) {
return {...prev, [key]: marker};
} else {
const newMarkers = {...prev};
delete newMarkers[key];
return newMarkers;
}
});
};
- Use este método no elemento
AdvancedMarker
para criar a referência de cada marcador.
<AdvancedMarker
key={poi.key}
position={poi.location}
ref={marker => setMarkerRef(marker, poi.key)}
>
<Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
</AdvancedMarker>
Os clusters de marcadores vão aparecer no seu mapa:
Se você aumentar e diminuir o zoom, o MarkerClustererPlus
vai renumerar e redimensionar automaticamente os clusters. Também é possível clicar no ícone de qualquer cluster para aumentar o zoom e ver todos os marcadores incluídos nele.
Nesta seção, você importou e usou a biblioteca de utilitários de código aberto do MarkerClustererPlus
para criar uma instância de MarkerClusterer
. Com a ajuda do estado e das referências do React, essa instância agrupou de forma automática os marcadores criados na etapa anterior.
O componente PoiMarkers
vai ficar assim:
const PoiMarkers = (props: { pois: Poi[] }) => {
const map = useMap();
const [markers, setMarkers] = useState<{[key: string]: Marker}>({});
const clusterer = useRef<MarkerClusterer | null>(null);
// Initialize MarkerClusterer, if the map has changed
useEffect(() => {
if (!map) return;
if (!clusterer.current) {
clusterer.current = new MarkerClusterer({map});
}
}, [map]);
// Update markers, if the markers array has changed
useEffect(() => {
clusterer.current?.clearMarkers();
clusterer.current?.addMarkers(Object.values(markers));
}, [markers]);
const setMarkerRef = (marker: Marker | null, key: string) => {
if (marker && markers[key]) return;
if (!marker && !markers[key]) return;
setMarkers(prev => {
if (marker) {
return {...prev, [key]: marker};
} else {
const newMarkers = {...prev};
delete newMarkers[key];
return newMarkers;
}
});
};
return (
<>
{props.pois.map( (poi: Poi) => (
<AdvancedMarker
key={poi.key}
position={poi.location}
ref={marker => setMarkerRef(marker, poi.key)}
>
<Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
</AdvancedMarker>
))}
</>
);
};
Agora você vai aprender como lidar com interações dos usuários.
8. Adicionar interação do usuário
Agora seu mapa mostra alguns dos destinos turísticos mais famosos de Sydney. Nesta seção, você vai incluir algumas respostas a interações das pessoas usando o sistema de eventos da API Maps JavaScript para melhorar ainda mais a experiência no seu mapa.
A API Maps JavaScript oferece um sistema abrangente que usa manipuladores de eventos JavaScript para lidar com várias interações no código. Por exemplo, você pode criar listeners de eventos para acionar a execução do código quando houver interações dos usuários, como clicar no mapa e nos marcadores, mover o mapa, aumentar e diminuir o zoom e muito mais.
Siga as etapas abaixo para adicionar um listener de click
aos marcadores e mover o mapa de forma programática para centralizar o marcador que foi clicado:
- Crie uma callback de manipulador de
click
.
No componente PoiMarkers
, defina um manipulador de click
com o useCallback()
do React.
O evento click
é acionado sempre que um usuário clica/toca em um marcador e retorna um evento como um objeto JSON com informações sobre o elemento da interface que recebeu o clique. Para melhorar a experiência do usuário no mapa, você pode processar o evento click
e usar o respectivo objeto LatLng
para ver a latitude e a longitude do marcador clicado.
Quando você souber a latitude e a longitude, passe essas informações à função panTo()
integrada da instância do Map
. Com isso, o mapa vai se mover um pouco e recentralizar no marcador que recebeu o clique. Basta adicionar este código à função de callback do manipulador de eventos:
const PoiMarkers = (props: { pois: Poi[] }) => {
...
const handleClick = useCallback((ev: google.maps.MapMouseEvent) => {
if(!map) return;
if(!ev.latLng) return;
console.log('marker clicked:', ev.latLng.toString());
map.panTo(ev.latLng);
});
...
};
- Atribua os manipuladores de
click
aos marcadores.
Os elementos AdvancedMarker
da biblioteca vis.gl/react-google-map
(link em inglês) mostram duas propriedades que ajudam a processar os cliques:
clickable
: se verdadeiro, será possível clicar noAdvancedMarker
, o que vai acionar o eventogmp-click
, com interações pensando na acessibilidade, por exemplo, a navegação pelo teclado usando as teclas de seta.onClick
: a função de callback acionada quando um eventoclick
ocorre.
- Atualize a renderização do componente
PoiMarkers
para atribuir um manipulador declick
a cada marcador:
return (
<>
{props.pois.map( (poi: Poi) => (
<AdvancedMarker
... other properties ...
clickable={true}
onClick={handleClick}
>
...
</AdvancedMarker>
))}
</>
);
- Abra o navegador e clique nos marcadores. O mapa vai retornar automaticamente ao centro quando houver um clique no marcador.
Nesta seção, você usou o sistema de eventos do React para atribuir um manipulador de click
a todos os marcadores no mapa, recuperar a latitude e longitude do marcador do evento de click
disparado e usar isso para recentralizar o mapa sempre que um marcador receber um clique.
Falta apenas uma etapa. Agora, você vai melhorar ainda mais a experiência no mapa usando os recursos de desenho da API Maps JavaScript.
9. Desenhar no mapa
Até agora, você criou um mapa de Sydney que mostra marcadores dos destinos turísticos mais famosos e lida com interações dos usuários. Na última etapa deste codelab, você usa os recursos de desenho da API Maps JavaScript para adicionar outros recursos úteis à experiência no mapa.
Imagine que esse mapa será acessado pelos usuários que gostariam de conhecer Sydney. Um recurso útil seria ver um raio em torno de um marcador após clicar nele. Com isso, é possível quais outros destinos estão a uma curta distância do marcador selecionado.
A API Maps JavaScript inclui um conjunto de funções para desenhar formas no mapa, como quadrados, polígonos, linhas e círculos. A biblioteca vis.gl/react-google-map
(link em inglês) disponibiliza esses recursos no React.
Agora você vai renderizar um círculo para mostrar um raio de 800 metros ao redor de um marcador após ele receber um clique.
O repositório inicial tem um componente personalizado para um elemento de circle
, que você encontra no arquivo src/components/circle.tsx
.
Para permitir desenhos no mapa:
- Atualize as importações para que incluam o componente de círculo fornecido.
import {Circle} from './components/circle'
- Crie uma variável de estado para o centro do círculo.
Capture o estado do centro do círculo no componente PoiMarkers
. Defina o estado inicial como nulo, considerando que um círculo será renderizado apesar se tiver um local central válido (e um raio).
const PoiMarkers = (props: { pois: Poi[] }) => {
...
const [circleCenter, setCircleCenter] = useState(null)
...
};
- Atualize o centro do círculo quando um evento de
click
for manipulado.
Chame o setCircleCenter
com o local encontrado no objeto do evento:
const handleClick = useCallback((ev: google.maps.MapMouseEvent) => {
...
setCircleCenter(ev.latLng);
});
Essas funções da API Maps JavaScript oferecem diversas opções para um objeto desenhado aparecer no mapa. Para renderizar um raio circular, defina as propriedades do elemento de círculo, como cor e peso do traço, onde ele vai ficar centralizado, além do raio.
- Adicione um círculo à renderização e vincule o centro dele à variável de estado. A renderização vai ficar assim:
return (
<>
<Circle
radius={800}
center={circleCenter}
strokeColor={'#0c4cb3'}
strokeOpacity={1}
strokeWeight={3}
fillColor={'#3b82f6'}
fillOpacity={0.3}
/>
{props.pois.map( (poi: Poi) => (
<AdvancedMarker
key={poi.key}
position={poi.location}
ref={marker => setMarkerRef(marker, poi.key)}
clickable={true}
onClick={handleClick}
>
<Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
</AdvancedMarker>
))}
</>
);
};
Pronto! Abra o navegador e clique em um dos marcadores. Você vai ver um raio circular renderizado ao redor dele:
10. Parabéns!
Você criou seu primeiro app da Web com a biblioteca vis.gl/react-google-map
(link em inglês) para a Plataforma Google Maps e aprendeu a carregar a API Maps JavaScript e um mapa, trabalhar com marcadores, controlar e desenhar no mapa, além de adicionar interações dos usuários.
Para ver o código pronto, confira o diretório /solutions
.