64 lines
1.9 KiB
TypeScript
64 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import React, { useCallback, useEffect, useState } from "react";
|
|
import { useAtomValue } from "jotai";
|
|
import { mapAtom } from "../../state/map";
|
|
|
|
const ScaleBar: React.FC = () => {
|
|
const map = useAtomValue(mapAtom);
|
|
const [barWidth, setBarWidth] = useState<number>(0);
|
|
const [label, setLabel] = useState<string>("");
|
|
const [barStyle, setBarStyle] = useState<object>({});
|
|
|
|
useEffect(() => {
|
|
setBarStyle({
|
|
width: barWidth + "px",
|
|
left: 5 + (125 - barWidth) / 2 + "px",
|
|
height: "2px",
|
|
});
|
|
}, [barWidth]);
|
|
|
|
const getRoundNumMeter = (meters: number): number => {
|
|
const roundNumbers = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000];
|
|
for (const num of roundNumbers) {
|
|
if (meters <= num) return num;
|
|
}
|
|
return 1000000;
|
|
};
|
|
|
|
const updateScaleBar = useCallback(() => {
|
|
if (!map) return;
|
|
|
|
const halfHeight = map.getSize().y / 2;
|
|
const maxPixelWidth = 100;
|
|
const maxMeters = map.containerPointToLatLng([0, halfHeight]).distanceTo(map.containerPointToLatLng([maxPixelWidth, halfHeight]));
|
|
const meters = getRoundNumMeter(maxMeters);
|
|
const label = meters < 1000 ? `${meters} m` : `${meters / 1000} km`;
|
|
|
|
setBarWidth((meters / maxMeters) * maxPixelWidth);
|
|
setLabel(label);
|
|
}, [map]);
|
|
|
|
useEffect(() => {
|
|
if (map) {
|
|
updateScaleBar();
|
|
map.on("zoomend", updateScaleBar);
|
|
map.on("moveend", updateScaleBar);
|
|
|
|
return () => {
|
|
map.off("zoomend", updateScaleBar);
|
|
map.off("moveend", updateScaleBar);
|
|
};
|
|
}
|
|
}, [map, updateScaleBar]);
|
|
|
|
return (
|
|
<div aria-label="ScaleBar" className="w-[250px] text-center">
|
|
<label className="mb-2 block text-xs">{label}</label>
|
|
<div className="mx-auto my-0 block bg-zinc-400 transition-all duration-500 ease-in-out dark:bg-white" style={barStyle} />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ScaleBar;
|