Добавьте карту Google в приложение React

1. Прежде чем начать

В этой практической работе вы изучите всё необходимое для начала работы с библиотекой vis.gl/react-google-map для JavaScript API Google Карт, которая позволяет добавлять карты Google в приложение React. Вы научитесь настраивать библиотеку, загружать JavaScript API Карт, отображать свою первую карту, работать с маркерами и кластеризацией маркеров, рисовать на карте и обрабатывать взаимодействие с пользователем.

Предпосылки

  • Базовые знания JavaScript, HTML и CSS

Чему вы учитесь

  • Как начать работу с библиотекой vis.gl/react-google-map для платформы Google Maps.
  • Как декларативно загрузить Maps JavaScript API.
  • Как загрузить карту в приложение React.
  • Как использовать маркеры, пользовательские маркеры и кластеризацию маркеров.
  • Как работать с системой событий Maps JavaScript API для обеспечения взаимодействия с пользователем.
  • Как динамически управлять картой.
  • Как рисовать на карте.

Что вам нужно

  • Учетная запись Google Cloud с включенной функцией выставления счетов.
  • Ключ API платформы Google Карт с включенным API JavaScript Карт.
  • Node.js установлен на вашем компьютере.
  • Текстовый редактор или IDE по вашему выбору.
  • Библиотека vis.gl/react-google-map для JavaScript API Google Maps.
  • Библиотека googlemaps/markerclusterer

Настройте платформу Google Карт

Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенным выставлением счетов, ознакомьтесь с руководством « Начало работы с Google Maps Platform», чтобы создать учетную запись для выставления счетов и проект.

  1. В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.

  1. Включите API и SDK платформы Google Карт, необходимые для этой лабораторной работы, в Google Cloud Marketplace . Для этого следуйте инструкциям в этом видео или в этой документации .
  2. Сгенерируйте ключ API на странице «Учётные данные» в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к платформе Google Карт требуется ключ API.

2. Настройте

Загрузите стартовый проект

Чтобы загрузить шаблон стартового проекта и код решения, выполните следующие действия:

  1. Скачайте или создайте форк репозитория GitHub . Стартовый проект находится в каталоге /starter и включает в себя базовую файловую структуру, необходимую для выполнения лабораторной работы. Вся работа выполняется в каталоге /starter/src .
git clone https://github.com/googlemaps-samples/codelab-maps-platform-101-react-js.git

Или нажмите эту кнопку, чтобы загрузить исходный код.

  1. Перейдите в каталог /starter и установите npm. Это установит все необходимые зависимости, перечисленные в файле package.json .
cd starter && npm install
  1. Пока еще в каталоге /starter :
npm start

Стартовый проект настроен для использования сервера разработки Vite, который компилирует и запускает написанный вами код локально. Сервер разработки Vite также автоматически перезагружает ваше приложение в браузере при каждом изменении кода. Перейдя по ссылке, предоставленной в конце процесса сборки, вы увидите веб-страницу с надписью «Hello, world!».

  1. Если вы хотите запустить полный код решения, перейдите в каталог /solution и выполните те же шаги по настройке.

3. Загрузите API JavaScript Карт

Основой использования платформы Google Карт для веб-приложений является API JavaScript для Карт. Этот API предоставляет JavaScript-интерфейс для использования всех функций платформы Google Карт, включая карту, маркеры, инструменты рисования и другие сервисы платформы Google Карт, такие как Places.

Чтобы загрузить Maps JavaScript API с помощью фреймворка React, необходимо использовать компонент APIProvider из библиотеки vis.gl/react-google-map . Этот компонент можно добавить на любой уровень приложения, обычно где-то в самом верху, и он отображает все дочерние компоненты без изменений. Помимо обработки загрузки Maps JavaScript API, он также предоставляет контекстную информацию и функции для других компонентов и хуков этой библиотеки. APIProvider входит в библиотеку vis.gl/react-google-map , поэтому он был установлен при выполнении команды npm install ранее.

Чтобы использовать компонент APIProvider , выполните следующие действия:

  1. Откройте файл /src/app.tsx . В этом файле вы будете выполнять всю работу по этой лабораторной работе.
  2. В верхней части файла импортируйте класс APIProvider из библиотеки @ vis.gl/react-google-maps :
import {APIProvider} from '@vis.gl/react-google-maps';
  1. В определении функции App задайте параметр apiKey компонента APIProvider с API Key, созданным на предыдущем шаге, а свойство onLoad — с сообщением журнала консоли:
<APIProvider apiKey={'Your API key here'} onLoad={() => console.log('Maps API has loaded.')}>

Компонент APIProvider принимает ряд свойств, которые определяют различные параметры загрузки Maps JavaScript API, включая ваш ключ API платформы Google Карт, версию API, которую вы хотите загрузить, и любые дополнительные библиотеки, предоставляемые Maps JavaScript API, которые вы хотите загрузить.

Ключ API Google Карт — единственное необходимое свойство для работы APIProvider , и мы включили свойство onLoad для демонстрационных целей. Подробнее см. в разделе Компонент <APIProvider> .

Ваш файл app.tsx должен выглядеть так:

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;

Если всё прошло успешно, вы увидите запись console.log в консоли браузера. Теперь, когда Maps JavaScript API загружен, на следующем шаге можно отрисовать динамическую карту.

4. Отобразить карту

Пришло время показать вашу первую карту!

Наиболее часто используемая часть JavaScript API Карт — это класс google.maps.Map , позволяющий создавать экземпляры карт и управлять ими. Библиотека vis.gl/react-google-map оборачивает этот класс в компонент Map . Сначала импортируйте классы Map и MapCameraChangedEvent .

import {APIProvider, Map, MapCameraChangedEvent} from '@vis.gl/react-google-maps';

Компонент Map поддерживает множество различных настроек карты. В этой лабораторной работе используются следующие настройки:

  • defaultCenter , который задает широту и долготу центра карты.
  • defaultZoom , который задает начальный уровень масштабирования карты.
  • Чтобы отобразить карту, поместите следующий код в теги APIProvider , чтобы центрировать карту на Сиднее, Австралия, и задайте уровень масштабирования 13 , что является правильным уровнем масштабирования для отображения центра города:
 <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>

Теперь вы должны увидеть карту Сиднея в своем браузере:

761c8c51c6631174.png

Итак, в этом разделе вы отобразили карту с помощью компонента <Map> и задали её начальное состояние с помощью свойств. Вы также использовали события для отслеживания смены положения камеры.

Ваш файл app.tsx должен выглядеть примерно так:

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. Добавьте облачный стиль карты.

Идентификатор карты необходим для использования расширенных маркеров, которые используются для отметки точек интереса на карте Сиднея. Идентификатор карты также используется для облачного оформления карты.

Вы можете настроить стиль своей карты, используя облачный стиль карт .

Создать идентификатор карты

Если вы еще не создали идентификатор карты со связанным с ним стилем карты, см. руководство по идентификаторам карт , чтобы выполнить следующие шаги:

  1. Создайте идентификатор карты.
  2. Свяжите идентификатор карты со стилем карты.

Чтобы использовать созданный вами идентификатор карты, задайте свойство mapId компонента <Map> :

<Map
    defaultZoom={13}
    defaultCenter={ { lat: -33.860664, lng: 151.208138 } }
    mapId='DEMO_MAP_ID'
    onCameraChanged={ (ev: MapCameraChangedEvent) =>
        console.log('camera changed:', ev.detail.center, 'zoom:', ev.detail.zoom)
    }>
</Map>

Вы должны увидеть выбранный вами стиль на карте!

6. Добавьте маркеры на карту.

Разработчики используют JavaScript API Карт для множества задач, но размещение маркеров на карте, безусловно, является самым популярным. Маркеры позволяют отображать определённые точки на карте и являются распространённым элементом пользовательского интерфейса для взаимодействия с пользователем. Если вы уже пользовались Google Картами, вы, вероятно, знакомы с маркером по умолчанию, который выглядит так:

d9a6513b82a2f1e1.png

Чтобы использовать компонент AdvancedMarker для размещения маркеров на карте, выполните следующие действия:

  1. Создайте список объектов, представляющих достопримечательности в районе Сиднея, разместите его прямо под импортированными объектами, за пределами определения 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 = () => (
  ...
);
  1. Настройте свои пины с помощью элемента <Pin> :
<Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
  1. Создайте пользовательский компонент для визуализации вашего списка с помощью расширенных маркеров, разместите его под определением 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>
      ))}
    </>
  );
};
  1. Добавьте компонент PoiMarkers как дочерний элемент компонента Map :
<Map
  ... map properties ...
>
  <PoiMarkers pois={locations} />
</Map>
  1. Наконец, добавьте Pin и AdvancedMarker к вашим импортам.
import {
  APIProvider,
  Map,
  AdvancedMarker,
  MapCameraChangedEvent,
  Pin
} from '@vis.gl/react-google-maps';

На карте вы увидите настроенные расширенные маркеры:

98d12a994e12a2c1.png

7. Включить кластеризацию маркеров

При использовании большого количества маркеров или маркеров, расположенных близко друг к другу, вы можете столкнуться с проблемой, когда маркеры накладываются друг на друга или располагаются слишком плотно, что негативно сказывается на пользовательском опыте. Например, после создания маркеров на последнем этапе вы могли заметить следующее:

98d12a994e12a2c1.png

Вот тут-то и пригодится кластеризация маркеров. Кластеризация маркеров — еще одна часто реализуемая функция, которая группирует близлежащие маркеры в один значок, который меняется в зависимости от уровня масштабирования, вот так:

3da24a6b737fe499.png

Алгоритм кластеризации маркеров разбивает видимую область карты на сетку, а затем группирует значки, находящиеся в одной ячейке. К счастью, вам не нужно беспокоиться об этом, поскольку команда платформы Google Карт создала полезную библиотеку с открытым исходным кодом MarkerClustererPlus , которая делает всё автоматически. Вы можете ознакомиться с исходным кодом библиотеки MarkerClustererPlus на GitHub .

Чтобы включить кластеризацию маркеров, выполните следующие действия:

  1. В верхней части файла app.tsx давайте обновим и добавим в нашу библиотеку импорты и вспомогательные типы.
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';

В шаблоне проекта для этой лабораторной работы библиотека утилит MarkerClustererPlus уже включена в зависимости, объявленные в файле package.json , поэтому вы уже установили ее, когда запустили npm install в начале этой лабораторной работы.

  1. Создайте переменные для MarkerClusterer и вспомогательных элементов в компоненте PoiMarkers .

Для инициализации MarkerClusterer вам понадобится экземпляр карты. Получите его с помощью хука useMap() :

const map = useMap();
  1. Создайте список маркеров, сохраненных в переменной состояния:
const [markers, setMarkers] = useState<{[key: string]: Marker}>({});
  1. Сохраните кластеризатор как ссылку:
const clusterer = useRef<MarkerClusterer | null>(null);
  1. Также в компоненте PoiMarkers создайте экземпляр MarkerClusterer и передайте ему экземпляр Map , на которой вы хотите отобразить кластеры маркеров:
 useEffect(() => {
    if (!map) return;
    if (!clusterer.current) {
      clusterer.current = new MarkerClusterer({map});
    }
  }, [map]);
  1. Создайте эффект, который обновляет кластеризатор при изменении списка маркеров:
useEffect(() => {
    clusterer.current?.clearMarkers();
    clusterer.current?.addMarkers(Object.values(markers));
  }, [markers]);
  1. Создайте функцию для создания ссылок на новые маркеры:
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;
      }
    });
  };
  1. Используйте этот метод в элементе AdvancedMarker для создания ссылки для каждого маркера.
<AdvancedMarker
  key={poi.key}
  position={poi.location}
  ref={marker => setMarkerRef(marker, poi.key)}
  >
    <Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
</AdvancedMarker>

Теперь вы должны увидеть кластеры маркеров на своей карте:

3da24a6b737fe499.png

При увеличении или уменьшении масштаба MarkerClustererPlus автоматически перенумерует и изменит размер кластеров. Вы также можете нажать на значок любого кластера маркеров, чтобы увеличить масштаб и увидеть все маркеры, входящие в этот кластер.

d5e75480e9abd3c7.png

Подведем итог: в этом разделе вы импортировали служебную библиотеку MarkerClustererPlus с открытым исходным кодом и использовали ее для создания экземпляра MarkerClusterer , который с помощью состояния и ссылок React автоматически кластеризовал маркеры, созданные вами на предыдущем шаге.

Ваш компонент PoiMarkers должен выглядеть следующим образом:

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

Далее вы узнаете, как обрабатывать взаимодействие с пользователем.

8. Добавьте взаимодействие с пользователем

Теперь у вас есть великолепная карта, отображающая некоторые из самых популярных туристических мест Сиднея. В этом разделе мы добавим дополнительную обработку взаимодействия пользователя с системой событий JavaScript API Карт, чтобы ещё больше улучшить пользовательский опыт использования вашей карты.

API JavaScript для Карт предоставляет комплексную систему событий, которая использует обработчики событий JavaScript для обработки различных взаимодействий пользователя в коде. Например, вы можете создать прослушиватели событий, которые будут запускать выполнение кода при таких взаимодействиях, как нажатие пользователем на карту и маркеры, панорамирование карты, увеличение и уменьшение масштаба и т. д.

Чтобы добавить прослушиватель click к маркерам, а затем программно панорамировать карту так, чтобы нажатый маркер отображался в центре карты, выполните следующие действия:

  1. Создайте обратный вызов обработчика click .

В компоненте PoiMarkers определите обработчик click с помощью метода React useCallback() .

Событие click срабатывает каждый раз, когда пользователь щёлкает или касается маркера, и возвращает событие в виде JSON-объекта с информацией об элементе пользовательского интерфейса, на который был нажат. Чтобы улучшить взаимодействие пользователя с картой, можно обработать событие click и использовать его объект LatLng для получения широты и долготы нажатого маркера.

Получив широту и долготу, передайте их встроенной функции panTo() экземпляра Map , чтобы карта плавно перемещалась к центру на выбранном маркере, добавив следующее в функцию обратного вызова обработчика событий:

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);
  });
...
};
  1. Назначьте обработчики click маркерам.

Элементы AdvancedMarker библиотеки vis.gl/react-google-map предоставляют два свойства, которые полезны для обработки кликов:

  • clickable : Если true, AdvancedMarker будет доступен для нажатия и вызовет событие gmp-click , а также будет интерактивным для обеспечения доступности. Например, он позволит осуществлять навигацию с помощью клавиатуры с помощью клавиш со стрелками.
  • onClick : Функция обратного вызова, вызываемая при возникновении события click .
  1. Обновите визуализацию PoiMarkers , чтобы назначить обработчик click каждому маркеру:
return (
    <>
      {props.pois.map( (poi: Poi) => (
        <AdvancedMarker
          ... other properties ...
          clickable={true}
          onClick={handleClick}
          >
           ...
        </AdvancedMarker>
      ))}
    </>
  );
  1. Откройте браузер и нажмите на маркеры. Вы увидите, как карта автоматически центрируется при нажатии на маркер.

Подведем итог: в этом разделе вы использовали систему событий React, чтобы назначить обработчик click всем маркерам на карте, извлекли широту и долготу маркера из сработавшего события click и использовали их для центрирования карты при каждом щелчке по маркеру.

Остался всего один шаг! Теперь вы можете ещё больше улучшить пользовательский интерфейс карты с помощью функций рисования Maps JavaScript API.

9. Нарисуйте на карте

На данный момент вы создали карту Сиднея, на которой отмечены популярные туристические направления и реализовано взаимодействие с пользователем. На последнем этапе этой лабораторной работы вы используете функции рисования JavaScript API Карт, чтобы добавить дополнительную полезную функцию к своей карте.

Представьте, что эта карта будет использоваться пользователями, желающими исследовать Сидней. Полезной функцией было бы визуализировать радиус вокруг маркера при нажатии на него. Это позволило бы пользователю понять, какие ещё места находятся в пешей доступности от выбранного маркера.

API JavaScript для Карт включает набор функций для рисования фигур на карте, таких как квадраты, многоугольники, линии и окружности. Библиотека vis.gl/react-google-map делает эти возможности доступными в React.

Затем вы рисуете круг, показывающий радиус 800 метров (примерно полмили) вокруг маркера при щелчке по нему.

Стартовый репозиторий содержит пользовательский компонент для элемента circle . Его можно найти в файле src/components/circle.tsx .

Чтобы разрешить пользователям рисовать на карте, выполните следующие действия:

  1. Обновите импорт, включив в него предоставленный компонент Circle.
import {Circle} from './components/circle'
  1. Создайте переменную состояния для центра окружности.

Зафиксируйте состояние центра окружности в компоненте PoiMarkers . Вы устанавливаете начальное состояние равным null и исходите из того, что окружность не будет отображаться, если у неё нет корректного центра (и радиуса).

const PoiMarkers = (props: { pois: Poi[] }) => {
...
  const [circleCenter, setCircleCenter] = useState(null)
...
};
  1. Обновлять центр круга при обработке события click .

Вызовите setCircleCenter , указав местоположение, найденное в объекте события:

const handleClick = useCallback((ev: google.maps.MapMouseEvent) => {
    ...
    setCircleCenter(ev.latLng);
  });

Функции рисования в Maps JavaScript API предоставляют широкий выбор вариантов отображения объекта на карте. Чтобы отобразить круг с радиусом, задайте свойства элемента «круг», такие как цвет и толщина обводки, а также центр круга и его радиус.

  1. Добавьте круг к вашему рендерингу и привяжите его центр к переменной состояния. Ваш рендеринг должен выглядеть так:
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>
      ))}
    </>
  );
};

Готово! Откройте браузер и нажмите на один из маркеров. Вокруг него должен появиться круг:

d243587f4a9ec4a6.png

10. Поздравления

Вы создали свое первое веб-приложение с помощью библиотеки vis.gl/react-google-map для платформы Google Maps, включая загрузку API JavaScript Карт, загрузку карты, работу с маркерами, управление и рисование на карте, а также добавление взаимодействия с пользователем.

Для просмотра готового кода см. каталог /solutions .

Узнать больше