@mapcomponents/react-maplibre 0.1.20 → 0.1.24

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/coverage/clover.xml +195 -198
  3. package/coverage/coverage-final.json +6 -6
  4. package/coverage/lcov-report/index.html +31 -31
  5. package/coverage/lcov-report/src/components/MapLibreMap/MapLibreMap.js.html +1 -1
  6. package/coverage/lcov-report/src/components/MapLibreMap/index.html +1 -1
  7. package/coverage/lcov-report/src/components/MlCreatePdfButton/MlCreatePdfButton.js.html +1 -1
  8. package/coverage/lcov-report/src/components/MlCreatePdfButton/index.html +1 -1
  9. package/coverage/lcov-report/src/components/MlFeatureEditor/MlFeatureEditor.js.html +1 -1
  10. package/coverage/lcov-report/src/components/MlFeatureEditor/index.html +1 -1
  11. package/coverage/lcov-report/src/components/MlFillExtrusionLayer/MlFillExtrusionLayer.js.html +1 -1
  12. package/coverage/lcov-report/src/components/MlFillExtrusionLayer/index.html +1 -1
  13. package/coverage/lcov-report/src/components/MlFollowGps/MlFollowGps.js.html +1 -1
  14. package/coverage/lcov-report/src/components/MlFollowGps/index.html +1 -1
  15. package/coverage/lcov-report/src/components/MlGPXViewer/MlGPXViewer.js.html +1 -1
  16. package/coverage/lcov-report/src/components/MlGPXViewer/gpxConverter.js.html +1 -1
  17. package/coverage/lcov-report/src/components/MlGPXViewer/index.html +1 -1
  18. package/coverage/lcov-report/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js.html +4 -4
  19. package/coverage/lcov-report/src/components/MlGeoJsonLayer/index.html +1 -1
  20. package/coverage/lcov-report/src/components/MlImageMarkerLayer/MlImageMarkerLayer.js.html +1 -1
  21. package/coverage/lcov-report/src/components/MlImageMarkerLayer/index.html +1 -1
  22. package/coverage/lcov-report/src/components/MlLayer/MlLayer.js.html +1 -1
  23. package/coverage/lcov-report/src/components/MlLayer/index.html +1 -1
  24. package/coverage/lcov-report/src/components/MlLayerMagnify/MlLayerMagnify.js.html +1 -1
  25. package/coverage/lcov-report/src/components/MlLayerMagnify/index.html +1 -1
  26. package/coverage/lcov-report/src/components/MlLayerSwipe/MlLayerSwipe.js.html +1 -1
  27. package/coverage/lcov-report/src/components/MlLayerSwipe/index.html +1 -1
  28. package/coverage/lcov-report/src/components/MlLayerSwitcher/MlLayerSwitcher.js.html +1 -1
  29. package/coverage/lcov-report/src/components/MlLayerSwitcher/components/LayerBox.js.html +1 -1
  30. package/coverage/lcov-report/src/components/MlLayerSwitcher/components/index.html +1 -1
  31. package/coverage/lcov-report/src/components/MlLayerSwitcher/index.html +1 -1
  32. package/coverage/lcov-report/src/components/MlMarker/MlMarker.js.html +1 -1
  33. package/coverage/lcov-report/src/components/MlMarker/index.html +1 -1
  34. package/coverage/lcov-report/src/components/MlNavigationCompass/MlNavigationCompass.js.html +1 -1
  35. package/coverage/lcov-report/src/components/MlNavigationCompass/index.html +1 -1
  36. package/coverage/lcov-report/src/components/MlNavigationTools/MlNavigationTools.js.html +1 -1
  37. package/coverage/lcov-report/src/components/MlNavigationTools/index.html +1 -1
  38. package/coverage/lcov-report/src/components/MlOsmLayer/MlOsmLayer.js.html +1 -1
  39. package/coverage/lcov-report/src/components/MlOsmLayer/index.html +1 -1
  40. package/coverage/lcov-report/src/components/MlScaleReference/MlScaleReference.js.html +1 -1
  41. package/coverage/lcov-report/src/components/MlScaleReference/index.html +1 -1
  42. package/coverage/lcov-report/src/components/MlShareMapState/MlShareMapState.js.html +261 -111
  43. package/coverage/lcov-report/src/components/MlShareMapState/index.html +9 -9
  44. package/coverage/lcov-report/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.js.html +1 -1
  45. package/coverage/lcov-report/src/components/MlSpatialElevationProfile/index.html +1 -1
  46. package/coverage/lcov-report/src/components/MlThreeJsLayer/MlThreeJsLayer.js.html +1 -1
  47. package/coverage/lcov-report/src/components/MlThreeJsLayer/index.html +1 -1
  48. package/coverage/lcov-report/src/components/MlUseMapDebugger/MlUseMapDebugger.js.html +1 -1
  49. package/coverage/lcov-report/src/components/MlUseMapDebugger/index.html +1 -1
  50. package/coverage/lcov-report/src/components/MlVectorTileLayer/MlVectorTileLayer.js.html +94 -124
  51. package/coverage/lcov-report/src/components/MlVectorTileLayer/index.html +17 -17
  52. package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/MlWmsFeatureInfoPopup.js.html +1 -1
  53. package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/index.html +1 -1
  54. package/coverage/lcov-report/src/components/MlWmsLayer/MlWmsLayer.js.html +35 -89
  55. package/coverage/lcov-report/src/components/MlWmsLayer/index.html +17 -17
  56. package/coverage/lcov-report/src/components/MlWmsLoader/MlWmsLoader.js.html +1 -1
  57. package/coverage/lcov-report/src/components/MlWmsLoader/index.html +1 -1
  58. package/coverage/lcov-report/src/hooks/index.html +1 -1
  59. package/coverage/lcov-report/src/hooks/useMap.js.html +23 -23
  60. package/coverage/lcov-report/src/hooks/useMapState.js.html +34 -34
  61. package/coverage/lcov-report/src/hooks/useWms.js.html +1 -1
  62. package/coverage/lcov-report/src/i18n.js.html +1 -1
  63. package/coverage/lcov-report/src/index.html +1 -1
  64. package/coverage/lcov-report/src/translations/english.js.html +1 -1
  65. package/coverage/lcov-report/src/translations/german.js.html +1 -1
  66. package/coverage/lcov-report/src/translations/index.html +1 -1
  67. package/coverage/lcov.info +334 -329
  68. package/dist/index.esm.js +86 -106
  69. package/dist/index.esm.js.map +1 -1
  70. package/package.json +1 -1
  71. package/scripts/build-catalogue-markdown-docs.js +5 -3
  72. package/src/components/MapLibreMap/lib/MapLibreGlWrapper.js +26 -21
  73. package/src/components/MapLibreMap/lib/MapLibreGlWrapper.test.js +3 -3
  74. package/src/components/MlCreatePdfButton/MlCreatePdfButton.doc.de.md +2 -2
  75. package/src/components/MlCreatePdfButton/MlCreatePdfButton.doc.en.md +3 -0
  76. package/src/components/MlFeatureEditor/MlFeatureEditor.test.js +2 -2
  77. package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js +3 -3
  78. package/src/components/MlNavigationCompass/MlNavigationCompass.test.js +2 -2
  79. package/src/components/MlShareMapState/MlShareMapState.js +138 -88
  80. package/src/components/MlShareMapState/MlShareMapState.stories.js +79 -29
  81. package/src/components/MlVectorTileLayer/MlVectorTileLayer.js +65 -75
  82. package/src/components/MlVectorTileLayer/MlVectorTileLayer.test.js +6 -6
  83. package/src/components/MlWmsLayer/MlWmsLayer.js +16 -34
  84. package/src/hooks/useMap.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapcomponents/react-maplibre",
3
- "version": "0.1.20",
3
+ "version": "0.1.24",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "module": "dist/index.esm.js",
@@ -7,19 +7,21 @@ let options = {};
7
7
  function getComponentNameFromPath(path) {
8
8
  let tmp = path.split("/");
9
9
  tmp = tmp[tmp.length - 1];
10
- return tmp.replace(".doc.de.md", "");
10
+ tmp = tmp.split(".doc");
11
+ return tmp[0];
11
12
  }
12
13
 
13
14
  const converter = new showdown.Converter();
14
15
 
15
- glob("src/components/**/*.doc.de.md", options, function (er, files) {
16
+ glob("src/components/**/*.doc.*.md", options, function (er, files) {
16
17
  console.log(files);
17
18
 
18
19
  for (var i = 0, len = files.length; i < len; i++) {
20
+ let language = files[i].slice(-5, -3);
19
21
  let rawdata = fs.readFileSync(files[i]);
20
22
  let html = converter.makeHtml(rawdata + "");
21
23
  fs.writeFileSync(
22
- "public/catalogue/" + getComponentNameFromPath(files[i]) + ".de.html",
24
+ "public/catalogue/" + getComponentNameFromPath(files[i]) + "." + language + ".html",
23
25
  html
24
26
  );
25
27
  }
@@ -97,7 +97,7 @@ const MapLibreGlWrapper = function (props) {
97
97
  /**
98
98
  * Maps layerIds to layerState in JSON string form for quick deep comparisons
99
99
  */
100
- layerStateStrings: {},
100
+ layerStateString: "",
101
101
  /**
102
102
  * Previous Version of layerStateString
103
103
  */
@@ -110,29 +110,29 @@ const MapLibreGlWrapper = function (props) {
110
110
  */
111
111
  buildLayerObject: (layer) => {
112
112
  //if (self.baseLayers.indexOf(layer.id) === -1) {
113
- let paint = {};
114
- let values = layer.paint?._values;
115
- Object.keys(values || {}).forEach((propName) => {
116
- paint[propName] =
117
- typeof values[propName].value !== "undefined"
118
- ? values[propName].value.value
119
- : values[propName];
120
- });
121
- let layout = {};
122
- values = layer.layout?._values;
123
- Object.keys(values || {}).forEach((propName) => {
124
- layout[propName] =
125
- typeof values[propName].value !== "undefined"
126
- ? values[propName].value.value
127
- : values[propName];
128
- });
113
+ //let paint = {};
114
+ //let values = layer.paint?._values;
115
+ //Object.keys(values || {}).map((propName) => {
116
+ // paint[propName] =
117
+ // typeof values[propName].value !== "undefined"
118
+ // ? values[propName].value.value
119
+ // : values[propName];
120
+ //});
121
+ //let layout = {};
122
+ //values = layer.layout?._values;
123
+ //Object.keys(values || {}).map((propName) => {
124
+ // layout[propName] =
125
+ // typeof values[propName].value !== "undefined"
126
+ // ? values[propName].value.value
127
+ // : values[propName];
128
+ //});
129
129
  return {
130
130
  id: layer.id,
131
131
  type: layer.type,
132
132
  visible: layer.visibility === "none" ? false : true,
133
133
  baseLayer: self.baseLayers.indexOf(layer.id) !== -1,
134
- paint,
135
- layout,
134
+ //paint,
135
+ //layout,
136
136
  //filter: layers[layerId].filter,
137
137
  //layout: layers[layerId].layout,
138
138
  //maxzoom: layers[layerId].maxzoom,
@@ -161,7 +161,10 @@ const MapLibreGlWrapper = function (props) {
161
161
  */
162
162
  refreshLayerState: () => {
163
163
  self.wrapper.layerState = self.wrapper.buildLayerObjects();
164
- self.wrapper.layerStateStrings = self.wrapper.layerState.map((el) => JSON.stringify(el));
164
+ if (JSON.stringify(self.wrapper.layerState) !== self.wrapper.layerStateString) {
165
+ self.wrapper.fire("layerchange");
166
+ self.wrapper.layerStateString = JSON.stringify(self.wrapper.layerState);
167
+ }
165
168
  },
166
169
  /**
167
170
  * Object containing information on the current viewport state
@@ -505,9 +508,11 @@ const MapLibreGlWrapper = function (props) {
505
508
  self.wrapper.viewportState = self.wrapper.getViewport();
506
509
  self.wrapper.fire("viewportchange");
507
510
  });
511
+ self.map.on("idle", () => {
512
+ self.wrapper.refreshLayerState();
513
+ });
508
514
  self.map.on("data", () => {
509
515
  self.wrapper.refreshLayerState();
510
- self.wrapper.fire("layerchange");
511
516
  });
512
517
  if (typeof props.onReady === "function") {
513
518
  props.onReady(self.map, self);
@@ -297,7 +297,7 @@ describe("MapLibreGlWrapper - event tests", () => {
297
297
  );
298
298
 
299
299
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
300
- expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(3);
300
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(4);
301
301
  });
302
302
 
303
303
  it("should remove an event using off from MapLibreGl using MapLibreGlWrapper.cleanup(componentId)", async () => {
@@ -310,7 +310,7 @@ describe("MapLibreGlWrapper - event tests", () => {
310
310
  );
311
311
 
312
312
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
313
- expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(3);
313
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(4);
314
314
 
315
315
  wrapper.find(".toggle_children_are_visible").simulate("click");
316
316
 
@@ -327,7 +327,7 @@ describe("MapLibreGlWrapper - event tests", () => {
327
327
  );
328
328
 
329
329
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
330
- expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(5);
330
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(6);
331
331
 
332
332
  wrapper.find(".toggle_children_are_visible").simulate("click");
333
333
 
@@ -1,3 +1,3 @@
1
- # Component Beschreibung
1
+ # Beschreibung
2
2
 
3
- Beschreibungstext
3
+ Export auf Knopfdruck ein PDF des aktuellen Kartenausschnitts.
@@ -0,0 +1,3 @@
1
+ # Description
2
+
3
+ Export the current map extent as a PDF document with the click of a single button.
@@ -82,7 +82,7 @@ describe("<MlFeatureEditor>", () => {
82
82
  );
83
83
 
84
84
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
85
- await waitFor(() => expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(4));
85
+ await waitFor(() => expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(5));
86
86
  });
87
87
 
88
88
  it("should deregister 2 event listeners to the maplibre instance", async () => {
@@ -93,7 +93,7 @@ describe("<MlFeatureEditor>", () => {
93
93
  );
94
94
 
95
95
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
96
- expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(4);
96
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(5);
97
97
 
98
98
  wrapper.find(".toggle_layer_visible").simulate("click");
99
99
 
@@ -139,15 +139,15 @@ const MlGeoJsonLayer = (props) => {
139
139
  );
140
140
 
141
141
  if (typeof props.onHover !== "undefined") {
142
- mapHook.map.on("mousemove", mapHook.componentId, props.onHover, mapHook.componentId);
142
+ mapHook.map.on("mousemove", layerId.current, props.onHover, mapHook.componentId);
143
143
  }
144
144
 
145
145
  if (typeof props.onClick !== "undefined") {
146
- mapHook.map.on("click", mapHook.componentId, props.onClick, mapHook.componentId);
146
+ mapHook.map.on("click", layerId.current, props.onClick, mapHook.componentId);
147
147
  }
148
148
 
149
149
  if (typeof props.onLeave !== "undefined") {
150
- mapHook.map.on("mouseleave", mapHook.componentId, props.onLeave, mapHook.componentId);
150
+ mapHook.map.on("mouseleave", layerId.current, props.onLeave, mapHook.componentId);
151
151
  }
152
152
 
153
153
  if (
@@ -48,7 +48,7 @@ describe("<MlNavigationCompass>", () => {
48
48
  );
49
49
 
50
50
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
51
- await waitFor(() => expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(3));
51
+ await waitFor(() => expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(4));
52
52
  });
53
53
 
54
54
  it("should deregister 1 event listener to the maplibre instance", async () => {
@@ -59,7 +59,7 @@ describe("<MlNavigationCompass>", () => {
59
59
  );
60
60
 
61
61
  // MapLibreGlWrapper now subscribes to "data", "move" events on its own
62
- expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(3);
62
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(4);
63
63
 
64
64
  wrapper.find(".toggle_layer_visible").simulate("click");
65
65
 
@@ -1,10 +1,19 @@
1
- import React, {useRef, useEffect, useContext, useState} from "react";
1
+ import React, { useRef, useEffect, useContext, useState, useCallback } from "react";
2
2
  import PropTypes from "prop-types";
3
3
 
4
- import {MapContext} from "@mapcomponents/react-core";
5
- import {v4 as uuidv4} from "uuid";
4
+ import { MapContext } from "@mapcomponents/react-core";
5
+ import { v4 as uuidv4 } from "uuid";
6
6
  import useMapState from "../../hooks/useMapState";
7
7
 
8
+ const getCurrentUrlParameters = () => {
9
+ let currentParams = Object.fromEntries(new URLSearchParams(window.location.search));
10
+ currentParams.layers = JSON.parse(currentParams?.layers ? currentParams.layers : "[]");
11
+
12
+ return currentParams;
13
+ };
14
+
15
+ const initialUrlParams = getCurrentUrlParameters();
16
+
8
17
  /**
9
18
  * TODO: Add short & useful description
10
19
  *
@@ -19,24 +28,73 @@ const MlShareMapState = (props) => {
19
28
  const initializedRef = useRef(false);
20
29
  const mapRef = useRef(undefined);
21
30
  const [map, setMap] = useState(undefined);
31
+ const layersFromUrlParamsRef = useRef({});
22
32
  const componentId = useRef((props.idPrefix ? props.idPrefix : "MlShareMapState-") + uuidv4());
23
- const [isInitialState, setIsInitialState] = useState(true);
24
33
  const mapState = useMapState({
25
34
  watch: {
26
35
  viewport: false,
27
36
  layers: true,
28
- sources: false
37
+ sources: false,
29
38
  },
30
39
  filter: {
31
- includeBaseLayers: false
32
- }
33
- })
40
+ includeBaseLayers: false,
41
+ },
42
+ });
43
+
44
+ const allStatesRestoredRef = useRef(false);
45
+ const layerStatesRestored = useRef(undefined);
46
+ const restoredStatesRef = useRef({
47
+ viewport: {
48
+ center: false,
49
+ bearing: false,
50
+ pitch: false,
51
+ zoom: false,
52
+ },
53
+ layers: {
54
+ ...layersFromUrlParamsRef,
55
+ },
56
+ });
34
57
 
58
+ // initial URL-Params
35
59
  const mapStateRef = useRef({});
36
60
 
61
+ const refreshUrlParameters = useCallback(() => {
62
+ if (!props.active) return;
63
+
64
+ let mapLayers = [];
65
+ for (let x in mapState.layers) {
66
+ mapLayers.push({
67
+ id: mapState.layers[x].id,
68
+ type: mapState.layers[x].type,
69
+ visible: mapState.layers[x].visible,
70
+ });
71
+ }
72
+ refreshMapState();
73
+ let urlParams = new URLSearchParams({
74
+ ...getCurrentUrlParameters(),
75
+ ...mapStateRef.current,
76
+ layers: JSON.stringify(mapLayers),
77
+ });
78
+ JSON.parse(Object.fromEntries(urlParams).layers).forEach((el) => {
79
+ layersFromUrlParamsRef.current[el.id] = false;
80
+ });
81
+
82
+ let currentParams = new URLSearchParams(window.location.search);
83
+ checkRestorationStates(mapState.layers);
84
+ if (urlParams.toString() !== currentParams.toString()) {
85
+ window.history.pushState(
86
+ { ...mapStateRef.current },
87
+ document.title,
88
+ "?" + urlParams.toString()
89
+ );
90
+ }
91
+ }, [mapState.layers, props.active]);
92
+
37
93
  useEffect(() => {
38
94
  let _componentId = componentId.current;
39
95
 
96
+ mapStateRef.current = getCurrentUrlParameters();
97
+
40
98
  return () => {
41
99
  // This is the cleanup function, it is called when this react component is removed from react-dom
42
100
  // try to remove anything this component has added to the MapLibre-gl instance
@@ -52,6 +110,24 @@ const MlShareMapState = (props) => {
52
110
  };
53
111
  }, []);
54
112
 
113
+ useEffect(() => {
114
+ if (!mapRef.current) return;
115
+
116
+ refreshUrlParameters();
117
+ }, [refreshUrlParameters]);
118
+
119
+ useEffect(() => {
120
+ if (!mapRef.current) return;
121
+
122
+ let _refreshUrlParameters = refreshUrlParameters;
123
+
124
+ mapRef.current.on("moveend", _refreshUrlParameters, componentId.current);
125
+
126
+ return () => {
127
+ mapRef.current?.off("moveend", _refreshUrlParameters);
128
+ };
129
+ }, [refreshUrlParameters, map]);
130
+
55
131
  useEffect(() => {
56
132
  if (!mapContext.mapExists(props.mapId) || initializedRef.current) return;
57
133
  // the MapLibre-gl instance (mapContext.getMap(props.mapId)) is accessible here
@@ -59,111 +135,85 @@ const MlShareMapState = (props) => {
59
135
  initializedRef.current = true;
60
136
  mapRef.current = mapContext.getMap(props.mapId);
61
137
  setMap(mapRef.current);
62
-
63
- const currentUrlParams = getCurrentUrlParameters();
64
- if (currentUrlParams.lat && currentUrlParams.lng) {
65
- mapStateRef.current.lat = currentUrlParams.lat;
66
- mapStateRef.current.lng = currentUrlParams.lng;
67
- mapStateRef.current.zoom = currentUrlParams.zoom;
68
- mapRef.current.setZoom(mapStateRef.current.zoom);
138
+ if (mapStateRef.current.lat && mapStateRef.current.lng) {
139
+ restoreViewportState();
69
140
  }
70
141
  }, [mapContext.mapIds, mapContext, props.mapId, props.active]);
71
142
 
72
143
  useEffect(() => {
73
- if (!map) return;
74
- if(!mapState.layers) return;
75
- if(!isInitialState) return;
144
+ if (!mapState?.layers?.length) return;
76
145
 
77
- const currentUrlParams = getCurrentUrlParameters()
146
+ if (typeof layerStatesRestored.current === "undefined") {
147
+ layerStatesRestored.current = {};
148
+ initialUrlParams?.layers.forEach((layer) => {
149
+ layerStatesRestored.current[layer.id] = false;
150
+ });
151
+ }
78
152
 
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
153
+ for (let key in layerStatesRestored.current) {
154
+ let _allDone = true;
155
+ if (layerStatesRestored.current[key] === false) {
156
+ _allDone = false;
157
+ }
158
+ if (_allDone) {
159
+ return;
83
160
  }
84
161
  }
85
162
 
86
- }, [mapState.layers, props.mapId, props.active])
163
+ if (initialUrlParams.layers) {
164
+ initialUrlParams.layers.forEach((layer) => {
165
+ if (mapRef.current?.getLayer(layer.id) && layerStatesRestored.current[layer.id] === false) {
166
+ layerStatesRestored.current[layer.id] = true;
167
+ mapRef.current
168
+ ?.getLayer(layer.id)
169
+ ?.setLayoutProperty("visibility", layer.visible ? "visible" : "none");
170
+ }
171
+ });
172
+ }
173
+ }, [mapState.layers, props.mapId, props.active]);
87
174
 
88
175
  useEffect(() => {
89
176
  if (!map) return;
90
177
  if (!mapState.layers) return;
91
178
 
92
- if (props.active) {
93
- setIsInitialState(false)
94
- map.on(
95
- "moveend",
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
- }
105
- refreshMapState();
106
- let urlParams = new URLSearchParams({
107
- ...getCurrentUrlParameters(),
108
- ...mapStateRef.current,
109
- layers : mapLayers
110
- });
111
-
112
- let currentParams = new URLSearchParams(window.location.search);
113
- if (urlParams.toString() !== currentParams.toString()) {
114
- window.history.pushState(
115
- {...mapStateRef.current},
116
- document.title,
117
- "?" + urlParams.toString()
118
- );
119
- }
120
- },
121
- componentId.current
122
- );
123
- } else {
179
+ if (!props.active) {
124
180
  map.cleanup(componentId.current);
125
181
  }
126
- }, [props.active, map]);
127
-
128
- const getCurrentUrlParameters = () => {
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
156
- };
182
+ }, [props.active, map, mapState.layers]);
157
183
 
158
184
  const refreshMapState = () => {
159
185
  mapStateRef.current.lat = mapRef.current.getCenter().lat;
160
186
  mapStateRef.current.lng = mapRef.current.getCenter().lng;
161
187
  mapStateRef.current.zoom = mapRef.current.getZoom();
188
+ mapStateRef.current.bearing = mapRef.current.getBearing();
189
+ mapStateRef.current.pitch = mapRef.current.getPitch();
190
+ };
191
+
192
+ const checkRestorationStates = (stateArray) => {
193
+ let tempArray = {};
194
+ stateArray.forEach((el, i, arr) => {
195
+ if (!arr[el.key]) tempArray[el.key] = true;
196
+ });
197
+ };
198
+
199
+ const restoreViewportState = () => {
200
+ if (!restoredStatesRef.current.viewport.center) {
201
+ restoredStatesRef.current.viewport.center = true;
202
+ mapRef.current.setCenter([mapStateRef.current.lng, mapStateRef.current.lat]);
203
+ mapRef.current.setZoom(mapStateRef.current.zoom);
204
+ mapRef.current.setBearing(mapStateRef.current.bearing);
205
+ mapRef.current.setPitch(mapStateRef.current.pitch);
206
+ }
207
+
208
+ allStatesRestoredRef.current = true;
162
209
  };
163
210
 
164
211
  window.onpopstate = (event) => {
165
212
  if (event.state && event.state.lng && event.state.lat && event.state.zoom) {
166
- mapRef.current.easeTo({zoom: event.state.zoom, center: [event.state.lng, event.state.lat]});
213
+ mapRef.current.easeTo({
214
+ zoom: event.state.zoom,
215
+ center: [event.state.lng, event.state.lat],
216
+ });
167
217
  }
168
218
  };
169
219
 
@@ -1,8 +1,22 @@
1
- import React, { useState} from "react";
1
+ import React, { useState } from "react";
2
2
 
3
3
  import MlShareMapState from "./MlShareMapState";
4
4
  import mapContextDecorator from "../../decorators/MapContextDecorator";
5
- import MlLayer from "../MlLayer/MlLayer";
5
+ import useMapState from "../../hooks/useMapState";
6
+ import useMap from "../../hooks/useMap";
7
+ import Sidebar from "../../ui_components/Sidebar";
8
+ import ListItem from "@mui/material/ListItem";
9
+ import IconButton from "@mui/material/IconButton";
10
+ import VisibilityIcon from "@mui/icons-material/Visibility";
11
+ import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
12
+ import ListItemText from "@mui/material/ListItemText";
13
+ import sample_geojson_1 from "../MlGeoJsonLayer/assets/sample_1.json";
14
+ import sample_geojson_2 from "../MlGeoJsonLayer/assets/sample_2.json";
15
+ import List from "@mui/material/List";
16
+ import MlGeoJsonLayer from "../MlGeoJsonLayer/MlGeoJsonLayer";
17
+ import { ToggleButton } from "@mui/material";
18
+ import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
19
+ import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
6
20
 
7
21
  const storyoptions = {
8
22
  title: "MapComponents/MlShareMapState",
@@ -12,37 +26,73 @@ const storyoptions = {
12
26
  };
13
27
  export default storyoptions;
14
28
 
15
- const Template = (args) => {
16
- const [watchState, setWatchState] = useState(false);
17
- const [testLayerVisible, setTestLayerVisible] = useState(true);
29
+ const Template = () => {
30
+ const geoJsonArray = [sample_geojson_1, sample_geojson_2];
31
+ const [watchState, setWatchState] = useState(true);
32
+ const mapHook = useMap({ mapId: "map_1" });
33
+ const mapState = useMapState({
34
+ mapId: "map_1",
35
+ watch: {
36
+ viewport: false,
37
+ layers: true,
38
+ sources: false,
39
+ },
40
+ filter: {
41
+ includeBaseLayers: false,
42
+ },
43
+ });
18
44
 
19
45
  return (
20
46
  <>
21
- <button
22
- style={{ zIndex: "1000", position: "absolute" }}
23
- onClick={() => setWatchState(!watchState)}
24
- >
25
- watch map state {watchState ? 1 : 0}
26
- </button>
27
- <button
28
- style={{ zIndex: "1000", position: "absolute" }}
29
- onClick={() => setTestLayerVisible(!testLayerVisible)}
30
- >
31
- visibility {testLayerVisible ? 1 : 0}
32
- </button>
33
47
  <MlShareMapState active={watchState} />
34
- <MlLayer
35
- layerId={"MlLayer-testLayer"}
36
- options={{ layout: { visibility: testLayerVisible ? "visible" : "none" } }}
37
- />
38
- <MlLayer
39
- layerId={"MlLayer-testLayer2"}
40
- options={{ layout: { visibility: testLayerVisible ? "visible" : "none" } }}
41
- />
42
- <MlLayer
43
- layerId={"MlLayer-testLayer3"}
44
- options={{ layout: { visibility: testLayerVisible ? "visible" : "none" } }}
45
- />
48
+ {geoJsonArray.map((el, i) => (
49
+ <MlGeoJsonLayer layerId={"GeoJson_" + i} type="line" geojson={el} key={"GeoJson_" + i} />
50
+ ))}
51
+ <Sidebar sx={{ wordBreak: "break-word" }}>
52
+ <ToggleButton
53
+ size="small"
54
+ selected={watchState}
55
+ value={watchState}
56
+ onChange={() => {
57
+ setWatchState(!watchState);
58
+ }}
59
+ >
60
+ {watchState ? <CheckCircleOutlineIcon /> : <ErrorOutlineIcon />}
61
+
62
+ {watchState ? "active" : "inactive"}
63
+ </ToggleButton>
64
+ <List dense key="layers">
65
+ {mapState.layers?.map((el) => (
66
+ <ListItem
67
+ key={el.id}
68
+ secondaryAction={
69
+ <IconButton
70
+ edge="end"
71
+ aria-label="toggle visibility"
72
+ onClick={() => {
73
+ let currentVisibility = mapHook.map
74
+ ?.getLayer?.(el.id)
75
+ ?.getLayoutProperty("visibility");
76
+ mapHook.map
77
+ ?.getLayer?.(el.id)
78
+ ?.setLayoutProperty(
79
+ "visibility",
80
+ currentVisibility === "none" ? "visible" : "none"
81
+ );
82
+ setTimeout(() => {
83
+ mapHook.map._render();
84
+ }, 100);
85
+ }}
86
+ >
87
+ {el.visible ? <VisibilityIcon /> : <VisibilityOffIcon />}
88
+ </IconButton>
89
+ }
90
+ >
91
+ <ListItemText primary={el.id} secondary={""} />
92
+ </ListItem>
93
+ ))}
94
+ </List>
95
+ </Sidebar>
46
96
  </>
47
97
  );
48
98
  };