【React.js + Leaflet】ReactLeafletで地図を表示しながらLeafletの理解を深める
はじめに
Leafletの恩恵を受けたい!!でもどうしてもプロジェクトに導入するにはラッパーを用いることが多い。とはいえLeafletの理解なしではいいかんじのコンポーネントもうまく使いこなせない・・・ということでLeafletのコードも書きつつReactLeafletを触ってみました。
Leafletについて
Leaflet
OpenStreetMapなどのマップと組み合わせることによってWebで地図を表示することができるJavaScriptライブラリ
React Leaflet
React.js上で使うためのラッパーライブラリでいいかんじに抽象化されたコンポーネントとして利用することができる
環境
- leaflet v1.7.1
- react-leaflet v3.2.5
導入から地図表示まで
導入からデフォルトの地図表示までをReactLeafletのチュートリアルを参考に進めていく
プロジェクト作成
npx create-react-app sample-react-leaflet
ライブラリインストール
npm install leaflet react-leaflet
コンポーネント作成
MapContainer
でマップオブジェクトを作成し、TileLayer
で地図を表示する。
レイヤーの概念がいくつかあり混同しやすいがここでは名前の通りタイル状に分割されたでOpenStreetMap地図データを表示するためTileLayer
を用いている。
import React from 'react';
import { MapContainer, TileLayer } from 'react-leaflet'
import './Map.css';
export const Map = () => {
// 緯度軽度
const position = [51.505, -0.09];
// 初期マップズームレベル
const zoom = 13;
return (
<MapContainer center={position} zoom={zoom}>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</MapContainer>
)
};
高さを指定しないと地図が表示されない
.leaflet-container {
width: 600px;
height: 300px;
margin: 10px;
}
コンポーネントを表示
import 'leaflet/dist/leaflet.css';
import { Map } from "./components/Map";
function App() {
return (
<div className="App">
<Map />
</div>
);
}
export default App;
起動
npm run start
ブラウザを確認
http://localhost:3000/
にアクセスして表示を確認する🎉
Leafletでの実装
ReactLeafletではMapContainer
やTileLayer
コンポーネンを使っているが実際はLeafletの実装を抽象化したものである。コンポーネントのPropsで渡していた値はオプション等で渡す必要がる(プラグインの読み込みやスタイルは省略)
<div id="map"></div>
<script>
const position = [51.505, -0.09];
const zoom = 13;
let map = L.map('map', {
center: position,
zoom: zoom
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
</script>
地図にマーカーとポップアップを表示
React Leaflet
そのままではMarker画像が表示されないので代替的にCDNから取得
MapContainer
にMarker
とPopup
を追加してマーカーとポップアップを表示します
import React from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'
import './Map.css';
import L from 'leaflet'
L.Icon.Default.imagePath = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/'
export const MarkerWithPopup = () => {
const position = [51.505, -0.09];
const zoom = 13;
return (
<MapContainer center={position} zoom={zoom}>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={position}>
<Popup>
A pretty CSS3 popup. <br/> Easily customizable.
</Popup>
</Marker>
</MapContainer>
)
};
Leaflet
<div id="map"></div>
<script>
const position = [51.505, -0.09];
const zoom = 13;
let map = L.map('map', {
center: position,
zoom: zoom
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
let marker = L.marker(position).addTo(map)
marker.bindPopup('A pretty CSS3 popup. <br/> Easily customizable.')
</script>
地図のマーカーをドラッグ可能にしてオプションを試す
地図のマーカーとポップアップに手を加えてマーカーをドラッグ可能にし、またドラッグ中は少しマーカーを透過、ドラッグを離したポイントの軽度緯度をポップアップに表示するようにする。
React Leaflet
import React, { useState, useRef } from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'
import './Map.css';
import L from 'leaflet'
L.Icon.Default.imagePath = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/'
export const DraggableMarker = () => {
const position = [51.505, -0.09];
const zoom = 13;
const [markerPosition, setMarkerPosition] = useState(L.latLng(position))
const markerRef = useRef(null)
const eventHandlers = {
dragstart: () => {
const marker = markerRef.current;
marker.setOpacity(0.6);
},
dragend: () => {
const marker = markerRef.current;
marker.setOpacity(1);
setMarkerPosition(marker.getLatLng());
}
}
return (
<MapContainer center={position} zoom={zoom}>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker
ref={markerRef}
position={markerPosition}
draggable={true}
eventHandlers={eventHandlers}
>
<Popup>
{`${markerPosition.lat}, ${markerPosition.lng}`}
</Popup>
</Marker>
</MapContainer>
)
};
eventHandlers
のオブジェクトでイベント名と関数を渡している
dragstart
とdragend
で指定しているのでマーカーを掴んだ時と離した時に関数が実行されている
const eventHandlers = {
dragstart: () => {
const marker = markerRef.current;
marker.setOpacity(0.6);
},
dragend: () => {
const marker = markerRef.current;
marker.setOpacity(1);
setMarkerPosition(marker.getLatLng());
}
}
refでL.Markerオブジェクトを取得してL.MarkerのメソッドでOpacityを設定
const marker = markerRef.current;
marker.setOpacity(0.6);
L.MarkerのようなLeafletのオブジェクトに対して使えるオプションやイベントはLeafletのドキュメントから探す
getLatLng, setOpacity
Leaflet
const position = [51.505, -0.09];
const zoom = 13;
let map = L.map('map', {
center: position,
zoom: zoom
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
let marker = L.marker(position, { draggable: true }).addTo(map)
marker.bindPopup(`${marker.getLatLng().lat}, ${marker.getLatLng().lng}`)
marker.on('dragstart', () => {
marker.setOpacity(0.6);
});
marker.on('dragend', () => {
marker.setOpacity(1);
marker.bindPopup(`${marker.getLatLng().lat}, ${marker.getLatLng().lng}`)
});
さいごに
いきなりReactLeafletで実装しようとおもうと色々すっ飛ばしすぎて不明なところが多かったのでExampleを参考にしつつ細かいところはLeafletのドキュメントを参考にするのがよさそうです
Discussion