@mapcomponents/react-maplibre 0.1.14 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/storybook.yml +4 -2
- package/CHANGELOG.md +42 -0
- package/README.md +22 -6
- package/coverage/clover.xml +448 -460
- package/coverage/coverage-final.json +14 -14
- package/coverage/lcov-report/index.html +77 -78
- package/coverage/lcov-report/src/components/MapLibreMap/MapLibreMap.js.html +2 -3
- package/coverage/lcov-report/src/components/MapLibreMap/index.html +2 -3
- package/coverage/lcov-report/src/components/MlCreatePdfButton/MlCreatePdfButton.js.html +2 -3
- package/coverage/lcov-report/src/components/MlCreatePdfButton/index.html +2 -3
- package/coverage/lcov-report/src/components/MlFeatureEditor/MlFeatureEditor.js.html +2 -3
- package/coverage/lcov-report/src/components/MlFeatureEditor/index.html +2 -3
- package/coverage/lcov-report/src/components/MlFillExtrusionLayer/MlFillExtrusionLayer.js.html +2 -3
- package/coverage/lcov-report/src/components/MlFillExtrusionLayer/index.html +2 -3
- package/coverage/lcov-report/src/components/MlFollowGps/MlFollowGps.js.html +112 -107
- package/coverage/lcov-report/src/components/MlFollowGps/index.html +16 -17
- package/coverage/lcov-report/src/components/MlGPXViewer/MlGPXViewer.js.html +2 -3
- package/coverage/lcov-report/src/components/MlGPXViewer/gpxConverter.js.html +2 -3
- package/coverage/lcov-report/src/components/MlGPXViewer/index.html +2 -3
- package/coverage/lcov-report/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js.html +168 -133
- package/coverage/lcov-report/src/components/MlGeoJsonLayer/index.html +20 -21
- package/coverage/lcov-report/src/components/MlImageMarkerLayer/MlImageMarkerLayer.js.html +46 -152
- package/coverage/lcov-report/src/components/MlImageMarkerLayer/index.html +20 -21
- package/coverage/lcov-report/src/components/MlLayer/MlLayer.js.html +92 -30
- package/coverage/lcov-report/src/components/MlLayer/index.html +20 -21
- package/coverage/lcov-report/src/components/MlLayerMagnify/MlLayerMagnify.js.html +2 -3
- package/coverage/lcov-report/src/components/MlLayerMagnify/index.html +2 -3
- package/coverage/lcov-report/src/components/MlLayerSwipe/MlLayerSwipe.js.html +2 -3
- package/coverage/lcov-report/src/components/MlLayerSwipe/index.html +2 -3
- package/coverage/lcov-report/src/components/MlLayerSwitcher/MlLayerSwitcher.js.html +3 -10
- package/coverage/lcov-report/src/components/MlLayerSwitcher/components/LayerBox.js.html +9 -82
- package/coverage/lcov-report/src/components/MlLayerSwitcher/components/index.html +10 -11
- package/coverage/lcov-report/src/components/MlLayerSwitcher/index.html +2 -3
- package/coverage/lcov-report/src/components/MlMarker/MlMarker.js.html +6 -7
- package/coverage/lcov-report/src/components/MlMarker/index.html +6 -7
- package/coverage/lcov-report/src/components/MlNavigationCompass/MlNavigationCompass.js.html +2 -3
- package/coverage/lcov-report/src/components/MlNavigationCompass/index.html +2 -3
- package/coverage/lcov-report/src/components/MlNavigationTools/MlNavigationTools.js.html +11 -15
- package/coverage/lcov-report/src/components/MlNavigationTools/index.html +8 -9
- package/coverage/lcov-report/src/components/MlOsmLayer/MlOsmLayer.js.html +2 -3
- package/coverage/lcov-report/src/components/MlOsmLayer/index.html +2 -3
- package/coverage/lcov-report/src/components/MlScaleReference/MlScaleReference.js.html +2 -3
- package/coverage/lcov-report/src/components/MlScaleReference/index.html +2 -3
- package/coverage/lcov-report/src/components/MlShareMapState/MlShareMapState.js.html +209 -18
- package/coverage/lcov-report/src/components/MlShareMapState/index.html +10 -11
- package/coverage/lcov-report/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.js.html +2 -3
- package/coverage/lcov-report/src/components/MlSpatialElevationProfile/index.html +2 -3
- package/coverage/lcov-report/src/components/MlThreeJsLayer/MlThreeJsLayer.js.html +2 -3
- package/coverage/lcov-report/src/components/MlThreeJsLayer/index.html +2 -3
- package/coverage/lcov-report/src/components/MlUseMapDebugger/MlUseMapDebugger.js.html +6 -25
- package/coverage/lcov-report/src/components/MlUseMapDebugger/index.html +6 -7
- package/coverage/lcov-report/src/components/MlVectorTileLayer/MlVectorTileLayer.js.html +2 -3
- package/coverage/lcov-report/src/components/MlVectorTileLayer/index.html +2 -3
- package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/MlWmsFeatureInfoPopup.js.html +2 -3
- package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/index.html +2 -3
- package/coverage/lcov-report/src/components/MlWmsLayer/MlWmsLayer.js.html +2 -3
- package/coverage/lcov-report/src/components/MlWmsLayer/index.html +2 -3
- package/coverage/lcov-report/src/components/MlWmsLoader/MlWmsLoader.js.html +6 -19
- package/coverage/lcov-report/src/components/MlWmsLoader/index.html +8 -9
- package/coverage/lcov-report/src/hooks/index.html +35 -36
- package/coverage/lcov-report/src/hooks/useMap.js.html +81 -169
- package/coverage/lcov-report/src/hooks/useMapState.js.html +82 -125
- package/coverage/lcov-report/src/hooks/useWms.js.html +9 -22
- package/coverage/lcov-report/src/i18n.js.html +2 -3
- package/coverage/lcov-report/src/index.html +2 -3
- package/coverage/lcov-report/src/translations/english.js.html +2 -3
- package/coverage/lcov-report/src/translations/german.js.html +2 -3
- package/coverage/lcov-report/src/translations/index.html +2 -3
- package/coverage/lcov.info +898 -900
- package/dist/index.esm.js +866 -758
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/MapLibreMap/lib/MapLibreGlWrapper.js +16 -14
- package/src/components/MlComponentTemplate/MlComponentTemplate.js +7 -32
- package/src/components/MlFollowGps/MlFollowGps.js +67 -65
- package/src/components/MlFollowGps/MlFollowGps.test.js +3 -5
- package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js +101 -89
- package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.stories.js +35 -6
- package/src/components/MlGeoJsonLayer/assets/sample_polygon_1.json +33 -0
- package/src/components/MlGeoJsonLayer/util/getDefaultLayerTypeByGeometry.js +25 -0
- package/src/components/MlGeoJsonLayer/util/getDefaultPaintPropsByType.js +31 -0
- package/src/components/MlImageMarkerLayer/MlImageMarkerLayer.js +21 -56
- package/src/components/MlLayer/MlLayer.js +26 -5
- package/src/components/MlLayerSwitcher/MlLayerSwitcher.js +0 -2
- package/src/components/MlLayerSwitcher/MlLayerSwitcher.stories.js +3 -6
- package/src/components/MlLayerSwitcher/components/LayerBox.js +2 -26
- package/src/components/MlMarker/MlMarker.js +1 -1
- package/src/components/MlNavigationTools/MlNavigationTools.js +4 -5
- package/src/components/MlShareMapState/MlShareMapState.js +73 -9
- package/src/components/MlShareMapState/MlShareMapState.stories.js +22 -2
- package/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.stories.js +1 -3
- package/src/components/MlUseMapDebugger/MlUseMapDebugger.js +1 -7
- package/src/components/MlWmsLoader/MlWmsLoader.js +0 -4
- package/src/hooks/useMap.js +33 -62
- package/src/hooks/useMapState.js +3 -17
- package/src/hooks/useWms.js +2 -7
- package/src/index.js +2 -0
- package/src/ui_components/ImageLoader.js +8 -3
- package/src/ui_components/Sidebar.js +1 -1
- package/src/ui_components/TopToolbar.js +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapcomponents/react-maplibre",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@fontsource/roboto": "^4.5.1",
|
|
13
13
|
"@mapbox/mapbox-gl-draw": "^1.2.0",
|
|
14
14
|
"@mapbox/mapbox-gl-sync-move": "^0.3.0",
|
|
15
|
-
"@mapcomponents/react-core": "^0.1.
|
|
15
|
+
"@mapcomponents/react-core": "^0.1.7",
|
|
16
16
|
"@mui/icons-material": "^5.0.1",
|
|
17
17
|
"@mui/material": "5.0.0",
|
|
18
18
|
"@mui/styles": "^5.0.1",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-ignore: TS export Problem to be fixed upstream
|
|
1
2
|
import maplibregl from "maplibre-gl/dist/maplibre-gl";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -68,6 +69,7 @@ const MapLibreGlWrapper = function (props) {
|
|
|
68
69
|
if (!Object.is(item.handler, handler)) {
|
|
69
70
|
return item;
|
|
70
71
|
}
|
|
72
|
+
return false;
|
|
71
73
|
});
|
|
72
74
|
},
|
|
73
75
|
/**
|
|
@@ -91,7 +93,7 @@ const MapLibreGlWrapper = function (props) {
|
|
|
91
93
|
/**
|
|
92
94
|
* Array containing an object for each layer in the MapLibre instance providing information on visibility, loading state, order, paint & layout properties
|
|
93
95
|
*/
|
|
94
|
-
layerState:
|
|
96
|
+
layerState: [],
|
|
95
97
|
/**
|
|
96
98
|
* Maps layerIds to layerState in JSON string form for quick deep comparisons
|
|
97
99
|
*/
|
|
@@ -110,7 +112,7 @@ const MapLibreGlWrapper = function (props) {
|
|
|
110
112
|
//if (self.baseLayers.indexOf(layer.id) === -1) {
|
|
111
113
|
let paint = {};
|
|
112
114
|
let values = layer.paint?._values;
|
|
113
|
-
Object.keys(values || {}).
|
|
115
|
+
Object.keys(values || {}).forEach((propName) => {
|
|
114
116
|
paint[propName] =
|
|
115
117
|
typeof values[propName].value !== "undefined"
|
|
116
118
|
? values[propName].value.value
|
|
@@ -118,7 +120,7 @@ const MapLibreGlWrapper = function (props) {
|
|
|
118
120
|
});
|
|
119
121
|
let layout = {};
|
|
120
122
|
values = layer.layout?._values;
|
|
121
|
-
Object.keys(values || {}).
|
|
123
|
+
Object.keys(values || {}).forEach((propName) => {
|
|
122
124
|
layout[propName] =
|
|
123
125
|
typeof values[propName].value !== "undefined"
|
|
124
126
|
? values[propName].value.value
|
|
@@ -499,17 +501,17 @@ const MapLibreGlWrapper = function (props) {
|
|
|
499
501
|
self.wrapper.refreshViewport();
|
|
500
502
|
self.wrapper.fire("viewportchange");
|
|
501
503
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
504
|
+
self.map.on("move", () => {
|
|
505
|
+
self.wrapper.viewportState = self.wrapper.getViewport();
|
|
506
|
+
self.wrapper.fire("viewportchange");
|
|
507
|
+
});
|
|
508
|
+
self.map.on("data", () => {
|
|
509
|
+
self.wrapper.refreshLayerState();
|
|
510
|
+
self.wrapper.fire("layerchange");
|
|
511
|
+
});
|
|
512
|
+
if (typeof props.onReady === "function") {
|
|
513
|
+
props.onReady(self.map, self);
|
|
514
|
+
}
|
|
513
515
|
};
|
|
514
516
|
initializeMapLibre();
|
|
515
517
|
};
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import React, { useRef, useEffect
|
|
1
|
+
import React, { useRef, useEffect } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
-
|
|
4
|
-
import { MapContext } from "@mapcomponents/react-core";
|
|
5
|
-
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import useMap from "../../hooks/useMap";
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* TODO: Add short & useful description
|
|
@@ -13,40 +11,17 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
13
11
|
* @component
|
|
14
12
|
*/
|
|
15
13
|
const MlComponentTemplate = (props) => {
|
|
16
|
-
|
|
17
|
-
const mapContext = useContext(MapContext);
|
|
18
|
-
|
|
14
|
+
const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
|
|
19
15
|
const initializedRef = useRef(false);
|
|
20
|
-
const mapRef = useRef(undefined);
|
|
21
|
-
const componentId = useRef((props.idPrefix ? props.idPrefix : "MlComponentTemplate-") + uuidv4());
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
let _componentId = componentId.current;
|
|
25
|
-
|
|
26
|
-
return () => {
|
|
27
|
-
// This is the cleanup function, it is called when this react component is removed from react-dom
|
|
28
|
-
// try to remove anything this component has added to the MapLibre-gl instance
|
|
29
|
-
// e.g.: remove the layer
|
|
30
|
-
// mapContext.getMap(props.mapId).removeLayer(layerRef.current);
|
|
31
|
-
// check for the existence of map.style before calling getLayer or getSource
|
|
32
|
-
|
|
33
|
-
if (mapRef.current) {
|
|
34
|
-
mapRef.current.cleanup(_componentId);
|
|
35
|
-
mapRef.current = undefined;
|
|
36
|
-
}
|
|
37
|
-
initializedRef.current = false;
|
|
38
|
-
};
|
|
39
|
-
}, []);
|
|
40
16
|
|
|
41
17
|
useEffect(() => {
|
|
42
|
-
if (!
|
|
43
|
-
// the MapLibre-gl instance (
|
|
18
|
+
if (!mapHook.mapIsReady || initializedRef.current) return;
|
|
19
|
+
// the MapLibre-gl instance (mapHook.map) is accessible here
|
|
44
20
|
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
45
21
|
initializedRef.current = true;
|
|
46
|
-
mapRef.current = mapContext.getMap(props.mapId);
|
|
47
22
|
|
|
48
|
-
|
|
49
|
-
}, [
|
|
23
|
+
mapHook.map.setCenter([7.132122000552613, 50.716405378037706]);
|
|
24
|
+
}, [mapHook.map, mapHook.mapIsReady, props.mapId]);
|
|
50
25
|
|
|
51
26
|
return <></>;
|
|
52
27
|
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import React, {useRef, useEffect,
|
|
1
|
+
import React, { useRef, useEffect, useState, useCallback } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
+
import useMap from "../../hooks/useMap";
|
|
3
4
|
|
|
4
|
-
import {MapContext} from "@mapcomponents/react-core";
|
|
5
|
-
import {v4 as uuidv4} from "uuid";
|
|
6
5
|
import Button from "@mui/material/Button";
|
|
7
6
|
import RoomIcon from "@mui/icons-material/Room";
|
|
8
|
-
import {point} from "@turf/turf"
|
|
7
|
+
import { point, circle } from "@turf/turf";
|
|
9
8
|
import MlGeoJsonLayer from "../MlGeoJsonLayer/MlGeoJsonLayer";
|
|
10
9
|
import MlImageMarkerLayer from "../MlImageMarkerLayer/MlImageMarkerLayer";
|
|
11
10
|
|
|
@@ -21,88 +20,72 @@ import marker from "./assets/marker.png";
|
|
|
21
20
|
* @component
|
|
22
21
|
*/
|
|
23
22
|
const MlFollowGps = (props) => {
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
|
|
24
|
+
|
|
26
25
|
const [isFollowed, setIsFollowed] = useState(false);
|
|
27
26
|
const [geoJson, setGeoJson] = useState(undefined);
|
|
28
27
|
const watchIdRef = useRef(undefined);
|
|
29
28
|
const [locationAccessDenied, setLocationAccessDenied] = useState(false);
|
|
30
29
|
|
|
31
|
-
const
|
|
32
|
-
const mapRef = useRef(undefined);
|
|
33
|
-
const componentId = useRef((props.idPrefix ? props.idPrefix : "MlFollowGps-") + uuidv4());
|
|
34
|
-
const [accuracyRadius, setAccuracyRadius] = useState(30);
|
|
30
|
+
const [accuracyGeoJson, setAccuracyGeoJson] = useState();
|
|
35
31
|
|
|
36
32
|
useEffect(() => {
|
|
37
|
-
let _componentId = componentId.current;
|
|
38
|
-
|
|
39
33
|
return () => {
|
|
40
|
-
// This is the cleanup function, it is called when this react component is removed from react-dom
|
|
41
|
-
// try to remove anything this component has added to the MapLibre-gl instance
|
|
42
|
-
// e.g.: remove the layer
|
|
43
|
-
// mapContext.getMap(props.mapId).removeLayer(layerRef.current);
|
|
44
|
-
// check for the existence of map.style before calling getLayer or getSource
|
|
45
|
-
|
|
46
|
-
if (mapRef.current) {
|
|
47
|
-
mapRef.current.cleanup(_componentId);
|
|
48
|
-
mapRef.current = undefined;
|
|
49
|
-
}
|
|
50
34
|
if (watchIdRef.current) {
|
|
51
|
-
initializedRef.current = false;
|
|
52
35
|
navigator.geolocation.clearWatch(watchIdRef.current);
|
|
53
36
|
watchIdRef.current = undefined;
|
|
54
37
|
}
|
|
55
38
|
};
|
|
56
39
|
}, []);
|
|
57
40
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
mapRef.current.setCenter([pos.coords.longitude, pos.coords.latitude]);
|
|
70
|
-
setAccuracyRadius(pos.coords.accuracy);
|
|
71
|
-
setGeoJson(point([pos.coords.longitude, pos.coords.latitude]));
|
|
72
|
-
};
|
|
41
|
+
const getLocationSuccess = useCallback(
|
|
42
|
+
(pos) => {
|
|
43
|
+
if (!mapHook.map) return;
|
|
44
|
+
|
|
45
|
+
mapHook.map.setCenter([pos.coords.longitude, pos.coords.latitude]);
|
|
46
|
+
const geoJsonPoint = point([pos.coords.longitude, pos.coords.latitude]);
|
|
47
|
+
setGeoJson(geoJsonPoint);
|
|
48
|
+
setAccuracyGeoJson(circle(geoJsonPoint, pos.coords.accuracy / 1000));
|
|
49
|
+
},
|
|
50
|
+
[mapHook.map]
|
|
51
|
+
);
|
|
73
52
|
|
|
74
53
|
const getLocationError = (err) => {
|
|
75
54
|
console.log("Access of user location denied");
|
|
76
55
|
setLocationAccessDenied(true);
|
|
77
56
|
};
|
|
78
57
|
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!mapHook.map) return;
|
|
60
|
+
|
|
61
|
+
if (isFollowed) {
|
|
62
|
+
watchIdRef.current = navigator.geolocation.watchPosition(
|
|
63
|
+
getLocationSuccess,
|
|
64
|
+
getLocationError
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
navigator.geolocation.clearWatch(watchIdRef.current);
|
|
68
|
+
}
|
|
69
|
+
}, [isFollowed, getLocationSuccess]);
|
|
70
|
+
|
|
79
71
|
return (
|
|
80
72
|
<>
|
|
81
73
|
{isFollowed && geoJson && (
|
|
82
74
|
<MlGeoJsonLayer
|
|
83
|
-
geojson={
|
|
84
|
-
type={"
|
|
75
|
+
geojson={accuracyGeoJson}
|
|
76
|
+
type={"fill"}
|
|
85
77
|
paint={{
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
[
|
|
90
|
-
20,
|
|
91
|
-
accuracyRadius /
|
|
92
|
-
0.075 /
|
|
93
|
-
Math.cos((geoJson.geometry.coordinates[1] * Math.PI) / 180),
|
|
94
|
-
],
|
|
95
|
-
],
|
|
96
|
-
base: 2,
|
|
97
|
-
},
|
|
98
|
-
"circle-color": "#ee7700",
|
|
99
|
-
"circle-opacity": 0.5,
|
|
78
|
+
"fill-color": "#ee7700",
|
|
79
|
+
"fill-opacity": 0.5,
|
|
80
|
+
...props.accuracyPaint,
|
|
100
81
|
}}
|
|
82
|
+
insertBeforeLayer={"MlFollowGpsMarker"}
|
|
101
83
|
/>
|
|
102
84
|
)}
|
|
103
85
|
|
|
104
86
|
{isFollowed && geoJson && (
|
|
105
87
|
<MlImageMarkerLayer
|
|
88
|
+
layerId={"MlFollowGpsMarker"}
|
|
106
89
|
options={{
|
|
107
90
|
type: "symbol",
|
|
108
91
|
source: {
|
|
@@ -112,29 +95,22 @@ const MlFollowGps = (props) => {
|
|
|
112
95
|
layout: {
|
|
113
96
|
"icon-size": 0.1,
|
|
114
97
|
"icon-offset": [0, -340],
|
|
98
|
+
...props.markerLayout,
|
|
115
99
|
},
|
|
116
100
|
}}
|
|
117
|
-
imgSrc={marker}
|
|
101
|
+
imgSrc={props.markerImage || marker}
|
|
118
102
|
/>
|
|
119
103
|
)}
|
|
120
104
|
|
|
121
105
|
<Button
|
|
122
|
-
sx={{ zIndex: 1002, color: isFollowed ?
|
|
106
|
+
sx={{ zIndex: 1002, color: isFollowed ? props.onColor : props.offColor, ...props.style }}
|
|
123
107
|
disabled={locationAccessDenied}
|
|
124
108
|
onClick={() => {
|
|
125
|
-
if (isFollowed) {
|
|
126
|
-
navigator.geolocation.clearWatch(watchIdRef.current);
|
|
127
|
-
} else {
|
|
128
|
-
watchIdRef.current = navigator.geolocation.watchPosition(
|
|
129
|
-
getLocationSuccess,
|
|
130
|
-
getLocationError
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
109
|
setIsFollowed(!isFollowed);
|
|
134
110
|
}}
|
|
135
111
|
>
|
|
136
112
|
{" "}
|
|
137
|
-
<RoomIcon sx={{fontSize: props.style.fontSize}}/>{" "}
|
|
113
|
+
<RoomIcon sx={{ fontSize: props.style.fontSize }} />{" "}
|
|
138
114
|
</Button>
|
|
139
115
|
</>
|
|
140
116
|
);
|
|
@@ -156,6 +132,8 @@ MlFollowGps.defaultProps = {
|
|
|
156
132
|
color: "#ececec",
|
|
157
133
|
},
|
|
158
134
|
},
|
|
135
|
+
onColor: "#ececec",
|
|
136
|
+
offColor: "#666",
|
|
159
137
|
};
|
|
160
138
|
|
|
161
139
|
MlFollowGps.propTypes = {
|
|
@@ -167,5 +145,29 @@ MlFollowGps.propTypes = {
|
|
|
167
145
|
* CSS style object that is applied to the button component
|
|
168
146
|
*/
|
|
169
147
|
style: PropTypes.object,
|
|
148
|
+
/**
|
|
149
|
+
* Active button font color
|
|
150
|
+
*/
|
|
151
|
+
onColor: PropTypes.string,
|
|
152
|
+
/**
|
|
153
|
+
* Inactive button font color
|
|
154
|
+
*/
|
|
155
|
+
offColor: PropTypes.string,
|
|
156
|
+
/**
|
|
157
|
+
* Accuracy paint property object, that is passed to the MlGeoJsonLayer responsible for drawing the accuracy circle.
|
|
158
|
+
* Use any available paint prop from layer type "fill".
|
|
159
|
+
* https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/#fill
|
|
160
|
+
*/
|
|
161
|
+
accuracyPaint: PropTypes.object,
|
|
162
|
+
/**
|
|
163
|
+
* Marker layout property object, that is passed to the MlImageMarkerLayer responsible for drawing the position marker.
|
|
164
|
+
* Use any available layout property from layer type "symbol".
|
|
165
|
+
* https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/#symbol
|
|
166
|
+
*/
|
|
167
|
+
markerLayout: PropTypes.object,
|
|
168
|
+
/**
|
|
169
|
+
* Replace the default marker image with a custom one.
|
|
170
|
+
*/
|
|
171
|
+
markerImage: PropTypes.string,
|
|
170
172
|
};
|
|
171
173
|
export default MlFollowGps;
|
|
@@ -6,14 +6,12 @@ import MlFollowGps from "./MlFollowGps";
|
|
|
6
6
|
import MapLibreMap from "./../MapLibreMap/MapLibreMap";
|
|
7
7
|
|
|
8
8
|
const mockGeolocation = {
|
|
9
|
-
watchPosition: jest.fn(),
|
|
9
|
+
watchPosition: jest.fn(() => 1),
|
|
10
10
|
clearWatch: jest.fn(),
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
|
|
13
13
|
global.navigator.geolocation = mockGeolocation;
|
|
14
14
|
|
|
15
|
-
global.navigator.geolocation.watchPosition.mockReturnValue(1);
|
|
16
|
-
|
|
17
15
|
const MlFollowGPSTestComponent = (props) => {
|
|
18
16
|
const [componentVisible, setComponentVisible] = useState(true);
|
|
19
17
|
|
|
@@ -60,6 +58,6 @@ describe("<MlFollowGps>", () => {
|
|
|
60
58
|
wrapper.find("MlFollowGps button").simulate("click");
|
|
61
59
|
//wrapper.find(".toggle_layer_visible").simulate("click");
|
|
62
60
|
|
|
63
|
-
await waitFor(() => expect(mockGeolocation.clearWatch).toHaveBeenCalledTimes(
|
|
61
|
+
await waitFor(() => expect(mockGeolocation.clearWatch).toHaveBeenCalledTimes(2));
|
|
64
62
|
});
|
|
65
63
|
});
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import React, { useRef, useEffect,
|
|
1
|
+
import React, { useRef, useEffect, useCallback } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
|
|
4
|
-
import { v4 as uuidv4 } from "uuid";
|
|
5
4
|
import * as turf from "@turf/turf";
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
import useMap from "../../hooks/useMap";
|
|
7
7
|
|
|
8
8
|
import { _transitionToGeojson } from "./util/transitionFunctions";
|
|
9
|
+
import getDefaultPaintPropsByType from "./util/getDefaultPaintPropsByType";
|
|
10
|
+
import getDefaulLayerTypeByGeometry from "./util/getDefaultLayerTypeByGeometry";
|
|
9
11
|
|
|
10
12
|
const msPerStep = 50;
|
|
13
|
+
const legalLayerTypes = ["circle", "fill", "line"];
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Adds source and layer of types "line", "fill" or "circle" to display GeoJSON data on the map.
|
|
@@ -16,50 +19,46 @@ const msPerStep = 50;
|
|
|
16
19
|
*/
|
|
17
20
|
const MlGeoJsonLayer = (props) => {
|
|
18
21
|
// Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
|
|
19
|
-
const
|
|
20
|
-
const oldGeojsonRef = useRef(null);
|
|
21
|
-
const mapRef = useRef(null);
|
|
22
|
+
const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
|
|
22
23
|
const initializedRef = useRef(false);
|
|
24
|
+
const layerId = useRef(props.layerId || "MlGeoJsonLayer-" + mapHook.componentId);
|
|
25
|
+
const layerTypeRef = useRef(undefined);
|
|
26
|
+
|
|
27
|
+
// transition effect variables
|
|
28
|
+
const oldGeojsonRef = useRef(null);
|
|
23
29
|
const transitionInProgressRef = useRef(false);
|
|
24
30
|
const transitionTimeoutRef = useRef(undefined);
|
|
25
31
|
const currentTransitionStepRef = useRef(false);
|
|
26
32
|
const transitionGeojsonDataRef = useRef([]);
|
|
27
33
|
const transitionGeojsonCommonDataRef = useRef([]);
|
|
28
|
-
const componentId = useRef(
|
|
29
|
-
(props.layerId ? props.layerId : "MlGeoJsonLayer-") + (props.idSuffix || uuidv4())
|
|
30
|
-
);
|
|
31
|
-
const layerId = useRef(props.layerId || componentId.current);
|
|
32
34
|
|
|
33
35
|
useEffect(() => {
|
|
34
|
-
let _componentId = componentId.current;
|
|
35
36
|
return () => {
|
|
36
37
|
// This is the cleanup function, it is called when this react component is removed from react-dom
|
|
37
38
|
if (transitionTimeoutRef.current) {
|
|
38
39
|
clearTimeout(transitionTimeoutRef.current);
|
|
39
40
|
}
|
|
40
|
-
if (mapRef.current) {
|
|
41
|
-
mapRef.current.cleanup(_componentId);
|
|
42
|
-
|
|
43
|
-
mapRef.current = null;
|
|
44
|
-
}
|
|
45
41
|
};
|
|
46
42
|
}, []);
|
|
47
43
|
|
|
48
44
|
useEffect(() => {
|
|
49
|
-
if (!
|
|
45
|
+
if (!mapHook.map || !initializedRef.current) return;
|
|
50
46
|
|
|
51
47
|
for (var key in props.layout) {
|
|
52
|
-
|
|
48
|
+
mapHook.map.setLayoutProperty(layerId.current, key, props.layout[key]);
|
|
53
49
|
}
|
|
54
|
-
}, [props.layout,
|
|
50
|
+
}, [props.layout, mapHook.map, props.mapId]);
|
|
55
51
|
|
|
56
52
|
useEffect(() => {
|
|
57
|
-
if (!
|
|
53
|
+
if (!mapHook.map || !initializedRef.current) return;
|
|
54
|
+
|
|
55
|
+
let _paint =
|
|
56
|
+
props.paint || getDefaultPaintPropsByType(layerTypeRef.current, props.defaultPaintOverrides);
|
|
58
57
|
|
|
59
|
-
for (var key in
|
|
60
|
-
|
|
58
|
+
for (var key in _paint) {
|
|
59
|
+
mapHook.map.setPaintProperty(layerId.current, key, _paint[key]);
|
|
61
60
|
}
|
|
62
|
-
}, [props.paint,
|
|
61
|
+
}, [props.paint, mapHook.map, props.mapId, props.defaultPaintOverrides]);
|
|
63
62
|
|
|
64
63
|
const transitionToGeojson = useCallback(
|
|
65
64
|
(newGeojson) => {
|
|
@@ -72,18 +71,16 @@ const MlGeoJsonLayer = (props) => {
|
|
|
72
71
|
oldGeojsonRef,
|
|
73
72
|
msPerStep,
|
|
74
73
|
currentTransitionStepRef,
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
mapHook.map,
|
|
75
|
+
layerId.current,
|
|
77
76
|
transitionTimeoutRef
|
|
78
77
|
);
|
|
79
78
|
},
|
|
80
|
-
[props]
|
|
79
|
+
[props, mapHook.map]
|
|
81
80
|
);
|
|
82
81
|
|
|
83
82
|
useEffect(() => {
|
|
84
|
-
if (!
|
|
85
|
-
// the MapLibre-gl instance (mapContext.map) is accessible here
|
|
86
|
-
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
83
|
+
if (!mapHook?.map?.getSource(layerId.current) || !initializedRef.current) return;
|
|
87
84
|
|
|
88
85
|
if (
|
|
89
86
|
typeof props.transitionTime !== "undefined" &&
|
|
@@ -96,79 +93,95 @@ const MlGeoJsonLayer = (props) => {
|
|
|
96
93
|
transitionGeojsonCommonDataRef.current = [];
|
|
97
94
|
transitionToGeojson(props.geojson);
|
|
98
95
|
} else {
|
|
99
|
-
|
|
96
|
+
mapHook.map.getSource(layerId.current).setData(props.geojson);
|
|
100
97
|
}
|
|
101
98
|
oldGeojsonRef.current = props.geojson;
|
|
102
99
|
}, [
|
|
103
100
|
props.geojson,
|
|
104
101
|
props.mapId,
|
|
105
|
-
|
|
102
|
+
mapHook.map,
|
|
106
103
|
props.type,
|
|
107
104
|
transitionToGeojson,
|
|
108
105
|
props.transitionTime,
|
|
109
106
|
]);
|
|
110
107
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// the MapLibre-gl instance (mapContext.map) is accessible here
|
|
114
|
-
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
108
|
+
const createLayer = useCallback(() => {
|
|
109
|
+
let geojson = props.geojson;
|
|
115
110
|
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
) {
|
|
126
|
-
var tmpChunks = turf.lineChunk(props.geojson, 0.01);
|
|
127
|
-
geojson = tmpChunks.features[0];
|
|
128
|
-
}
|
|
111
|
+
if (
|
|
112
|
+
props.type === "line" &&
|
|
113
|
+
typeof props.transitionTime !== "undefined" &&
|
|
114
|
+
props.transitionTime &&
|
|
115
|
+
typeof props.geojson.geometry !== "undefined"
|
|
116
|
+
) {
|
|
117
|
+
var tmpChunks = turf.lineChunk(props.geojson, 0.01);
|
|
118
|
+
geojson = tmpChunks.features[0];
|
|
119
|
+
}
|
|
129
120
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
},
|
|
139
|
-
type: props.type || "line",
|
|
140
|
-
paint: props.paint || {
|
|
141
|
-
"line-color": "rgb(100,200,100)",
|
|
142
|
-
"line-width": 10,
|
|
143
|
-
},
|
|
144
|
-
layout: props.layout || {},
|
|
121
|
+
layerTypeRef.current = props.type || getDefaulLayerTypeByGeometry(props.geojson);
|
|
122
|
+
|
|
123
|
+
mapHook.map.addLayer(
|
|
124
|
+
{
|
|
125
|
+
id: layerId.current,
|
|
126
|
+
source: {
|
|
127
|
+
type: "geojson",
|
|
128
|
+
data: geojson,
|
|
145
129
|
},
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
130
|
+
type: layerTypeRef.current,
|
|
131
|
+
paint:
|
|
132
|
+
props.paint ||
|
|
133
|
+
getDefaultPaintPropsByType(layerTypeRef.current, props.defaultPaintOverrides),
|
|
134
|
+
layout: props.layout || {},
|
|
135
|
+
},
|
|
136
|
+
props.insertBeforeLayer,
|
|
137
|
+
mapHook.componentId
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (typeof props.onHover !== "undefined") {
|
|
141
|
+
mapHook.map.on("mousemove", mapHook.componentId, props.onHover, mapHook.componentId);
|
|
142
|
+
}
|
|
149
143
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
if (typeof props.onClick !== "undefined") {
|
|
145
|
+
mapHook.map.on("click", mapHook.componentId, props.onClick, mapHook.componentId);
|
|
146
|
+
}
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
if (typeof props.onLeave !== "undefined") {
|
|
149
|
+
mapHook.map.on("mouseleave", mapHook.componentId, props.onLeave, mapHook.componentId);
|
|
150
|
+
}
|
|
157
151
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
152
|
+
if (
|
|
153
|
+
props.type === "line" &&
|
|
154
|
+
typeof props.transitionTime !== "undefined" &&
|
|
155
|
+
typeof props.geojson.geometry !== "undefined"
|
|
156
|
+
) {
|
|
157
|
+
transitionToGeojson(props.geojson);
|
|
158
|
+
oldGeojsonRef.current = props.geojson;
|
|
159
|
+
}
|
|
160
|
+
}, [mapHook.map, props, transitionToGeojson]);
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!mapHook.mapIsReady || !props.geojson) return;
|
|
164
|
+
|
|
165
|
+
if (
|
|
166
|
+
initializedRef.current &&
|
|
167
|
+
legalLayerTypes.indexOf(props.type) !== -1 &&
|
|
168
|
+
layerTypeRef.current &&
|
|
169
|
+
props.type !== layerTypeRef.current
|
|
170
|
+
) {
|
|
171
|
+
mapHook.map.cleanup(mapHook.componentId);
|
|
172
|
+
} else if (
|
|
173
|
+
initializedRef.current &&
|
|
174
|
+
(legalLayerTypes.indexOf(props.type) === -1 ||
|
|
175
|
+
(legalLayerTypes.indexOf(props.type) !== -1 && props.type === layerTypeRef.current))
|
|
176
|
+
) {
|
|
177
|
+
return;
|
|
170
178
|
}
|
|
171
|
-
|
|
179
|
+
|
|
180
|
+
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
181
|
+
initializedRef.current = true;
|
|
182
|
+
|
|
183
|
+
createLayer();
|
|
184
|
+
}, [mapHook.mapIsReady, createLayer, props]);
|
|
172
185
|
|
|
173
186
|
return <></>;
|
|
174
187
|
};
|
|
@@ -199,6 +212,10 @@ MlGeoJsonLayer.propTypes = {
|
|
|
199
212
|
* https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/#fill
|
|
200
213
|
*/
|
|
201
214
|
paint: PropTypes.object,
|
|
215
|
+
/**
|
|
216
|
+
* Javascript object with optional properties "fill", "line", "circle" to override implicit layer type default paint properties.
|
|
217
|
+
*/
|
|
218
|
+
defaultPaintOverrides: PropTypes.object,
|
|
202
219
|
/**
|
|
203
220
|
* GeoJSON data that is supposed to be rendered by this component.
|
|
204
221
|
*/
|
|
@@ -230,11 +247,6 @@ MlGeoJsonLayer.propTypes = {
|
|
|
230
247
|
* Only works with layer type "line" and LineString GeoJSON data.
|
|
231
248
|
*/
|
|
232
249
|
transitionTime: PropTypes.number,
|
|
233
|
-
/**
|
|
234
|
-
* Id suffix string that is appended to the componentId.
|
|
235
|
-
* Probably removed soon.
|
|
236
|
-
*/
|
|
237
|
-
idSuffix: PropTypes.string,
|
|
238
250
|
};
|
|
239
251
|
|
|
240
252
|
export default MlGeoJsonLayer;
|