React Leaflet関連
Reactで地図を扱いたかったので、メモ。
やりたいこと:
・地図上にマーカを表示する。また、マーカの色は分類別に異なる。
・地図上にマーカを追加もしくは削除する。
とりあえずこれぐらいできれば十分。
公式ドキュメント
導入
npm install react react-dom leaflet
npm install react-leaflet
npm install -D @types/leaflet
手始めに、React LeafletのSetupをコピペして試してみる。
import React from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
export const Home = () => {
return (
<div>
<MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
</div>
);
};
しかし、こんな感じでタイルがバラバラに。
ググると、どうやらheight, widthとcssの読み込みが原因らしい。
Setupのページの下のほうにもよく見ると書いてあった。
そのため、下記のように修正。
import React from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css'; // 追加
export const Home = () => {
return (
<div>
<MapContainer
center={[51.505, -0.09]}
zoom={16}
scrollWheelZoom={false}
style={{ height: '100vh', width: '100vw' }} // 追加
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
</div>
);
};
すると、いい感じに表示された!
さきほど、地図が表示されのはいったん確認したが、マーカー(ピン)がうまく表示されない状態となっているので修正していく。
上記記事を参考に、修正。import React from 'react';
import Leaflet from 'leaflet';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import icon from 'leaflet/dist/images/marker-icon.png'; // 追加
import iconShadow from 'leaflet/dist/images/marker-shadow.png'; // 追加
// 追加
const DefaultIcon = Leaflet.icon({
iconUrl: icon,
shadowUrl: iconShadow,
});
Leaflet.Marker.prototype.options.icon = DefaultIcon;
export const Home = () => {
return (
<div>
<MapContainer
center={[51.505, -0.09]}
zoom={16}
scrollWheelZoom={false}
style={{ height: '100vh', width: '100vw' }}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
</div>
);
};
すると、マーカーが表示された!
マーカーは表示されるようになったが、ポップアップとマーカーの中心がずれているのが気になる。
import React from 'react';
import Leaflet from 'leaflet';
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
const DefaultIcon = Leaflet.icon({
iconUrl: icon,
shadowUrl: iconShadow,
popupAnchor: [12, 0], //追加
});
Leaflet.Marker.prototype.options.icon = DefaultIcon;
export const Home = () => {
return (
<div>
<MapContainer
center={[51.505, -0.09]}
zoom={16}
// scrollWheelZoom={false}
style={{ height: '100vh', width: '100vw' }}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
</div>
);
};
popupAnchorでポップアップの位置を調整してあげるといい感じになった。
ここまでの内容で、すでに地図上にマーカを建てることは十分可能と思われるため、ここからはクリックした場所にマーカを建てていく。
import React, { useState } from 'react';
import Leaflet from 'leaflet';
import {
MapContainer,
TileLayer,
Marker,
Popup,
useMapEvents,
} from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
const DefaultIcon = Leaflet.icon({
iconUrl: icon,
shadowUrl: iconShadow,
popupAnchor: [12, 0],
});
Leaflet.Marker.prototype.options.icon = DefaultIcon;
const AddMaker = () => {
const [position, setPosition] = useState<any>();
const map = useMapEvents({
click(e) {
setPosition(e.latlng);
map.flyTo(e.latlng, map.getZoom());
},
});
return !position ? null : (
<Marker position={position}>
<Popup>Add marker.</Popup>
</Marker>
);
};
export const Home = () => {
return (
<div>
<MapContainer
center={[51.505, -0.09]}
zoom={16}
// scrollWheelZoom={false}
style={{ height: '100vh', width: '100vw' }}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
<AddMaker />
</MapContainer>
</div>
);
};
投げやりになってきたが、この内容で右クリっくした位置にマーカが建てられる。
OpenStreetMapも優秀なのだが、やはり見慣れたGoogle Mapで表示させたい。
TileLayer
のurlを変えるだけ。
url="https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}"
いいかんじ!
右クリしたいちにマーカを建てるようにしたが、マーカ画像の左上がクリックした位置となるため、実際にマーカを建てたくクリックした位置とマーカが建つ位置にずれが生じたように感じる、、
DefaultIconの定義にiconAnchorを追加してあげると、いい感じに調整できた。
また、iconAnchorの値をいじるとpopupAnchorにも影響するので、こちらも調整するといい感じになった。
const DefaultIcon = Leaflet.icon({
iconUrl: icon,
shadowUrl: iconShadow,
iconAnchor: [12, 50],
popupAnchor: [0, -50],
});
※値は使用しているデフォのアイコンのサイズが、25*41だったのでそれをもとにいろいろ試して様子見しながら適当に調整した。
ピンをいくつでも建てることができるように修正。
import React, { useState } from 'react';
import Leaflet, { LatLng } from 'leaflet';
import {
MapContainer,
TileLayer,
Marker,
Popup,
useMapEvents,
} from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
const DefaultIcon = Leaflet.icon({
iconUrl: icon,
shadowUrl: iconShadow,
iconAnchor: [12, 52],
popupAnchor: [0, -52],
});
Leaflet.Marker.prototype.options.icon = DefaultIcon;
const AddMaker = () => {
const [positions, setPositions] = useState<LatLng[]>([]);
useMapEvents({
click(e) {
setPositions((prev) => [...prev, e.latlng]);
},
});
return (
<>
{positions.map((position) => (
<Marker position={position}>
<Popup>Add marker.</Popup>
</Marker>
))}
</>
);
};
export const NotifyPointMaster = () => {
return (
<div>
<MapContainer
center={[51.505, -0.09]}
zoom={16}
style={{ height: '100vh', width: '100vw' }}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
<AddMaker />
</MapContainer>
</div>
);
};