@mapcomponents/react-maplibre 0.1.14 → 0.1.15
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/coverage/clover.xml +325 -297
- package/coverage/coverage-final.json +8 -8
- package/coverage/lcov-report/index.html +63 -63
- package/coverage/lcov-report/src/components/MapLibreMap/MapLibreMap.js.html +1 -1
- package/coverage/lcov-report/src/components/MapLibreMap/index.html +1 -1
- package/coverage/lcov-report/src/components/MlCreatePdfButton/MlCreatePdfButton.js.html +1 -1
- package/coverage/lcov-report/src/components/MlCreatePdfButton/index.html +1 -1
- package/coverage/lcov-report/src/components/MlFeatureEditor/MlFeatureEditor.js.html +1 -1
- package/coverage/lcov-report/src/components/MlFeatureEditor/index.html +1 -1
- package/coverage/lcov-report/src/components/MlFillExtrusionLayer/MlFillExtrusionLayer.js.html +1 -1
- package/coverage/lcov-report/src/components/MlFillExtrusionLayer/index.html +1 -1
- package/coverage/lcov-report/src/components/MlFollowGps/MlFollowGps.js.html +37 -34
- package/coverage/lcov-report/src/components/MlFollowGps/index.html +11 -11
- package/coverage/lcov-report/src/components/MlGPXViewer/MlGPXViewer.js.html +1 -1
- package/coverage/lcov-report/src/components/MlGPXViewer/gpxConverter.js.html +1 -1
- package/coverage/lcov-report/src/components/MlGPXViewer/index.html +1 -1
- package/coverage/lcov-report/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js.html +96 -30
- package/coverage/lcov-report/src/components/MlGeoJsonLayer/index.html +19 -19
- package/coverage/lcov-report/src/components/MlImageMarkerLayer/MlImageMarkerLayer.js.html +45 -150
- package/coverage/lcov-report/src/components/MlImageMarkerLayer/index.html +19 -19
- package/coverage/lcov-report/src/components/MlLayer/MlLayer.js.html +93 -27
- package/coverage/lcov-report/src/components/MlLayer/index.html +19 -19
- package/coverage/lcov-report/src/components/MlLayerMagnify/MlLayerMagnify.js.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerMagnify/index.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerSwipe/MlLayerSwipe.js.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerSwipe/index.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerSwitcher/MlLayerSwitcher.js.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerSwitcher/components/LayerBox.js.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerSwitcher/components/index.html +1 -1
- package/coverage/lcov-report/src/components/MlLayerSwitcher/index.html +1 -1
- package/coverage/lcov-report/src/components/MlMarker/MlMarker.js.html +1 -1
- package/coverage/lcov-report/src/components/MlMarker/index.html +1 -1
- package/coverage/lcov-report/src/components/MlNavigationCompass/MlNavigationCompass.js.html +1 -1
- package/coverage/lcov-report/src/components/MlNavigationCompass/index.html +1 -1
- package/coverage/lcov-report/src/components/MlNavigationTools/MlNavigationTools.js.html +10 -13
- package/coverage/lcov-report/src/components/MlNavigationTools/index.html +7 -7
- package/coverage/lcov-report/src/components/MlOsmLayer/MlOsmLayer.js.html +1 -1
- package/coverage/lcov-report/src/components/MlOsmLayer/index.html +1 -1
- package/coverage/lcov-report/src/components/MlScaleReference/MlScaleReference.js.html +1 -1
- package/coverage/lcov-report/src/components/MlScaleReference/index.html +1 -1
- package/coverage/lcov-report/src/components/MlShareMapState/MlShareMapState.js.html +208 -16
- package/coverage/lcov-report/src/components/MlShareMapState/index.html +9 -9
- package/coverage/lcov-report/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.js.html +1 -1
- package/coverage/lcov-report/src/components/MlSpatialElevationProfile/index.html +1 -1
- package/coverage/lcov-report/src/components/MlThreeJsLayer/MlThreeJsLayer.js.html +1 -1
- package/coverage/lcov-report/src/components/MlThreeJsLayer/index.html +1 -1
- package/coverage/lcov-report/src/components/MlUseMapDebugger/MlUseMapDebugger.js.html +1 -1
- package/coverage/lcov-report/src/components/MlUseMapDebugger/index.html +1 -1
- package/coverage/lcov-report/src/components/MlVectorTileLayer/MlVectorTileLayer.js.html +1 -1
- package/coverage/lcov-report/src/components/MlVectorTileLayer/index.html +1 -1
- package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/MlWmsFeatureInfoPopup.js.html +1 -1
- package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/index.html +1 -1
- package/coverage/lcov-report/src/components/MlWmsLayer/MlWmsLayer.js.html +1 -1
- package/coverage/lcov-report/src/components/MlWmsLayer/index.html +1 -1
- package/coverage/lcov-report/src/components/MlWmsLoader/MlWmsLoader.js.html +1 -1
- package/coverage/lcov-report/src/components/MlWmsLoader/index.html +1 -1
- package/coverage/lcov-report/src/hooks/index.html +32 -32
- package/coverage/lcov-report/src/hooks/useMap.js.html +85 -163
- package/coverage/lcov-report/src/hooks/useMapState.js.html +82 -82
- package/coverage/lcov-report/src/hooks/useWms.js.html +1 -1
- package/coverage/lcov-report/src/i18n.js.html +1 -1
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/translations/english.js.html +1 -1
- package/coverage/lcov-report/src/translations/german.js.html +1 -1
- package/coverage/lcov-report/src/translations/index.html +1 -1
- package/coverage/lcov.info +620 -570
- package/dist/index.esm.js +787 -665
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/MapLibreMap/lib/MapLibreGlWrapper.js +13 -12
- package/src/components/MlFollowGps/MlFollowGps.js +23 -22
- package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js +23 -1
- package/src/components/MlImageMarkerLayer/MlImageMarkerLayer.js +21 -56
- package/src/components/MlLayer/MlLayer.js +26 -4
- package/src/components/MlNavigationTools/MlNavigationTools.js +4 -5
- package/src/components/MlShareMapState/MlShareMapState.js +73 -9
- package/src/components/MlShareMapState/MlShareMapState.stories.js +24 -1
- package/src/hooks/useMap.js +36 -62
- package/src/hooks/useMapState.js +3 -3
- package/src/index.js +2 -0
package/package.json
CHANGED
|
@@ -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
|
/**
|
|
@@ -91,7 +92,7 @@ const MapLibreGlWrapper = function (props) {
|
|
|
91
92
|
/**
|
|
92
93
|
* Array containing an object for each layer in the MapLibre instance providing information on visibility, loading state, order, paint & layout properties
|
|
93
94
|
*/
|
|
94
|
-
layerState:
|
|
95
|
+
layerState: [],
|
|
95
96
|
/**
|
|
96
97
|
* Maps layerIds to layerState in JSON string form for quick deep comparisons
|
|
97
98
|
*/
|
|
@@ -499,17 +500,17 @@ const MapLibreGlWrapper = function (props) {
|
|
|
499
500
|
self.wrapper.refreshViewport();
|
|
500
501
|
self.wrapper.fire("viewportchange");
|
|
501
502
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
503
|
+
self.map.on("move", () => {
|
|
504
|
+
self.wrapper.viewportState = self.wrapper.getViewport();
|
|
505
|
+
self.wrapper.fire("viewportchange");
|
|
506
|
+
});
|
|
507
|
+
self.map.on("data", () => {
|
|
508
|
+
self.wrapper.refreshLayerState();
|
|
509
|
+
self.wrapper.fire("layerchange");
|
|
510
|
+
});
|
|
511
|
+
if (typeof props.onReady === "function") {
|
|
512
|
+
props.onReady(self.map, self);
|
|
513
|
+
}
|
|
513
514
|
};
|
|
514
515
|
initializeMapLibre();
|
|
515
516
|
};
|
|
@@ -5,7 +5,7 @@ import {MapContext} from "@mapcomponents/react-core";
|
|
|
5
5
|
import {v4 as uuidv4} from "uuid";
|
|
6
6
|
import Button from "@mui/material/Button";
|
|
7
7
|
import RoomIcon from "@mui/icons-material/Room";
|
|
8
|
-
import {point} from "@turf/turf"
|
|
8
|
+
import {point, circle} from "@turf/turf"
|
|
9
9
|
import MlGeoJsonLayer from "../MlGeoJsonLayer/MlGeoJsonLayer";
|
|
10
10
|
import MlImageMarkerLayer from "../MlImageMarkerLayer/MlImageMarkerLayer";
|
|
11
11
|
|
|
@@ -31,7 +31,7 @@ const MlFollowGps = (props) => {
|
|
|
31
31
|
const initializedRef = useRef(false);
|
|
32
32
|
const mapRef = useRef(undefined);
|
|
33
33
|
const componentId = useRef((props.idPrefix ? props.idPrefix : "MlFollowGps-") + uuidv4());
|
|
34
|
-
const [
|
|
34
|
+
const [accuracyGeoJson, setAccuracyGeoJson] = useState();
|
|
35
35
|
|
|
36
36
|
useEffect(() => {
|
|
37
37
|
let _componentId = componentId.current;
|
|
@@ -67,8 +67,9 @@ const MlFollowGps = (props) => {
|
|
|
67
67
|
const getLocationSuccess = (pos) => {
|
|
68
68
|
if (!mapRef.current) return;
|
|
69
69
|
mapRef.current.setCenter([pos.coords.longitude, pos.coords.latitude]);
|
|
70
|
-
|
|
71
|
-
setGeoJson(
|
|
70
|
+
const geoJsonPoint = point([pos.coords.longitude, pos.coords.latitude])
|
|
71
|
+
setGeoJson(geoJsonPoint);
|
|
72
|
+
setAccuracyGeoJson(circle(geoJsonPoint, pos.coords.accuracy/1000))
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
const getLocationError = (err) => {
|
|
@@ -80,29 +81,19 @@ const MlFollowGps = (props) => {
|
|
|
80
81
|
<>
|
|
81
82
|
{isFollowed && geoJson && (
|
|
82
83
|
<MlGeoJsonLayer
|
|
83
|
-
geojson={
|
|
84
|
-
type={"
|
|
84
|
+
geojson={accuracyGeoJson}
|
|
85
|
+
type={"fill"}
|
|
85
86
|
paint={{
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
[0, 0],
|
|
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,
|
|
87
|
+
"fill-color": "#ee7700",
|
|
88
|
+
"fill-opacity": 0.5,
|
|
100
89
|
}}
|
|
90
|
+
insertBeforeLayer={"MlFollowGpsMarker"}
|
|
101
91
|
/>
|
|
102
92
|
)}
|
|
103
93
|
|
|
104
94
|
{isFollowed && geoJson && (
|
|
105
95
|
<MlImageMarkerLayer
|
|
96
|
+
layerId={"MlFollowGpsMarker"}
|
|
106
97
|
options={{
|
|
107
98
|
type: "symbol",
|
|
108
99
|
source: {
|
|
@@ -119,7 +110,7 @@ const MlFollowGps = (props) => {
|
|
|
119
110
|
)}
|
|
120
111
|
|
|
121
112
|
<Button
|
|
122
|
-
sx={{ zIndex: 1002, color: isFollowed ?
|
|
113
|
+
sx={{ zIndex: 1002, color: isFollowed ? props.onColor : props.offColor, ...props.style }}
|
|
123
114
|
disabled={locationAccessDenied}
|
|
124
115
|
onClick={() => {
|
|
125
116
|
if (isFollowed) {
|
|
@@ -134,7 +125,7 @@ const MlFollowGps = (props) => {
|
|
|
134
125
|
}}
|
|
135
126
|
>
|
|
136
127
|
{" "}
|
|
137
|
-
<RoomIcon sx={{fontSize: props.style.fontSize}}/>{" "}
|
|
128
|
+
<RoomIcon sx={{ fontSize: props.style.fontSize }} />{" "}
|
|
138
129
|
</Button>
|
|
139
130
|
</>
|
|
140
131
|
);
|
|
@@ -156,6 +147,8 @@ MlFollowGps.defaultProps = {
|
|
|
156
147
|
color: "#ececec",
|
|
157
148
|
},
|
|
158
149
|
},
|
|
150
|
+
onColor: "#ececec",
|
|
151
|
+
offColor: "#666"
|
|
159
152
|
};
|
|
160
153
|
|
|
161
154
|
MlFollowGps.propTypes = {
|
|
@@ -167,5 +160,13 @@ MlFollowGps.propTypes = {
|
|
|
167
160
|
* CSS style object that is applied to the button component
|
|
168
161
|
*/
|
|
169
162
|
style: PropTypes.object,
|
|
163
|
+
/**
|
|
164
|
+
* Active button font color
|
|
165
|
+
*/
|
|
166
|
+
onColor: PropTypes.string,
|
|
167
|
+
/**
|
|
168
|
+
* Inactive button font color
|
|
169
|
+
*/
|
|
170
|
+
offColor: PropTypes.string,
|
|
170
171
|
};
|
|
171
172
|
export default MlFollowGps;
|
|
@@ -4,6 +4,7 @@ import PropTypes from "prop-types";
|
|
|
4
4
|
import { v4 as uuidv4 } from "uuid";
|
|
5
5
|
import * as turf from "@turf/turf";
|
|
6
6
|
import { MapContext } from "@mapcomponents/react-core";
|
|
7
|
+
import useMapState from "../../hooks/useMapState";
|
|
7
8
|
|
|
8
9
|
import { _transitionToGeojson } from "./util/transitionFunctions";
|
|
9
10
|
|
|
@@ -17,6 +18,14 @@ const msPerStep = 50;
|
|
|
17
18
|
const MlGeoJsonLayer = (props) => {
|
|
18
19
|
// Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
|
|
19
20
|
const mapContext = useContext(MapContext);
|
|
21
|
+
const mapState = useMapState({
|
|
22
|
+
mapId: props.mapId,
|
|
23
|
+
watch: {
|
|
24
|
+
viewport: false,
|
|
25
|
+
layers: true,
|
|
26
|
+
sources: false,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
20
29
|
const oldGeojsonRef = useRef(null);
|
|
21
30
|
const mapRef = useRef(null);
|
|
22
31
|
const initializedRef = useRef(false);
|
|
@@ -114,6 +123,19 @@ const MlGeoJsonLayer = (props) => {
|
|
|
114
123
|
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
115
124
|
|
|
116
125
|
if (props.geojson) {
|
|
126
|
+
//check if insertBeforeLayer exists
|
|
127
|
+
if (props.insertBeforeLayer) {
|
|
128
|
+
let layerFound = false;
|
|
129
|
+
|
|
130
|
+
mapState?.layers?.forEach((layer) => {
|
|
131
|
+
if (layer.id === props.insertBeforeLayer) {
|
|
132
|
+
layerFound = true;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
if (!layerFound) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
117
139
|
initializedRef.current = true;
|
|
118
140
|
let geojson = props.geojson;
|
|
119
141
|
|
|
@@ -168,7 +190,7 @@ const MlGeoJsonLayer = (props) => {
|
|
|
168
190
|
oldGeojsonRef.current = props.geojson;
|
|
169
191
|
}
|
|
170
192
|
}
|
|
171
|
-
}, [mapContext.mapIds, mapContext, props, transitionToGeojson]);
|
|
193
|
+
}, [mapContext.mapIds, mapContext, props, transitionToGeojson, mapState.layers]);
|
|
172
194
|
|
|
173
195
|
return <></>;
|
|
174
196
|
};
|
|
@@ -1,34 +1,18 @@
|
|
|
1
|
-
import React, { useRef, useCallback, useEffect
|
|
1
|
+
import React, { useRef, useCallback, useEffect } from "react";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { MapContext } from "@mapcomponents/react-core";
|
|
3
|
+
import useMap from "../../hooks/useMap";
|
|
5
4
|
|
|
6
5
|
const MlImageMarkerLayer = (props) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const componentId = useRef((props.idPrefix ? props.idPrefix : "MlImageMarkerLayer-") + uuidv4());
|
|
10
|
-
const mapContext = useContext(MapContext);
|
|
6
|
+
const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
|
|
7
|
+
|
|
11
8
|
const layerInitializedRef = useRef(false);
|
|
12
|
-
const idSuffixRef = useRef(props.idSuffix || new Date().getTime());
|
|
13
9
|
const imageIdRef = useRef(props.imageId || "img_" + new Date().getTime());
|
|
14
|
-
const layerId = useRef(props.layerId || componentId
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
let _componentId = componentId.current;
|
|
18
|
-
return () => {
|
|
19
|
-
// This is the cleanup function, it is called when this react component is removed from react-dom
|
|
20
|
-
if (mapRef.current) {
|
|
21
|
-
mapRef.current.cleanup(_componentId);
|
|
22
|
-
|
|
23
|
-
mapRef.current = null;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
}, []);
|
|
10
|
+
const layerId = useRef(props.layerId || "MlImageMarkerLayer-" + mapHook.componentId);
|
|
27
11
|
|
|
28
12
|
useEffect(() => {
|
|
29
13
|
if (
|
|
30
|
-
!
|
|
31
|
-
(
|
|
14
|
+
!mapHook.mapIsReady ||
|
|
15
|
+
(mapHook.map && !mapHook.map.getLayer(layerId.current)) ||
|
|
32
16
|
!props.options
|
|
33
17
|
)
|
|
34
18
|
return;
|
|
@@ -38,19 +22,15 @@ const MlImageMarkerLayer = (props) => {
|
|
|
38
22
|
|
|
39
23
|
if (props.options.layout) {
|
|
40
24
|
for (key in props.options.layout) {
|
|
41
|
-
|
|
42
|
-
.getMap(props.mapId)
|
|
43
|
-
.setLayoutProperty(layerId.current, key, props.options.layout[key]);
|
|
25
|
+
mapHook.map.setLayoutProperty(layerId.current, key, props.options.layout[key]);
|
|
44
26
|
}
|
|
45
27
|
}
|
|
46
28
|
if (props.options.paint) {
|
|
47
29
|
for (key in props.options.paint) {
|
|
48
|
-
|
|
49
|
-
.getMap(props.mapId)
|
|
50
|
-
.setPaintProperty(layerId.current, key, props.options.paint[key]);
|
|
30
|
+
mapHook.map.setPaintProperty(layerId.current, key, props.options.paint[key]);
|
|
51
31
|
}
|
|
52
32
|
}
|
|
53
|
-
}, [props.options, layerId.current,
|
|
33
|
+
}, [props.options, layerId.current, props.mapId]);
|
|
54
34
|
|
|
55
35
|
const addLayer = useCallback(() => {
|
|
56
36
|
let tmpOptions = {
|
|
@@ -59,50 +39,35 @@ const MlImageMarkerLayer = (props) => {
|
|
|
59
39
|
...props.options,
|
|
60
40
|
};
|
|
61
41
|
tmpOptions.layout["icon-image"] = imageIdRef.current;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
props.insertBeforeLayer,
|
|
65
|
-
componentId.current
|
|
66
|
-
);
|
|
67
|
-
}, [props]);
|
|
42
|
+
mapHook.map.addLayer(tmpOptions, props.insertBeforeLayer, mapHook.componentId);
|
|
43
|
+
}, [props, mapHook.mapIsReady, mapHook.map]);
|
|
68
44
|
|
|
69
45
|
useEffect(() => {
|
|
70
|
-
if (
|
|
71
|
-
!props.options ||
|
|
72
|
-
!mapContext.mapExists(props.mapId) ||
|
|
73
|
-
layerInitializedRef.current
|
|
74
|
-
)
|
|
75
|
-
return;
|
|
76
|
-
|
|
77
|
-
// the MapLibre-gl instance (mapContext.map) is accessible here
|
|
78
|
-
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
79
|
-
mapRef.current = mapContext.getMap(props.mapId);
|
|
46
|
+
if (!props.options || !mapHook.mapIsReady || layerInitializedRef.current) return;
|
|
80
47
|
|
|
81
48
|
layerInitializedRef.current = true;
|
|
82
49
|
|
|
83
|
-
console.log(props.imgSrc);
|
|
84
50
|
if (props.imgSrc) {
|
|
85
|
-
|
|
51
|
+
mapHook.map.loadImage(props.imgSrc, function (error, image) {
|
|
86
52
|
if (error) throw error;
|
|
87
|
-
|
|
53
|
+
mapHook.map.addImage(imageIdRef.current, image, mapHook.componentId);
|
|
88
54
|
});
|
|
89
55
|
}
|
|
56
|
+
|
|
90
57
|
addLayer();
|
|
91
|
-
}, [
|
|
58
|
+
}, [mapHook.mapIsReady, mapHook.map, addLayer, props]);
|
|
92
59
|
|
|
93
60
|
useEffect(() => {
|
|
94
61
|
if (
|
|
95
|
-
!
|
|
96
|
-
(
|
|
62
|
+
!mapHook.mapIsReady ||
|
|
63
|
+
(mapHook.map && !mapHook.map.getLayer(layerId.current)) ||
|
|
97
64
|
!props.options
|
|
98
65
|
) {
|
|
99
66
|
return;
|
|
100
67
|
}
|
|
101
|
-
// the MapLibre-gl instance (mapContext.map) is accessible here
|
|
102
|
-
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
103
68
|
|
|
104
|
-
|
|
105
|
-
}, [props.options.source.data,
|
|
69
|
+
mapHook.map.getSource(layerId.current).setData(props.options.source.data);
|
|
70
|
+
}, [props.options.source.data, props]);
|
|
106
71
|
|
|
107
72
|
return <></>;
|
|
108
73
|
};
|
|
@@ -2,15 +2,23 @@ import React, { useRef, useEffect, useContext } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
4
|
import { MapContext } from "@mapcomponents/react-core";
|
|
5
|
+
import useMapState from "../../hooks/useMapState";
|
|
5
6
|
|
|
6
7
|
const MlLayer = (props) => {
|
|
7
8
|
// Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
|
|
8
9
|
const mapContext = useContext(MapContext);
|
|
10
|
+
|
|
11
|
+
const mapState = useMapState({
|
|
12
|
+
mapId: props.mapId,
|
|
13
|
+
watch: {
|
|
14
|
+
viewport: false,
|
|
15
|
+
layers: true,
|
|
16
|
+
sources: false,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
9
19
|
const layerInitializedRef = useRef(false);
|
|
10
20
|
const mapRef = useRef(null);
|
|
11
|
-
const componentId = useRef(
|
|
12
|
-
(props.layerId ? props.layerId : "MlLayer-") + uuidv4()
|
|
13
|
-
);
|
|
21
|
+
const componentId = useRef((props.layerId ? props.layerId : "MlLayer-") + uuidv4());
|
|
14
22
|
const idSuffixRef = useRef(props.idSuffix || new Date().getTime());
|
|
15
23
|
const layerId = useRef(props.layerId || componentId.current);
|
|
16
24
|
const layerPaintConfRef = useRef(undefined);
|
|
@@ -60,6 +68,20 @@ const MlLayer = (props) => {
|
|
|
60
68
|
// the MapLibre-gl instance (mapContext.map) is accessible here
|
|
61
69
|
// initialize the layer and add it to the MapLibre-gl instance or do something else with it
|
|
62
70
|
|
|
71
|
+
//check if insertBeforeLayer exists
|
|
72
|
+
if (props.insertBeforeLayer) {
|
|
73
|
+
let layerFound = false;
|
|
74
|
+
|
|
75
|
+
mapState?.layers?.forEach((layer) => {
|
|
76
|
+
if (layer.id === props.insertBeforeLayer) {
|
|
77
|
+
layerFound = true;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
if (!layerFound) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
63
85
|
mapRef.current = mapContext.getMap(props.mapId);
|
|
64
86
|
if (mapRef.current) {
|
|
65
87
|
layerInitializedRef.current = true;
|
|
@@ -78,7 +100,7 @@ const MlLayer = (props) => {
|
|
|
78
100
|
layerPaintConfRef.current = JSON.stringify(props.options?.paint);
|
|
79
101
|
layerLayoutConfRef.current = JSON.stringify(props.options?.layout);
|
|
80
102
|
}
|
|
81
|
-
}, [mapContext.mapIds, mapContext, props,
|
|
103
|
+
}, [mapContext.mapIds, mapContext, props, mapState.layers]);
|
|
82
104
|
|
|
83
105
|
return <></>;
|
|
84
106
|
};
|
|
@@ -29,7 +29,6 @@ const MlNavigationTools = (props) => {
|
|
|
29
29
|
minHeight: "20px",
|
|
30
30
|
width: mediaIsMobile ? "50px" : "30px",
|
|
31
31
|
height: mediaIsMobile ? "50px" : "30px",
|
|
32
|
-
color: "#bbb",
|
|
33
32
|
backgroundColor: "#414141",
|
|
34
33
|
borderRadius: "23%",
|
|
35
34
|
//border: "1px solid #bbb",
|
|
@@ -38,8 +37,8 @@ const MlNavigationTools = (props) => {
|
|
|
38
37
|
fontSize: mediaIsMobile ? "1.5em" : "1.2em",
|
|
39
38
|
":hover": {
|
|
40
39
|
backgroundColor: "#515151",
|
|
41
|
-
color: "#ececec",
|
|
42
40
|
},
|
|
41
|
+
color: "#ececec"
|
|
43
42
|
};
|
|
44
43
|
|
|
45
44
|
useEffect(() => {
|
|
@@ -144,7 +143,7 @@ const MlNavigationTools = (props) => {
|
|
|
144
143
|
<Button sx={buttonStyle} onClick={moveToCurrentLocation} disabled={locationAccessDenied}>
|
|
145
144
|
<GpsFixedIcon sx={{fontSize: mediaIsMobile ? "1.5em" : "1.2em"}}/>
|
|
146
145
|
</Button>
|
|
147
|
-
<MlFollowGps style={buttonStyle}/>
|
|
146
|
+
<MlFollowGps style={{...(({color, ...rest}) => rest)(buttonStyle)}} />
|
|
148
147
|
<ButtonGroup
|
|
149
148
|
orientation="vertical"
|
|
150
149
|
sx={{
|
|
@@ -154,10 +153,10 @@ const MlNavigationTools = (props) => {
|
|
|
154
153
|
"Button:hover": {border: "none"},
|
|
155
154
|
}}
|
|
156
155
|
>
|
|
157
|
-
<Button sx={buttonStyle} onClick={zoomIn}>
|
|
156
|
+
<Button sx={{...buttonStyle, color: "#ececec",}} onClick={zoomIn}>
|
|
158
157
|
<ControlPointIcon sx={{fontSize: mediaIsMobile ? "1.5em" : "1.2em"}}/>
|
|
159
158
|
</Button>
|
|
160
|
-
<Button sx={buttonStyle} onClick={zoomOut}>
|
|
159
|
+
<Button sx={{...buttonStyle, color: "#ececec",}} onClick={zoomOut}>
|
|
161
160
|
<RemoveCircleOutlineIcon sx={{fontSize: mediaIsMobile ? "1.5em" : "1.2em"}}/>
|
|
162
161
|
</Button>
|
|
163
162
|
</ButtonGroup>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {useRef, useEffect, useContext, useState} from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import {MapContext} from "@mapcomponents/react-core";
|
|
5
|
+
import {v4 as uuidv4} from "uuid";
|
|
6
|
+
import useMapState from "../../hooks/useMapState";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* TODO: Add short & useful description
|
|
@@ -15,11 +16,21 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
15
16
|
const MlShareMapState = (props) => {
|
|
16
17
|
// Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
|
|
17
18
|
const mapContext = useContext(MapContext);
|
|
18
|
-
|
|
19
19
|
const initializedRef = useRef(false);
|
|
20
20
|
const mapRef = useRef(undefined);
|
|
21
21
|
const [map, setMap] = useState(undefined);
|
|
22
22
|
const componentId = useRef((props.idPrefix ? props.idPrefix : "MlShareMapState-") + uuidv4());
|
|
23
|
+
const [isInitialState, setIsInitialState] = useState(true);
|
|
24
|
+
const mapState = useMapState({
|
|
25
|
+
watch: {
|
|
26
|
+
viewport: false,
|
|
27
|
+
layers: true,
|
|
28
|
+
sources: false
|
|
29
|
+
},
|
|
30
|
+
filter: {
|
|
31
|
+
includeBaseLayers: false
|
|
32
|
+
}
|
|
33
|
+
})
|
|
23
34
|
|
|
24
35
|
const mapStateRef = useRef({});
|
|
25
36
|
|
|
@@ -49,32 +60,59 @@ const MlShareMapState = (props) => {
|
|
|
49
60
|
mapRef.current = mapContext.getMap(props.mapId);
|
|
50
61
|
setMap(mapRef.current);
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
const currentUrlParams = getCurrentUrlParameters();
|
|
53
64
|
if (currentUrlParams.lat && currentUrlParams.lng) {
|
|
54
65
|
mapStateRef.current.lat = currentUrlParams.lat;
|
|
55
66
|
mapStateRef.current.lng = currentUrlParams.lng;
|
|
56
67
|
mapStateRef.current.zoom = currentUrlParams.zoom;
|
|
57
|
-
mapRef.current.setCenter([mapStateRef.current.lng, mapStateRef.current.lat]);
|
|
58
68
|
mapRef.current.setZoom(mapStateRef.current.zoom);
|
|
59
69
|
}
|
|
60
70
|
}, [mapContext.mapIds, mapContext, props.mapId, props.active]);
|
|
61
71
|
|
|
62
72
|
useEffect(() => {
|
|
63
73
|
if (!map) return;
|
|
74
|
+
if(!mapState.layers) return;
|
|
75
|
+
if(!isInitialState) return;
|
|
76
|
+
|
|
77
|
+
const currentUrlParams = getCurrentUrlParameters()
|
|
78
|
+
|
|
79
|
+
if(currentUrlParams.layers) {
|
|
80
|
+
for (let x in currentUrlParams.layers) {
|
|
81
|
+
mapRef.current.getLayer(currentUrlParams.layers[x].id).visibility = currentUrlParams.layers[x].visible ? "visible" : "none"
|
|
82
|
+
mapRef.current.getLayer(currentUrlParams.layers[x].id).type = currentUrlParams.layers[x].type
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}, [mapState.layers, props.mapId, props.active])
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!map) return;
|
|
90
|
+
if (!mapState.layers) return;
|
|
64
91
|
|
|
65
92
|
if (props.active) {
|
|
93
|
+
setIsInitialState(false)
|
|
66
94
|
map.on(
|
|
67
95
|
"moveend",
|
|
68
96
|
() => {
|
|
97
|
+
let mapLayers = []
|
|
98
|
+
for (let x in mapState.layers) {
|
|
99
|
+
mapLayers.push(new URLSearchParams({
|
|
100
|
+
id: mapState.layers[x].id,
|
|
101
|
+
type: mapState.layers[x].type,
|
|
102
|
+
visible: mapState.layers[x].visible
|
|
103
|
+
}))
|
|
104
|
+
}
|
|
69
105
|
refreshMapState();
|
|
70
106
|
let urlParams = new URLSearchParams({
|
|
71
107
|
...getCurrentUrlParameters(),
|
|
72
108
|
...mapStateRef.current,
|
|
109
|
+
layers : mapLayers
|
|
73
110
|
});
|
|
111
|
+
|
|
74
112
|
let currentParams = new URLSearchParams(window.location.search);
|
|
75
113
|
if (urlParams.toString() !== currentParams.toString()) {
|
|
76
114
|
window.history.pushState(
|
|
77
|
-
{
|
|
115
|
+
{...mapStateRef.current},
|
|
78
116
|
document.title,
|
|
79
117
|
"?" + urlParams.toString()
|
|
80
118
|
);
|
|
@@ -88,7 +126,33 @@ const MlShareMapState = (props) => {
|
|
|
88
126
|
}, [props.active, map]);
|
|
89
127
|
|
|
90
128
|
const getCurrentUrlParameters = () => {
|
|
91
|
-
|
|
129
|
+
let parameterObject = Object.fromEntries(new URLSearchParams(window.location.search))
|
|
130
|
+
|
|
131
|
+
if(window.location.search.indexOf("layers")!==-1) {
|
|
132
|
+
let layerParamString = window.location.search.substring(window.location.search.indexOf("layers"))
|
|
133
|
+
layerParamString = layerParamString.substring(0, (layerParamString.indexOf("&")!==-1) ? layerParamString.indexOf("&") : layerParamString.length)
|
|
134
|
+
parameterObject = Object.fromEntries(new URLSearchParams(window.location.search.replace(layerParamString, "")))
|
|
135
|
+
let layerParams = layerParamString.substring(7)
|
|
136
|
+
layerParams = layerParams.replaceAll("%3D", "=")
|
|
137
|
+
layerParams = layerParams.replaceAll("%26", "&")
|
|
138
|
+
layerParams = layerParams.replaceAll("%2C", ",")
|
|
139
|
+
|
|
140
|
+
if (layerParams.indexOf(",")) {
|
|
141
|
+
layerParams = layerParams.split(",")
|
|
142
|
+
} else {
|
|
143
|
+
layerParams = [layerParams]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (let x in layerParams) {
|
|
147
|
+
let layerState = layerParams[x].split("&")
|
|
148
|
+
layerParams[x] = {}
|
|
149
|
+
for (let y in layerState) {
|
|
150
|
+
layerParams[x][layerState[y].split("=")[0]] = layerState[y].split("=")[1]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
parameterObject["layers"] = layerParams
|
|
154
|
+
}
|
|
155
|
+
return parameterObject
|
|
92
156
|
};
|
|
93
157
|
|
|
94
158
|
const refreshMapState = () => {
|
|
@@ -99,7 +163,7 @@ const MlShareMapState = (props) => {
|
|
|
99
163
|
|
|
100
164
|
window.onpopstate = (event) => {
|
|
101
165
|
if (event.state && event.state.lng && event.state.lat && event.state.zoom) {
|
|
102
|
-
mapRef.current.easeTo({
|
|
166
|
+
mapRef.current.easeTo({zoom: event.state.zoom, center: [event.state.lng, event.state.lat]});
|
|
103
167
|
}
|
|
104
168
|
};
|
|
105
169
|
|
|
@@ -2,6 +2,8 @@ import React, { useState} from "react";
|
|
|
2
2
|
|
|
3
3
|
import MlShareMapState from "./MlShareMapState";
|
|
4
4
|
import mapContextDecorator from "../../decorators/MapContextDecorator";
|
|
5
|
+
import useMapState from "../../hooks/useMapState";
|
|
6
|
+
import MlLayer from "../MlLayer/MlLayer";
|
|
5
7
|
|
|
6
8
|
const storyoptions = {
|
|
7
9
|
title: "MapComponents/MlShareMapState",
|
|
@@ -12,7 +14,19 @@ const storyoptions = {
|
|
|
12
14
|
export default storyoptions;
|
|
13
15
|
|
|
14
16
|
const Template = (args) => {
|
|
15
|
-
const [watchState, setWatchState] = useState(
|
|
17
|
+
const [watchState, setWatchState] = useState(false);
|
|
18
|
+
const [testLayerVisible, setTestLayerVisible] = useState(true);
|
|
19
|
+
const mapState = useMapState({
|
|
20
|
+
watch : {
|
|
21
|
+
viewport : false,
|
|
22
|
+
layers : true,
|
|
23
|
+
sources : false
|
|
24
|
+
},
|
|
25
|
+
filter : {
|
|
26
|
+
includeBaseLayers : false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
)
|
|
16
30
|
|
|
17
31
|
return (
|
|
18
32
|
<>
|
|
@@ -22,7 +36,16 @@ const Template = (args) => {
|
|
|
22
36
|
>
|
|
23
37
|
watch map state {watchState?1:0}
|
|
24
38
|
</button>
|
|
39
|
+
<button
|
|
40
|
+
style={{ zIndex: "1000", position: "absolute" }}
|
|
41
|
+
onClick={() => setTestLayerVisible(!testLayerVisible)}
|
|
42
|
+
>
|
|
43
|
+
visibility {testLayerVisible?1:0}
|
|
44
|
+
</button>
|
|
25
45
|
<MlShareMapState active={watchState} />
|
|
46
|
+
<MlLayer layerId={"MlLayer-testLayer"} options={{layout: {visibility: (testLayerVisible ? "visible":"none")}}} />
|
|
47
|
+
<MlLayer layerId={"MlLayer-testLayer2"} options={{layout: {visibility: (testLayerVisible ? "visible":"none")}}} />
|
|
48
|
+
<MlLayer layerId={"MlLayer-testLayer3"} options={{layout: {visibility: (testLayerVisible ? "visible":"none")}}} />
|
|
26
49
|
</>
|
|
27
50
|
);
|
|
28
51
|
};
|