React アプリに Google マップを追加する

1.始める前に

この Codelab では、Google Maps JavaScript API の vis.gl/react-google-map ライブラリを使用するのに必要な情報をすべて確認できます。これらの情報を基に、React アプリに Google マップを追加できます。事前の設定、Maps JavaScript API の読み込み、最初の地図の表示、マーカーとマーカー クラスタリングの操作、地図上での図形の描画、ユーザー操作の処理に関する方法を学習できます。

前提条件

  • JavaScript、HTML、CSS に関する基礎的な知識

学習内容

  • Google Maps Platform の vis.gl/react-google-map ライブラリを使用する方法
  • Maps JavaScript API を宣言的に読み込む方法
  • React アプリに地図を読み込む方法
  • マーカー、カスタム マーカー、マーカー クラスタリングを使用する方法
  • Maps JavaScript API のイベント システムを使用してユーザー操作を処理する方法
  • 地図を動的にコントロールする方法
  • 地図上に図形を描画する方法

必要なもの

  • 課金が有効になっている Google Cloud アカウント
  • Maps JavaScript API を有効にした Google Maps Platform API キー
  • Node.js(パソコンにインストール)
  • 任意のテキスト エディタまたは IDE
  • Google Maps JavaScript API の vis.gl/react-google-map ライブラリ
  • googlemaps/markerclusterer ライブラリ

Google Maps Platform を設定する

課金を有効にした Google Cloud Platform アカウントとプロジェクトをまだ作成していない場合は、Google Maps Platform スタートガイドに沿って請求先アカウントとプロジェクトを作成してください。

  1. Cloud Console で、プロジェクトのプルダウン メニューをクリックし、この Codelab に使用するプロジェクトを選択します。

  1. Google Cloud Marketplace で、この Codelab に必要な Google Maps Platform API と SDK を有効にします。詳しい手順については、こちらの動画またはドキュメントをご覧ください。
  2. Cloud Console の [認証情報] ページで API キーを生成します。詳しい手順については、こちらの動画またはドキュメントをご覧ください。Google Maps Platform へのすべてのリクエストで API キーが必要になります。

2. 準備

スターター プロジェクトをダウンロードする

スターター プロジェクト テンプレートとソリューション コードをダウンロードするには、次の手順を行います。

  1. GitHub リポジトリをダウンロードまたはフォークします。スターター プロジェクトは /starter ディレクトリに配置されており、この Codelab の実行に必要な基本的なファイル構造が含まれています。作業はすべて /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 development server を使用するように設定されています。Vite development server により、ローカルに記述したコードがコンパイルされ、実行されます。また、コードを変更するたびに、ブラウザ上でアプリが自動的に再読み込みされます。ビルドプロセスの最後に記載されているリンクをクリックすると、「Hello, world!」というウェブページが表示されます。

  1. ソリューション コード全体を実行する場合は、/solution ディレクトリに移動して、同じセットアップ手順を実施します。

3. Maps JavaScript API を読み込む

Google Maps Platform をウェブで使用するための基盤となるのが Maps JavaScript API です。この API は、Google Maps Platform のすべての機能を使用するための JavaScript インターフェースです。

地図、マーカー、描画ツールに加え、プレイスなど、その他の Google Maps Platform サービスを使用できます。

React フレームワークを使用して Maps JavaScript API を読み込むには、vis.gl/react-google-map ライブラリに含まれる APIProvider コンポーネントを使用する必要があります。このコンポーネントは、アプリのどのレベルでも追加できます。通常は上部に追加して、すべての子コンポーネントを変更せずにレンダリングします。Maps JavaScript API の読み込みを処理するだけでなく、このライブラリの他のコンポーネントとフックに関するコンテキスト情報と関数も提供します。APIProvidervis.gl/react-google-map ライブラリに含まれているため、先ほど npm install を実行した際にインストール済みです。

APIProvider コンポーネントを使用するには、次の手順を行います。

  1. /src/app.tsx ファイルを開きます。本 Codelab の作業はすべてこのファイルで行います。
  2. ファイルの上部で、@vis.gl/react-google-maps ライブラリから APIProvider クラスをインポートします。
import {APIProvider} from '@vis.gl/react-google-maps';
  1. App 関数定義で、前の手順で作成した API キーと、コンソール ログ メッセージを含む onLoad プロパティを使用して、APIProvider コンポーネントの apiKey パラメータを設定します。
<APIProvider apiKey={'Your API key here'} onLoad={() => console.log('Maps API has loaded.')}>

APIProvider コンポーネントでは、Maps JavaScript API を読み込む際のさまざまなオプション(Google Maps Platform API キー、読み込む API のバージョン、Maps JavaScript API の追加ライブラリの中で読み込みたいものなど)を指定する、一連のプロパティを受け取ります。

Google Maps API キーは 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. 地図を表示する

ではいよいよ地図を表示してみましょう。

Maps JavaScript API で最も頻繁に使用するのは google.maps.Map です。このクラスを使用して地図インスタンスを作成し、操作します。vis.gl/react-google-map ライブラリは Map コンポーネントでこのクラスをラップします。まずは、Map クラスと MapCameraChangedEvent クラスをインポートします。

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

Map コンポーネントは地図のさまざまな設定をサポートしています。この Codelab では、次の設定を使用します。

  • 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. Cloud ベースのマップのスタイル設定を追加する

Cloud ベースのマップのスタイル設定は、高度なマーカーを使用する際に必要となります。高度なマーカーは、シドニーの地図で場所を示すために使用します。

Cloud ベースのマップのスタイル設定によって地図のスタイルをカスタマイズすることも可能です。

マップ ID を作成する

地図スタイルを関連付けたマップ ID の作成が済んでいない場合は、マップ ID のガイドを参照して、以下を行いましょう。

  1. マップ ID を作成します。
  2. マップ ID を地図スタイルに関連付けます。

作成したマップ ID を使用するには、<Map> コンポーネントの mapId プロパティを設定します。

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

これで、選択したスタイルが地図に反映されます。

6. 地図にマーカーを追加する

Maps JavaScript API で実行できる操作は多数ありますが、最も頻繁に使用するのは地図上にマーカーを配置する操作です。地図上の特定の地点を示すマーカーは、ユーザー操作を処理するための一般的な UI 要素です。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. Map の子コンポーネントとして、PoiMarkers コンポーネントを追加します。
<Map
  ... map properties ...
>
  <PoiMarkers pois={locations} />
</Map>
  1. 最後に、PinAdvancedMarker をインポートに追加します。
import {
  APIProvider,
  Map,
  AdvancedMarker,
  MapCameraChangedEvent,
  Pin
} from '@vis.gl/react-google-maps';

地図にカスタマイズされた高度なマーカーが表示されます。

98d12a994e12a2c1.png

7. マーカー クラスタリングを有効にする

マーカーの数が多くて密集している場合、複数のマーカーが重なって表示され、ユーザーの利便性が低下します。たとえば、ステップ 5 で作成したマーカーは次のように表示されます。

98d12a994e12a2c1.png

このような場合に効果を発揮するのがマーカー クラスタリングです。マーカー クラスタリングも広く利用されている機能で、以下のように、近接する複数のマーカーをズームレベルに応じて 1 つのアイコンにグループ化できます。

3da24a6b737fe499.png

マーカー クラスタリングのアルゴリズムは、地図の表示可能領域をグリッドに分割し、同じセル内にあるアイコンをクラスタ化します。この処理は、Google Maps Platform チームが作成したオープンソース ユーティリティ ライブラリ 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';

この Codelab のテンプレート プロジェクトでは、MarkerClustererPlus ユーティリティ ライブラリは package.json ファイルで宣言されている依存関係にあらかじめ含まれているため、この Codelab の冒頭で npm install を実行した際にインストールが完了しています。

  1. PoiMarkers コンポーネントで MarkerClusterer およびサポートされる要素の変数を作成します。

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.ユーザー操作を追加する

シドニーの人気観光スポットを示す地図が完成しました。このセクションでは、Maps JavaScript API のイベント システムを使用してユーザー操作を処理し、地図の利便性を向上させます。

Maps JavaScript API には、コードで JavaScript イベント ハンドラを使用してさまざまなユーザー操作を処理できる、包括的なイベント システムが用意されています。たとえば、地図やマーカーをクリックする、地図表示をパンする、ズームイン / ズームアウトするなど、ユーザーがなんらかの操作を行ったときにコードを実行するイベント リスナーを作成できます。

マーカーに click リスナーを追加し、クリックされたマーカーが中央に表示されるように地図をパンするには、次の手順を行います。

  1. click ハンドラ コールバックを作成します。

PoiMarkers コンポーネントで、React の useCallback() を使用して click ハンドラを定義します。

ユーザーがマーカーをクリックまたはタップするたびに click イベントがトリガーされます。このイベントは、クリックされた UI 要素に関する情報を含む JSON オブジェクトをイベントとして返します。地図の利便性を高めるため、click イベントを処理し、LatLng オブジェクトを使用して、クリックされたマーカーの緯度と経度を取得します。

その後で、イベント ハンドラのコールバック関数に以下のコードを追加します。これにより、取得した位置情報を Map インスタンスの組み込み panTo() 関数に渡し、地図をスムーズにパンして、クリックされたマーカーを中央に表示できます。

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 ハンドラをマーカーに割り当てます。

vis.gl/react-google-map ライブラリの AdvancedMarker 要素が、クリックを処理するのに有用な 2 つのプロパティを公開します。

  • clickable: 該当する場合は、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 イベントからマーカーの緯度と経度を取得し、その情報を使用して、クリックされたマーカーが中央に表示されるように地図をパンしました。

あと 1 ステップで完了です。次は、Maps JavaScript API の描画機能を使用して、地図の利便性をさらに向上させます。

9.地図上に図形を描画する

人気観光スポットがマーカーとして表示され、ユーザーの操作を処理できるシドニーの地図が完成しました。この Codelab の最後の手順では、Maps JavaScript API の描画機能を使用して、地図を操作するうえで有用な機能を追加します。

この地図は、シドニー市内を探索する観光客が使用することを想定しています。クリックされたマーカーを中心とする半径を地図上に描画し、その地点から歩いて行ける範囲がわかるようにしましょう。

Maps JavaScript API には、正方形、ポリゴン、ライン、円などの図形を地図上に描画する関数が用意されています。vis.gl/react-google-map ライブラリによって、React でこれらを利用可能にできます。

次に、クリックされたマーカーを中心に、半径 800 メートル(約 0.5 マイル)の円を描画します。

スターター リポジトリには、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.完了

Google Maps Platform の vis.gl/react-google-map ライブラリを使用して、最初のウェブアプリを構築しました。Maps JavaScript API および地図の読み込み、マーカーの操作、地図のコントロールと描画、ユーザー操作の追加を行いました。

最終的なコードは、/solutions ディレクトリでご確認いただけます。

詳細