React.jsで一通り地図を扱う【React-Leaflet】

目次

用語説明

Reactとは?
JavaScriptフレームワーク御三家の一。
Leafletとは?
→Webアプリで地図を扱うためのライブラリ。
React-Leafletとは?
→Reactに入れて使うLeafletのモジュール。

はじめに

Reactで地図を使ってる記事が少なかったので。

素のJavaScriptで地図を使おうとすると、画面の状態管理がめちゃくちゃしんどいですが、Reactの力である程度のりきることができます。

(Leafletが2.0に更新されてReactのContext APIをサポートしてたり、国土地理院がベクトルタイルを公開し始めたりと、地図を使ったWebアプリを作るのになにかと追い風な状況です。)

皆さんも地図を使ったWebアプリ始めましょう。

f:id:tatsuzaki98:20200111163930p:plain

⭐️とりあえず地図を表示する⭐️

Reactアプリを立ち上げて、react-leafletを導入します。

npx create-react-app app
cd app
npm install react-leaflet leaflet

まずは、LeafletのMap要素だけを表示してみましょう。
説明のため、コードにコメントをつけています。

/* src/App.js */
import React from 'react';
import './App.css';
import {Map} from 'react-leaflet';
// leafletのCSSを別に読み込む必要があります(これ公式に書いてなくて結構詰まった)
import '../node_modules/leaflet/dist/leaflet.css';

const App = () => (
  // Map要素は画面読み込み時の座標(center: LatLngTupple)と縮尺(zoom: number)を指定する必要があります。
  // viewport: {center, zoom} 形式で指定することもできます。
  <Map center={[34.9843, 135.7596]} zoom={7}/>
);

export default App;
/* src/App.css */
.leaflet-container {
  height: 100vh;
  width: 100vw;
}
/* デフォルトではMap要素の高さが0なので、なんらかの高さを指定する必要があります。Map要素のクラス名前はleaflet-containerです。 */

こんな感じで灰色の空っぽの地図が表示されます。
f:id:tatsuzaki98:20200111115315p:plain

このままでは地図としての役割を果たさないので、地図タイル(説明は後述)を追加します。

import React from 'react';
import './App.css';
import '../node_modules/leaflet/dist/leaflet.css';
import { Map, TileLayer } from 'react-leaflet';

// 国土地理院の地図タイルを利用します。
// 国土地理院に限らず、Attributionの記述は必須です。
const tileAttribution = '<a href="https://maps.gsi.go.jp/development/ichiran.html">地理院タイル</a>';
const tileUrl = 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png';

const App = () => (
  <Map center={[34.9843, 135.7596]} zoom={7}>
    <TileLayer attribution={tileAttribution} url={tileUrl} />
  </Map>
);

export default App;

ここまで書くと、いい感じで地図が表示されます。
f:id:tatsuzaki98:20200111122023p:plain

地図タイルの説明😉✨↓

地図はいわば大きな一枚の画像ですが、これを全て読み込むとなると大変です。
そこで、小さな正方形の画像に区切ってサーバーに保存しておき、必要な領域の画像だけサーバーから受け取る方法が考え出されました。
この区切られた正方形の画像が地図タイルで、地図タイルを配布しているサーバーをタイルサーバーといいます。
TileLayer要素にタイルサーバーのURLを指定することで、Leafletがユーザーの操作に応じて地図タイルをリクエストして表示してくれます。


地図タイルの概念はこちらのページがわかりやすいと思いますタイル座標 | 国土地理院

⭐️Leaflet要素いろいろ⭐️

Map要素の内側にReact-Leafletの要素を追記することで、地図に色々書くことができます。

・範囲円を表示

<Circle center={[34.9843, 135.7596]} radius={100000} />

f:id:tatsuzaki98:20200111133415p:plain

・線を表示

<Polyline positions={[[35, 136], [34, 135], [34.5, 137]]} color='red' />

f:id:tatsuzaki98:20200111133914p:plain

・マーカーを表示

import {icon} from 'leaflet';
import iconImage from './marker.png'
...
const markerIcon = icon({
  iconUrl: iconImage,
  iconSize: [20, 32],
  iconAnchor: [20/2, 32],
});
...
<Marker position={[34.9843, 135.7596]} icon={markerIcon} />

marker.pngの部分は好きな画像に置き換えることができます。
f:id:tatsuzaki98:20200111144122p:plain

ここらへんの詳しいことはReact-Leaflet ComponentsLeaflet API referenceを参考にしてみてください。

最後に1つ、GeoJsonというモノを紹介しておきます。
GeoJsonは2016年ごろにRFC 7946で標準化された、地理情報をJSON形式で扱うためのフォーマットです。

LeafletでももちろんGeoJSON形式を読み込むことができます。
地理情報のデータセットを表示する場合は、MarkerやCircleをArray.mapするより、GeoJSON形式で表示してしまうほうが、コードの見通しがよくなります。

・GeoJSONを表示
今回は例として京都駅のバス停のデータセットを利用します。(国土数値情報 バス停留所データの詳細

データをこんな感じでGeoJSON形式に整形します。
f:id:tatsuzaki98:20200111164755p:plain

Reactのソースコード

...

import { Map, TileLayer, GeoJSON } from 'react-leaflet';
import {icon, marker} from 'leaflet';
import geoData from './geodata.json';
import iconImage from './marker-icon.png';

...

const markerIcon = icon({
  iconUrl: iconImage,
  iconSize: [20, 32],
  iconAnchor: [20/2, 32],
  popupAnchor: [0, -32],
});

...

<Map ... >

...

<GeoJSON
  data={geoData}
  // pointToLayerは、GeoJSONの中のそれぞれのFeature(要素)ごとにLeaflet要素を返し、レイヤに追加する。
  pointToLayer={(_feature, latlng) => marker(latlng, {icon: markerIcon})}
  // onEachFeatureは、それぞれのFeatureに対してイベントを指定したり設定を適用したりできる。
  onEachFeature={(feature, layer) => layer.bindPopup(feature.properties.label)}
/>
</Map>

...

f:id:tatsuzaki98:20200111163930p:plain

⭐️イベント管理いろいろ⭐️

後日追記