Skip to content

Commit f66fd58

Browse files
wip
1 parent 74cc1e9 commit f66fd58

File tree

3 files changed

+168
-2
lines changed

3 files changed

+168
-2
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
},
6262
"dependencies": {
6363
"@floating-ui/react": "^0.27.9",
64+
"@turf/helpers": "^7.2.0",
65+
"@turf/length": "^7.2.0",
6466
"camelize-object-keys": "^3.0.0",
6567
"clsx": "^2.1.1",
6668
"query-string": "^9.1.2",
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { useCallback, useEffect, useState } from "react"
2+
import { Source, Layer, useMap } from "react-map-gl/maplibre"
3+
import turfLength from "@turf/length"
4+
import {
5+
point as turfPoint,
6+
lineString as turfLineString,
7+
featureCollection as turfFeatureCollection,
8+
} from "@turf/helpers"
9+
10+
import useCssCustomProperties from "../../js/use-css-custom-properties"
11+
import { generateMapLibreId } from "../../js/maplibre"
12+
13+
export default function MaplibreRuler({ onLengthUpdate }) {
14+
const { map } = useMap()
15+
const [geojson, setGeojson] = useState(turfFeatureCollection([]))
16+
17+
const cssCustomProps = useCssCustomProperties([
18+
`--color-black`,
19+
`--color-white`,
20+
])
21+
22+
const mapClick = useCallback(
23+
(e) => {
24+
const features = map.queryRenderedFeatures(e.point, {
25+
layers: [`fu-ruler-points`],
26+
})
27+
28+
const newGeojson = structuredClone(geojson)
29+
30+
// remove the line as it's always the last feature
31+
if (newGeojson.features.length > 1) newGeojson.features.pop()
32+
33+
if (features.length) {
34+
// if a point feature was clicked, remove it
35+
newGeojson.features = newGeojson.features.filter(
36+
(p) => p.id != features[0].id
37+
)
38+
} else {
39+
// add it otherwise
40+
newGeojson.features.push(
41+
turfPoint([e.lngLat.lng, e.lngLat.lat], null, {
42+
id: generateMapLibreId(),
43+
})
44+
)
45+
}
46+
47+
if (newGeojson.features.length > 1) {
48+
const lineString = turfLineString(
49+
newGeojson.features.map((point) => point.geometry.coordinates)
50+
)
51+
newGeojson.features.push(lineString)
52+
53+
if (onLengthUpdate)
54+
onLengthUpdate(turfLength(lineString, { units: `kilometers` }))
55+
} else onLengthUpdate?.(0)
56+
57+
setGeojson(newGeojson)
58+
},
59+
[geojson, map, onLengthUpdate]
60+
)
61+
62+
const mapMouseMove = useCallback(
63+
(e) => {
64+
// TODO: implement projection
65+
const features = map.queryRenderedFeatures(e.point, {
66+
layers: [`fu-ruler-points`],
67+
})
68+
69+
map.getCanvas().style.cursor = features.length ? `pointer` : `crosshair`
70+
},
71+
[map]
72+
)
73+
74+
useEffect(() => {
75+
map.getMap().doubleClickZoom.disable() // fix: const { enableMapInteractions, disableMapInteractions } = useToggleMapInteractions([`doubleClickZoom`])
76+
map.on(`click`, mapClick)
77+
map.on(`mousemove`, mapMouseMove)
78+
79+
return () => {
80+
map.getMap().doubleClickZoom.enable() // fix
81+
map.off(`click`, mapClick)
82+
map.off(`mousemove`, mapMouseMove)
83+
map.getCanvas().style.cursor = null
84+
}
85+
}, [map, mapClick, mapMouseMove])
86+
87+
return (
88+
<>
89+
<Source id="fu-ruler" type="geojson" data={geojson} />
90+
91+
<Layer
92+
source="fu-ruler"
93+
id="fu-ruler-points"
94+
type="circle"
95+
paint={{
96+
"circle-radius": 6,
97+
"circle-color": cssCustomProps[`--color-black`],
98+
}}
99+
filter={[`in`, `$type`, `Point`]}
100+
/>
101+
102+
<Layer
103+
source="fu-ruler"
104+
id="fu-ruler-lines"
105+
type="line"
106+
layout={{
107+
"line-cap": `round`,
108+
"line-join": `round`,
109+
}}
110+
paint={{
111+
"line-width": 3,
112+
"line-color": cssCustomProps[`--color-black`],
113+
}}
114+
filter={[`in`, `$type`, `LineString`]}
115+
/>
116+
</>
117+
)
118+
}

yarn.lock

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,52 @@
375375
"@parcel/watcher-win32-ia32" "2.5.0"
376376
"@parcel/watcher-win32-x64" "2.5.0"
377377

378+
"@turf/distance@^7.2.0":
379+
version "7.2.0"
380+
resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-7.2.0.tgz#bfc0bba9a1c5c8d601b2a8d02feffc9918bd48b5"
381+
integrity sha512-HBjjXIgEcD/wJYjv7/6OZj5yoky2oUvTtVeIAqO3lL80XRvoYmVg6vkOIu6NswkerwLDDNT9kl7+BFLJoHbh6Q==
382+
dependencies:
383+
"@turf/helpers" "^7.2.0"
384+
"@turf/invariant" "^7.2.0"
385+
"@types/geojson" "^7946.0.10"
386+
tslib "^2.8.1"
387+
388+
"@turf/helpers@^7.2.0":
389+
version "7.2.0"
390+
resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-7.2.0.tgz#5771308108c98d608eb8e7f16dcd0eb3fb8a3417"
391+
integrity sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==
392+
dependencies:
393+
"@types/geojson" "^7946.0.10"
394+
tslib "^2.8.1"
395+
396+
"@turf/invariant@^7.2.0":
397+
version "7.2.0"
398+
resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-7.2.0.tgz#ba5b377ea20ee8c45af0a4c9b8bf03aa1c43bd5d"
399+
integrity sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==
400+
dependencies:
401+
"@turf/helpers" "^7.2.0"
402+
"@types/geojson" "^7946.0.10"
403+
tslib "^2.8.1"
404+
405+
"@turf/length@^7.2.0":
406+
version "7.2.0"
407+
resolved "https://registry.yarnpkg.com/@turf/length/-/length-7.2.0.tgz#488e48cbf3c6648ec087466d95512716ccdecfc5"
408+
integrity sha512-LBmYN+iCgVtWNLsckVnpQIJENqIIPO63mogazMp23lrDGfWXu07zZQ9ZinJVO5xYurXNhc/QI2xxoqt2Xw90Ig==
409+
dependencies:
410+
"@turf/distance" "^7.2.0"
411+
"@turf/helpers" "^7.2.0"
412+
"@turf/meta" "^7.2.0"
413+
"@types/geojson" "^7946.0.10"
414+
tslib "^2.8.1"
415+
416+
"@turf/meta@^7.2.0":
417+
version "7.2.0"
418+
resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-7.2.0.tgz#6a6b1918890b4d9d2b5ff10b3ad47e2fd7470912"
419+
integrity sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==
420+
dependencies:
421+
"@turf/helpers" "^7.2.0"
422+
"@types/geojson" "^7946.0.10"
423+
378424
"@types/estree@^1.0.6":
379425
version "1.0.7"
380426
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8"
@@ -387,7 +433,7 @@
387433
dependencies:
388434
"@types/geojson" "*"
389435

390-
"@types/geojson@*", "@types/geojson@^7946.0.16":
436+
"@types/geojson@*", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.16":
391437
version "7946.0.16"
392438
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
393439
integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
@@ -3495,7 +3541,7 @@ ts-easing@^0.2.0:
34953541
resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec"
34963542
integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==
34973543

3498-
tslib@^2.1.0:
3544+
tslib@^2.1.0, tslib@^2.8.1:
34993545
version "2.8.1"
35003546
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
35013547
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==

0 commit comments

Comments
 (0)