@mapcomponents/react-maplibre 0.1.13 → 0.1.17
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 +33 -0
- package/README.md +22 -6
- package/coverage/clover.xml +447 -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 +142 -134
- 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 +891 -896
- package/dist/index.esm.js +941 -717
- 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 +91 -88
- package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.stories.js +20 -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 +22 -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 +3 -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.17",
|
|
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,43 @@ 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;
|
|
58
54
|
|
|
59
55
|
for (var key in props.paint) {
|
|
60
|
-
|
|
56
|
+
mapHook.map.setPaintProperty(layerId.current, key, props.paint[key]);
|
|
61
57
|
}
|
|
62
|
-
}, [props.paint,
|
|
58
|
+
}, [props.paint, mapHook.map, props.mapId]);
|
|
63
59
|
|
|
64
60
|
const transitionToGeojson = useCallback(
|
|
65
61
|
(newGeojson) => {
|
|
@@ -72,18 +68,16 @@ const MlGeoJsonLayer = (props) => {
|
|
|
72
68
|
oldGeojsonRef,
|
|
73
69
|
msPerStep,
|
|
74
70
|
currentTransitionStepRef,
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
mapHook.map,
|
|
72
|
+
layerId.current,
|
|
77
73
|
transitionTimeoutRef
|
|
78
74
|
);
|
|
79
75
|
},
|
|
80
|
-
[props]
|
|
76
|
+
[props, mapHook.map]
|
|
81
77
|
);
|
|
82
78
|
|
|
83
79
|
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
|
|
80
|
+
if (!mapHook?.map?.getSource(layerId.current) || !initializedRef.current) return;
|
|
87
81
|
|
|
88
82
|
if (
|
|
89
83
|
typeof props.transitionTime !== "undefined" &&
|
|
@@ -96,79 +90,93 @@ const MlGeoJsonLayer = (props) => {
|
|
|
96
90
|
transitionGeojsonCommonDataRef.current = [];
|
|
97
91
|
transitionToGeojson(props.geojson);
|
|
98
92
|
} else {
|
|
99
|
-
|
|
93
|
+
mapHook.map.getSource(layerId.current).setData(props.geojson);
|
|
100
94
|
}
|
|
101
95
|
oldGeojsonRef.current = props.geojson;
|
|
102
96
|
}, [
|
|
103
97
|
props.geojson,
|
|
104
98
|
props.mapId,
|
|
105
|
-
|
|
99
|
+
mapHook.map,
|
|
106
100
|
props.type,
|
|
107
101
|
transitionToGeojson,
|
|
108
102
|
props.transitionTime,
|
|
109
103
|
]);
|
|
110
104
|
|
|
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
|
|
105
|
+
const createLayer = useCallback(() => {
|
|
106
|
+
let geojson = props.geojson;
|
|
115
107
|
|
|
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
|
-
}
|
|
108
|
+
if (
|
|
109
|
+
props.type === "line" &&
|
|
110
|
+
typeof props.transitionTime !== "undefined" &&
|
|
111
|
+
props.transitionTime &&
|
|
112
|
+
typeof props.geojson.geometry !== "undefined"
|
|
113
|
+
) {
|
|
114
|
+
var tmpChunks = turf.lineChunk(props.geojson, 0.01);
|
|
115
|
+
geojson = tmpChunks.features[0];
|
|
116
|
+
}
|
|
129
117
|
|
|
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 || {},
|
|
118
|
+
layerTypeRef.current = props.type || getDefaulLayerTypeByGeometry(props.geojson);
|
|
119
|
+
|
|
120
|
+
mapHook.map.addLayer(
|
|
121
|
+
{
|
|
122
|
+
id: layerId.current,
|
|
123
|
+
source: {
|
|
124
|
+
type: "geojson",
|
|
125
|
+
data: geojson,
|
|
145
126
|
},
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
127
|
+
type: layerTypeRef.current,
|
|
128
|
+
paint: props.paint || getDefaultPaintPropsByType(layerTypeRef.current),
|
|
129
|
+
layout: props.layout || {},
|
|
130
|
+
},
|
|
131
|
+
props.insertBeforeLayer,
|
|
132
|
+
mapHook.componentId
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (typeof props.onHover !== "undefined") {
|
|
136
|
+
mapHook.map.on("mousemove", mapHook.componentId, props.onHover, mapHook.componentId);
|
|
137
|
+
}
|
|
149
138
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
139
|
+
if (typeof props.onClick !== "undefined") {
|
|
140
|
+
mapHook.map.on("click", mapHook.componentId, props.onClick, mapHook.componentId);
|
|
141
|
+
}
|
|
153
142
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
143
|
+
if (typeof props.onLeave !== "undefined") {
|
|
144
|
+
mapHook.map.on("mouseleave", mapHook.componentId, props.onLeave, mapHook.componentId);
|
|
145
|
+
}
|
|
157
146
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
147
|
+
if (
|
|
148
|
+
props.type === "line" &&
|
|
149
|
+
typeof props.transitionTime !== "undefined" &&
|
|
150
|
+
typeof props.geojson.geometry !== "undefined"
|
|
151
|
+
) {
|
|
152
|
+
transitionToGeojson(props.geojson);
|
|
153
|
+
oldGeojsonRef.current = props.geojson;
|
|
154
|
+
}
|
|
155
|
+
}, [mapHook.map, props, transitionToGeojson]);
|
|
161
156
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (!mapHook.mapIsReady || !props.geojson) return;
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
initializedRef.current &&
|
|
162
|
+
legalLayerTypes.indexOf(props.type) !== -1 &&
|
|
163
|
+
layerTypeRef.current &&
|
|
164
|
+
props.type !== layerTypeRef.current
|
|
165
|
+
) {
|
|
166
|
+
mapHook.map.cleanup(mapHook.componentId);
|
|
167
|
+
} else if (
|
|
168
|
+
initializedRef.current &&
|
|
169
|
+
(legalLayerTypes.indexOf(props.type) === -1 ||
|
|
170
|
+
(legalLayerTypes.indexOf(props.type) !== -1 && props.type === layerTypeRef.current))
|
|
171
|
+
) {
|
|
172
|
+
return;
|
|
170
173
|
}
|
|
171
|
-
|
|
174
|
+
|
|
175
|
+
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
176
|
+
initializedRef.current = true;
|
|
177
|
+
|
|
178
|
+
createLayer();
|
|
179
|
+
}, [mapHook.mapIsReady, createLayer, props]);
|
|
172
180
|
|
|
173
181
|
return <></>;
|
|
174
182
|
};
|
|
@@ -230,11 +238,6 @@ MlGeoJsonLayer.propTypes = {
|
|
|
230
238
|
* Only works with layer type "line" and LineString GeoJSON data.
|
|
231
239
|
*/
|
|
232
240
|
transitionTime: PropTypes.number,
|
|
233
|
-
/**
|
|
234
|
-
* Id suffix string that is appended to the componentId.
|
|
235
|
-
* Probably removed soon.
|
|
236
|
-
*/
|
|
237
|
-
idSuffix: PropTypes.string,
|
|
238
241
|
};
|
|
239
242
|
|
|
240
243
|
export default MlGeoJsonLayer;
|