はじめに
ユーザーが移動しても地図上に表示されたマーカーが移動しない症状につまずいたため記事にします。
【修正前のコード】
.tsx
import L from 'leaflet'; // Lはleafletグローバルオブジェクト
import markerIcon from '../../node_modules/leaflet/dist/images/marker-icon.png'
import markerShadow from '../../node_modules/leaflet/dist/images/marker-shadow.png'
import { MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet'
import '../../node_modules/leaflet/dist/leaflet.css'; // 追加
import { useAtom, useAtomValue } from 'jotai';
import { latitudeAtom, longitudeAtom, manualLatitudeAtom, manualLongitudeAtom, watchedLatitudeAtom, watchedLongitudeAtom } from './Atom';
import { CenterMapButton } from './CenterMapButton';
import { ToHomeButton } from './ToHomeButton';
import { AutoFlyTo } from './AutoFlyTo';
import { CurrentCoordinate } from './CurrentCoordinate';
import { useGeoWatcher } from './useGeoWatcher';
import { useEffect } from 'react';
const DefaultIcon = L.icon({ // .iconカスタムアイコン作成のクラス
iconUrl: markerIcon, // iconとして表示する画像のURL
shadowUrl: markerShadow, // マーカーに影を表示する場合その影のURL
iconSize: [25, 41], // アイコン画像の幅と高さピクセル単位
iconAnchor: [12, 41], // アイコン画像のどの点が地図上の座標と一致するか ピクセル単位
popupAnchor: [1, -34], // ポップアップウィンドウがアイコン画像からどのくらい離れるか
shadowSize: [41, 41] // 影画像の幅と高さ
});
L.Marker.prototype.options.icon = DefaultIcon;
export const MapPage = () => {
const latitude = useAtomValue(latitudeAtom);
const longitude = useAtomValue(longitudeAtom);
const [ watchedLatitude , setWatchedLatitude ] = useAtom(watchedLatitudeAtom);
const [ watchedLongitude , setWatchedLongitude ] = useAtom(watchedLongitudeAtom);
const manualLatitude = useAtomValue(manualLatitudeAtom);
// 位置管理ロジック
useEffect(() => {
const watchId = navigator.geolocation.watchPosition((position) => {
console.log('watchのpositon値', position);
if (manualLatitude === null) {
setWatchedLatitude(position.coords.latitude);
setWatchedLongitude(position.coords.longitude);
}
},
(error) => console.error('watch位置情報取得エラー', error),
{ enableHighAccuracy: true }
);
return () => navigator.geolocation.clearWatch(watchId);
}, [manualLatitude]);
return (
<>
<MapContainer
center={
latitude && longitude ? [latitude, longitude] : [35.681641, 139.766921]
}
zoom={17}
scrollWheelZoom={true} // scrollでzoom可
zoomSnap={0.5} // zoomの段階調整
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"
/>
<CurrentCoordinate /> {/* 座標表示 */}
<CenterMapButton /> {/* 現在地に移動ボタン */}
<ToHomeButton />
<AutoFlyTo/> {/* 追従 */}
<Marker
key={`${latitude}-${longitude}`}
position={
latitude && longitude ? [latitude, longitude] : [35.681641, 139.766921]
}
>
<Popup>
現在地: <br /> {latitude?.toFixed(6)}, {longitude?.toFixed(6)}
</Popup>
</Marker>
</MapContainer>
</>
)
}
解決方法
watchPositionで取得した座標をマーカーに渡す
.tsx
import L from 'leaflet'; // Lはleafletグローバルオブジェクト
import markerIcon from '../../node_modules/leaflet/dist/images/marker-icon.png'
import markerShadow from '../../node_modules/leaflet/dist/images/marker-shadow.png'
import { MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet'
import '../../node_modules/leaflet/dist/leaflet.css'; // 追加
import { useAtom, useAtomValue } from 'jotai';
import { latitudeAtom, longitudeAtom, manualLatitudeAtom, manualLongitudeAtom, watchedLatitudeAtom, watchedLongitudeAtom } from './Atom';
import { CenterMapButton } from './CenterMapButton';
import { ToHomeButton } from './ToHomeButton';
import { AutoFlyTo } from './AutoFlyTo';
import { CurrentCoordinate } from './CurrentCoordinate';
import { useGeoWatcher } from './useGeoWatcher';
import { useEffect } from 'react';
const DefaultIcon = L.icon({ // .iconカスタムアイコン作成のクラス
iconUrl: markerIcon, // iconとして表示する画像のURL
shadowUrl: markerShadow, // マーカーに影を表示する場合その影のURL
iconSize: [25, 41], // アイコン画像の幅と高さピクセル単位
iconAnchor: [12, 41], // アイコン画像のどの点が地図上の座標と一致するか ピクセル単位
popupAnchor: [1, -34], // ポップアップウィンドウがアイコン画像からどのくらい離れるか
shadowSize: [41, 41] // 影画像の幅と高さ
});
L.Marker.prototype.options.icon = DefaultIcon;
export const MapPage = () => {
const latitude = useAtomValue(latitudeAtom);
const longitude = useAtomValue(longitudeAtom);
const [ watchedLatitude , setWatchedLatitude ] = useAtom(watchedLatitudeAtom);
const [ watchedLongitude , setWatchedLongitude ] = useAtom(watchedLongitudeAtom);
const manualLatitude = useAtomValue(manualLatitudeAtom);
// 位置管理ロジック
useEffect(() => {
const watchId = navigator.geolocation.watchPosition((position) => {
console.log('watchのpositon値', position);
if (manualLatitude === null) {
setWatchedLatitude(position.coords.latitude);
setWatchedLongitude(position.coords.longitude);
}
},
(error) => console.error('watch位置情報取得エラー', error),
{ enableHighAccuracy: true }
);
return () => navigator.geolocation.clearWatch(watchId);
}, [manualLatitude]);
return (
<>
<MapContainer
center={
watchedLatitude && watchedLongitude ? [watchedLatitude, watchedLongitude] : [35.681641, 139.766921]
}
zoom={17}
scrollWheelZoom={true} // scrollでzoom可
zoomSnap={0.5} // zoomの段階調整
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"
/>
<CurrentCoordinate /> {/* 座標表示 */}
<CenterMapButton /> {/* 現在地に移動ボタン */}
<ToHomeButton />
<AutoFlyTo/> {/* 追従 */}
<Marker
key={`${watchedLatitude}-${watchedLongitude}`}
position={
watchedLatitude && watchedLongitude ? [watchedLatitude, watchedLongitude] : [35.681641, 139.766921]
} // ↑ このあたりの記述を修正
>
<Popup>
現在地: <br /> {watchedLatitude?.toFixed(6)}, {watchedLongitude?.toFixed(6)}
</Popup>
</Marker>
</MapContainer>
</>
)
}
参考
おわりに
単純な修正に気付くまでに1日かけてしまいました。