@mapcomponents/react-maplibre 0.1.12 → 0.1.16

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 (133) hide show
  1. package/.github/workflows/storybook.yml +4 -2
  2. package/CHANGELOG.md +33 -0
  3. package/README.md +22 -6
  4. package/coverage/clover.xml +893 -760
  5. package/coverage/coverage-final.json +22 -17
  6. package/coverage/lcov-report/index.html +183 -123
  7. package/coverage/lcov-report/{components → src/components}/MapLibreMap/MapLibreMap.js.html +10 -10
  8. package/coverage/lcov-report/{components → src/components}/MapLibreMap/index.html +10 -10
  9. package/coverage/lcov-report/{components → src/components}/MlCreatePdfButton/MlCreatePdfButton.js.html +10 -10
  10. package/coverage/lcov-report/{components → src/components}/MlCreatePdfButton/index.html +10 -10
  11. package/coverage/lcov-report/{components → src/components}/MlFeatureEditor/MlFeatureEditor.js.html +10 -10
  12. package/coverage/lcov-report/{components → src/components}/MlFeatureEditor/index.html +10 -10
  13. package/coverage/lcov-report/{components → src/components}/MlFillExtrusionLayer/MlFillExtrusionLayer.js.html +10 -10
  14. package/coverage/lcov-report/{components → src/components}/MlFillExtrusionLayer/index.html +10 -10
  15. package/coverage/lcov-report/{components → src/components}/MlFollowGps/MlFollowGps.js.html +148 -136
  16. package/coverage/lcov-report/{components → src/components}/MlFollowGps/index.html +24 -24
  17. package/coverage/lcov-report/{components → src/components}/MlGPXViewer/MlGPXViewer.js.html +66 -60
  18. package/coverage/lcov-report/{components → src/components}/MlGPXViewer/gpxConverter.js.html +49 -70
  19. package/coverage/lcov-report/{components → src/components}/MlGPXViewer/index.html +25 -25
  20. package/coverage/lcov-report/{components → src/components}/MlGeoJsonLayer/MlGeoJsonLayer.js.html +155 -47
  21. package/coverage/lcov-report/{components → src/components}/MlGeoJsonLayer/index.html +28 -28
  22. package/coverage/lcov-report/{components/MlLayer/MlLayer.js.html → src/components/MlImageMarkerLayer/MlImageMarkerLayer.js.html} +88 -121
  23. package/coverage/lcov-report/{components → src/components}/MlImageMarkerLayer/index.html +28 -28
  24. package/coverage/lcov-report/{components/MlImageMarkerLayer/MlImageMarkerLayer.js.html → src/components/MlLayer/MlLayer.js.html} +116 -125
  25. package/coverage/lcov-report/src/components/MlLayer/index.html +117 -0
  26. package/coverage/lcov-report/{components → src/components}/MlLayerMagnify/MlLayerMagnify.js.html +41 -41
  27. package/coverage/lcov-report/{components → src/components}/MlLayerMagnify/index.html +24 -24
  28. package/coverage/lcov-report/{components → src/components}/MlLayerSwipe/MlLayerSwipe.js.html +38 -41
  29. package/coverage/lcov-report/{components → src/components}/MlLayerSwipe/index.html +24 -24
  30. package/coverage/lcov-report/src/components/MlLayerSwitcher/MlLayerSwitcher.js.html +755 -0
  31. package/coverage/lcov-report/src/components/MlLayerSwitcher/components/LayerBox.js.html +380 -0
  32. package/coverage/lcov-report/src/components/MlLayerSwitcher/components/index.html +117 -0
  33. package/coverage/lcov-report/src/components/MlLayerSwitcher/index.html +117 -0
  34. package/coverage/lcov-report/{components → src/components}/MlMarker/MlMarker.js.html +11 -11
  35. package/coverage/lcov-report/{components → src/components}/MlMarker/index.html +10 -10
  36. package/coverage/lcov-report/{components → src/components}/MlNavigationCompass/MlNavigationCompass.js.html +10 -10
  37. package/coverage/lcov-report/{components → src/components}/MlNavigationCompass/index.html +10 -10
  38. package/coverage/lcov-report/{components → src/components}/MlNavigationTools/MlNavigationTools.js.html +50 -41
  39. package/coverage/lcov-report/{components → src/components}/MlNavigationTools/index.html +18 -18
  40. package/coverage/lcov-report/{components → src/components}/MlOsmLayer/MlOsmLayer.js.html +10 -10
  41. package/coverage/lcov-report/{components → src/components}/MlOsmLayer/index.html +10 -10
  42. package/coverage/lcov-report/{components → src/components}/MlScaleReference/MlScaleReference.js.html +10 -10
  43. package/coverage/lcov-report/{components → src/components}/MlScaleReference/index.html +10 -10
  44. package/coverage/lcov-report/{components → src/components}/MlShareMapState/MlShareMapState.js.html +217 -25
  45. package/coverage/lcov-report/{components → src/components}/MlShareMapState/index.html +18 -18
  46. package/coverage/lcov-report/{components → src/components}/MlSpatialElevationProfile/MlSpatialElevationProfile.js.html +10 -10
  47. package/coverage/lcov-report/{components → src/components}/MlSpatialElevationProfile/index.html +10 -10
  48. package/coverage/lcov-report/{components → src/components}/MlThreeJsLayer/MlThreeJsLayer.js.html +30 -54
  49. package/coverage/lcov-report/{components → src/components}/MlThreeJsLayer/index.html +24 -24
  50. package/coverage/lcov-report/{components → src/components}/MlUseMapDebugger/MlUseMapDebugger.js.html +10 -10
  51. package/coverage/lcov-report/{components → src/components}/MlUseMapDebugger/index.html +10 -10
  52. package/coverage/lcov-report/{components → src/components}/MlVectorTileLayer/MlVectorTileLayer.js.html +10 -10
  53. package/coverage/lcov-report/{components → src/components}/MlVectorTileLayer/index.html +10 -10
  54. package/coverage/lcov-report/{components → src/components}/MlWmsFeatureInfoPopup/MlWmsFeatureInfoPopup.js.html +10 -10
  55. package/coverage/lcov-report/{components → src/components}/MlWmsFeatureInfoPopup/index.html +10 -10
  56. package/coverage/lcov-report/{components → src/components}/MlWmsLayer/MlWmsLayer.js.html +13 -13
  57. package/coverage/lcov-report/{components → src/components}/MlWmsLayer/index.html +14 -14
  58. package/coverage/lcov-report/{components → src/components}/MlWmsLoader/MlWmsLoader.js.html +31 -19
  59. package/coverage/lcov-report/{components → src/components}/MlWmsLoader/index.html +16 -16
  60. package/coverage/lcov-report/src/hooks/index.html +147 -0
  61. package/coverage/lcov-report/src/hooks/useMap.js.html +296 -0
  62. package/coverage/lcov-report/{hooks → src/hooks}/useMapState.js.html +91 -91
  63. package/coverage/lcov-report/{hooks → src/hooks}/useWms.js.html +18 -18
  64. package/coverage/lcov-report/src/i18n.js.html +167 -0
  65. package/coverage/lcov-report/src/index.html +117 -0
  66. package/coverage/lcov-report/src/translations/english.js.html +95 -0
  67. package/coverage/lcov-report/src/translations/german.js.html +95 -0
  68. package/coverage/lcov-report/src/translations/index.html +132 -0
  69. package/coverage/lcov.info +1610 -1314
  70. package/dist/b556faa3bc6829d2.png +0 -0
  71. package/dist/index.esm.js +934 -668
  72. package/dist/index.esm.js.map +1 -1
  73. package/package.json +3 -1
  74. package/public/assets/dop.png +0 -0
  75. package/public/assets/historic.png +0 -0
  76. package/public/assets/osm.png +0 -0
  77. package/public/thumbnails/MlFollowGps.png +0 -0
  78. package/public/thumbnails/MlThreeJsLayer.png +0 -0
  79. package/src/components/MapLibreMap/lib/MapLibreGlWrapper.js +53 -67
  80. package/src/components/MlComponentTemplate/MlComponentTemplate.js +6 -31
  81. package/src/components/MlFeatureEditor/MlFeatureEditor.meta.json +2 -2
  82. package/src/components/MlFollowGps/MlFollowGps.js +92 -88
  83. package/src/components/MlFollowGps/MlFollowGps.meta.json +2 -2
  84. package/src/components/MlFollowGps/MlFollowGps.test.js +3 -5
  85. package/src/components/MlFollowGps/assets/marker.png +0 -0
  86. package/src/components/MlGPXViewer/MlGPXViewer.js +45 -43
  87. package/src/components/MlGPXViewer/gpxConverter.js +22 -29
  88. package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js +45 -9
  89. package/src/components/MlImageMarkerLayer/MlImageMarkerLayer.js +21 -57
  90. package/src/components/MlImageMarkerLayer/MlImageMarkerLayer.test.js +6 -7
  91. package/src/components/MlLayer/MlLayer.js +28 -6
  92. package/src/components/MlLayer/MlLayer.test.js +12 -10
  93. package/src/components/MlLayerMagnify/MlLayerMagnify.js +3 -3
  94. package/src/components/MlLayerSwipe/MlLayerSwipe.js +4 -5
  95. package/src/components/MlLayerSwitcher/MlLayerSwitcher.css +17 -0
  96. package/src/components/MlLayerSwitcher/MlLayerSwitcher.doc.de.md +3 -0
  97. package/src/components/MlLayerSwitcher/MlLayerSwitcher.js +223 -0
  98. package/src/components/MlLayerSwitcher/MlLayerSwitcher.meta_.json +15 -0
  99. package/src/components/MlLayerSwitcher/MlLayerSwitcher.stories.js +106 -0
  100. package/src/components/MlLayerSwitcher/assets/sample_1.json +26 -0
  101. package/src/components/MlLayerSwitcher/assets/sample_2.json +22 -0
  102. package/src/components/MlLayerSwitcher/components/LayerBox.js +98 -0
  103. package/src/components/MlMarker/MlMarker.js +1 -1
  104. package/src/components/MlNavigationTools/MlNavigationTools.js +29 -26
  105. package/src/components/MlScaleReference/MlScaleReference.meta.json +1 -1
  106. package/src/components/MlScaleReference/MlScaleReference.stories.js +25 -21
  107. package/src/components/MlShareMapState/MlShareMapState.js +73 -9
  108. package/src/components/MlShareMapState/MlShareMapState.stories.js +24 -1
  109. package/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.stories.js +12 -6
  110. package/src/components/MlThreeJsLayer/MlThreeJsLayer.js +8 -15
  111. package/src/components/MlWmsLayer/MlWmsLayer.js +1 -1
  112. package/src/components/MlWmsLoader/MlWmsLoader.js +8 -4
  113. package/src/components/MlWmsLoader/MlWmsLoader.meta.json +1 -1
  114. package/src/components/MlWmsLoader/MlWmsLoader.stories.js +5 -4
  115. package/src/decorators/EmptyMapContextDecorator.js +11 -6
  116. package/src/decorators/MapContext3DDecorator.js +25 -20
  117. package/src/decorators/MapContextDashboardDecorator.js +7 -2
  118. package/src/decorators/MapContextDecorator.js +7 -3
  119. package/src/decorators/MapContextKlokantechBasicDecorator.js +8 -4
  120. package/src/decorators/MultiMapContextDecorator.js +2 -1
  121. package/src/hooks/useMap.js +33 -62
  122. package/src/hooks/useMapState.js +3 -3
  123. package/src/hooks/useWms.js +7 -6
  124. package/src/i18n.js +28 -0
  125. package/src/index.js +3 -0
  126. package/src/translations/english.js +4 -0
  127. package/src/translations/german.js +4 -0
  128. package/src/ui_components/ImageLoader.js +73 -0
  129. package/src/ui_components/Sidebar.js +75 -20
  130. package/src/ui_components/TopToolbar.js +18 -18
  131. package/coverage/lcov-report/components/MlLayer/index.html +0 -117
  132. package/coverage/lcov-report/hooks/index.html +0 -147
  133. package/coverage/lcov-report/hooks/useMap.js.html +0 -383
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapcomponents/react-maplibre",
3
- "version": "0.1.12",
3
+ "version": "0.1.16",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "module": "dist/index.esm.js",
@@ -9,6 +9,7 @@
9
9
  "@emotion/css": "^11.1.3",
10
10
  "@emotion/react": "^11.4.1",
11
11
  "@emotion/styled": "^11.3.0",
12
+ "@fontsource/roboto": "^4.5.1",
12
13
  "@mapbox/mapbox-gl-draw": "^1.2.0",
13
14
  "@mapbox/mapbox-gl-sync-move": "^0.3.0",
14
15
  "@mapcomponents/react-core": "^0.1.1",
@@ -18,6 +19,7 @@
18
19
  "@turf/turf": "^6.5.0",
19
20
  "jspdf": "^2.3.0",
20
21
  "maplibre-gl": "^1.13.0-rc.5",
22
+ "react-i18next": "^11.14.3",
21
23
  "three": "^0.126.1",
22
24
  "uuid": "^8.3.2",
23
25
  "wms-capabilities": "^0.5.1",
Binary file
Binary file
Binary file
@@ -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
  /**
@@ -41,7 +42,7 @@ const MapLibreGlWrapper = function (props) {
41
42
  on: (eventName, handler, options, componentId) => {
42
43
  if (!self.eventHandlers[eventName]) return;
43
44
 
44
- if(typeof options === 'string'){
45
+ if (typeof options === "string") {
45
46
  componentId = options;
46
47
  options = {};
47
48
  }
@@ -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
  */
@@ -108,38 +109,38 @@ const MapLibreGlWrapper = function (props) {
108
109
  */
109
110
  buildLayerObject: (layer) => {
110
111
  //if (self.baseLayers.indexOf(layer.id) === -1) {
111
- let paint = {};
112
- let values = layer.paint?._values;
113
- Object.keys(values || {}).map((propName) => {
114
- paint[propName] =
115
- typeof values[propName].value !== "undefined"
116
- ? values[propName].value.value
117
- : values[propName];
118
- });
119
- let layout = {};
120
- values = layer.layout?._values;
121
- Object.keys(values || {}).map((propName) => {
122
- layout[propName] =
123
- typeof values[propName].value !== "undefined"
124
- ? values[propName].value.value
125
- : values[propName];
126
- });
127
- return {
128
- id: layer.id,
129
- type: layer.type,
130
- visible: layer.visibility === "none" ? false : true,
131
- baseLayer: self.baseLayers.indexOf(layer.id) !== -1,
132
- paint,
133
- layout,
134
- //filter: layers[layerId].filter,
135
- //layout: layers[layerId].layout,
136
- //maxzoom: layers[layerId].maxzoom,
137
- //metadata: layers[layerId].metadata,
138
- //minzoom: layers[layerId].minzoom,
139
- //paint: layers[layerId].paint.get(),
140
- //source: layers[layerId].source,
141
- //sourceLayer: layers[layerId].sourceLayer,
142
- };
112
+ let paint = {};
113
+ let values = layer.paint?._values;
114
+ Object.keys(values || {}).map((propName) => {
115
+ paint[propName] =
116
+ typeof values[propName].value !== "undefined"
117
+ ? values[propName].value.value
118
+ : values[propName];
119
+ });
120
+ let layout = {};
121
+ values = layer.layout?._values;
122
+ Object.keys(values || {}).map((propName) => {
123
+ layout[propName] =
124
+ typeof values[propName].value !== "undefined"
125
+ ? values[propName].value.value
126
+ : values[propName];
127
+ });
128
+ return {
129
+ id: layer.id,
130
+ type: layer.type,
131
+ visible: layer.visibility === "none" ? false : true,
132
+ baseLayer: self.baseLayers.indexOf(layer.id) !== -1,
133
+ paint,
134
+ layout,
135
+ //filter: layers[layerId].filter,
136
+ //layout: layers[layerId].layout,
137
+ //maxzoom: layers[layerId].maxzoom,
138
+ //metadata: layers[layerId].metadata,
139
+ //minzoom: layers[layerId].minzoom,
140
+ //paint: layers[layerId].paint.get(),
141
+ //source: layers[layerId].source,
142
+ //sourceLayer: layers[layerId].sourceLayer,
143
+ };
143
144
  //}
144
145
  },
145
146
  /**
@@ -159,7 +160,7 @@ const MapLibreGlWrapper = function (props) {
159
160
  */
160
161
  refreshLayerState: () => {
161
162
  self.wrapper.layerState = self.wrapper.buildLayerObjects();
162
- self.wrapper.layerStateStrings = self.wrapper.layerState.map(el => JSON.stringify(el));
163
+ self.wrapper.layerStateStrings = self.wrapper.layerState.map((el) => JSON.stringify(el));
163
164
  },
164
165
  /**
165
166
  * Object containing information on the current viewport state
@@ -173,29 +174,17 @@ const MapLibreGlWrapper = function (props) {
173
174
  * Previous version of viewportStateString
174
175
  */
175
176
  oldViewportStateString: "{}",
176
- getViewport: () => (typeof self.map.getCenter === 'function'?{
177
- center: (({ lng, lat, ...rest }) => ({ lng, lat }))(self.map.getCenter()),
178
- zoom: self.map.getZoom(),
179
- bearing: self.map.getBearing(),
180
- pitch: self.map.getPitch(),
181
- }:{}),
182
- viewportRefreshEnabled: true,
183
- viewportRefreshWaiting: false,
184
- refreshViewport: (force) => {
185
- if (self.wrapper.viewportRefreshEnabled || force) {
186
- self.wrapper.viewportRefreshEnabled = false;
187
- self.wrapper.viewportState = self.wrapper.getViewport();
188
- self.wrapper.viewportStateString = JSON.stringify(self.wrapper.viewportState);
189
- setTimeout(() => {
190
- self.wrapper.viewportRefreshEnabled = true;
191
- if (self.wrapper.viewportRefreshWaiting) {
192
- self.wrapper.viewportRefreshWaiting = false;
193
- self.wrapper.refreshViewport();
177
+ getViewport: () =>
178
+ typeof self.map.getCenter === "function"
179
+ ? {
180
+ center: (({ lng, lat, ...rest }) => ({ lng, lat }))(self.map.getCenter()),
181
+ zoom: self.map.getZoom(),
182
+ bearing: self.map.getBearing(),
183
+ pitch: self.map.getPitch(),
194
184
  }
195
- }, 50);
196
- }else{
197
- self.wrapper.viewportRefreshWaiting = true;
198
- }
185
+ : {},
186
+ refreshViewport: () => {
187
+ self.wrapper.viewportState = self.wrapper.getViewport();
199
188
  },
200
189
  };
201
190
 
@@ -484,10 +473,10 @@ const MapLibreGlWrapper = function (props) {
484
473
  ) {
485
474
  await fetch(props.mapOptions.style)
486
475
  .then((response) => {
487
- if(response.ok){
488
- return response.json()
489
- }else{
490
- throw new Error('error loading map style.json')
476
+ if (response.ok) {
477
+ return response.json();
478
+ } else {
479
+ throw new Error("error loading map style.json");
491
480
  }
492
481
  })
493
482
  .then((styleJson) => {
@@ -508,15 +497,12 @@ const MapLibreGlWrapper = function (props) {
508
497
  self.map = new maplibregl.Map(props.mapOptions);
509
498
 
510
499
  self.addNativeMaplibreFunctionsAndProps();
511
- self.wrapper.refreshViewport(true);
500
+ self.wrapper.refreshViewport();
512
501
  self.wrapper.fire("viewportchange");
513
502
 
514
503
  self.map.on("move", () => {
515
- self.wrapper.refreshViewport();
516
- if (self.wrapper.viewportStateString !== self.wrapper.oldViewportStateString) {
517
- self.wrapper.oldViewportStateString = self.wrapper.viewportStateString;
518
- self.wrapper.fire("viewportchange");
519
- }
504
+ self.wrapper.viewportState = self.wrapper.getViewport();
505
+ self.wrapper.fire("viewportchange");
520
506
  });
521
507
  self.map.on("data", () => {
522
508
  self.wrapper.refreshLayerState();
@@ -1,8 +1,6 @@
1
1
  import React, { useRef, useEffect, useContext } 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
- // Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
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 (!mapContext.mapExists(props.mapId) || initializedRef.current) return;
43
- // the MapLibre-gl instance (mapContext.getMap(props.mapId)) is accessible here
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
- mapRef.current.setCenter([7.132122000552613, 50.716405378037706]);
49
- }, [mapContext.mapIds, mapContext, props.mapId]);
23
+ mapHook.map.setCenter([7.132122000552613, 50.716405378037706]);
24
+ }, [mapHook.map, props.mapId]);
50
25
 
51
26
  return <></>;
52
27
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "MlFeatureEditor",
3
- "title": "Sketch Tool",
3
+ "title": "Sketch tools",
4
4
  "description": "Enables the editing of Point, Linestring and Polygon GeoJSON Files.",
5
5
  "i18n": {
6
6
  "de": {
@@ -8,7 +8,7 @@
8
8
  "description": "Ermöglicht das Bearbeiten von Punkt, Linestring und Polygon GeoJSON Daten."
9
9
  }
10
10
  },
11
- "tags": [ "Map add-on" ],
11
+ "tags": ["Map add-on"],
12
12
  "category": "add-ons",
13
13
  "type": "component",
14
14
  "price": 0
@@ -1,14 +1,15 @@
1
- import React, {useRef, useEffect, useContext, useState} from "react";
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
 
11
+ import marker from "./assets/marker.png";
12
+
12
13
  /**
13
14
  * Adds a button that makes the map follow the users GPS position using
14
15
  * navigator.geolocation.watchPosition if activated
@@ -19,121 +20,97 @@ import MlImageMarkerLayer from "../MlImageMarkerLayer/MlImageMarkerLayer";
19
20
  * @component
20
21
  */
21
22
  const MlFollowGps = (props) => {
22
- // Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
23
- const mapContext = useContext(MapContext);
23
+ const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
24
+
24
25
  const [isFollowed, setIsFollowed] = useState(false);
25
26
  const [geoJson, setGeoJson] = useState(undefined);
26
27
  const watchIdRef = useRef(undefined);
27
28
  const [locationAccessDenied, setLocationAccessDenied] = useState(false);
28
29
 
29
- const initializedRef = useRef(false);
30
- const mapRef = useRef(undefined);
31
- const componentId = useRef((props.idPrefix ? props.idPrefix : "MlFollowGps-") + uuidv4());
32
- const [accuracyRadius, setAccuracyRadius] = useState(30);
30
+ const [accuracyGeoJson, setAccuracyGeoJson] = useState();
33
31
 
34
32
  useEffect(() => {
35
- let _componentId = componentId.current;
36
-
37
-
38
33
  return () => {
39
- // This is the cleanup function, it is called when this react component is removed from react-dom
40
- // try to remove anything this component has added to the MapLibre-gl instance
41
- // e.g.: remove the layer
42
- // mapContext.getMap(props.mapId).removeLayer(layerRef.current);
43
- // check for the existence of map.style before calling getLayer or getSource
44
-
45
- if (mapRef.current) {
46
- mapRef.current.cleanup(_componentId);
47
- mapRef.current = undefined;
48
- }
49
34
  if (watchIdRef.current) {
50
- initializedRef.current = false;
51
35
  navigator.geolocation.clearWatch(watchIdRef.current);
52
36
  watchIdRef.current = undefined;
53
37
  }
54
38
  };
55
39
  }, []);
56
40
 
57
- useEffect(() => {
58
- if (!mapContext.mapExists(props.mapId) || initializedRef.current) return;
59
- // the MapLibre-gl instance (mapContext.getMap(props.mapId)) is accessible here
60
- // initialize the layer and add it to the MapLibre-gl instance or do something else with it
61
- initializedRef.current = true;
62
- mapRef.current = mapContext.getMap(props.mapId);
63
- mapRef.current.setCenter([7.132122000552613, 50.716405378037706]);
64
-
65
- }, [mapContext.mapIds, mapContext, props.mapId]);
66
-
67
- const getLocationSuccess = (pos) => {
68
- if (!mapRef.current) return;
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
- {isFollowed && geoJson &&
82
- <MlGeoJsonLayer geojson={geoJson} type={"circle"}
83
- paint={{
84
- "circle-radius": {
85
- stops: [
86
- [0, 0],
87
- [
88
- 20,
89
- (accuracyRadius) /
90
- 0.075 /
91
- Math.cos((geoJson.geometry.coordinates[1] * Math.PI) / 180),
92
- ],
93
- ],
94
- base: 2,
95
- },
96
- "circle-color": "#ee7700",
97
- "circle-opacity": 0.5,
98
- }}
99
- />
100
- }
101
-
102
- {isFollowed && geoJson &&
103
- <MlImageMarkerLayer
104
- options={{
105
- type: "symbol",
106
- source: {
107
- type: "geojson",
108
- data: geoJson
109
- },
110
- layout: {
111
- "icon-size": 0.1,
112
- "icon-offset": [0, -340]
113
- },
114
- }
115
- }
116
- imgSrc={"/assets/marker.png"}
117
- />
118
- }
73
+ {isFollowed && geoJson && (
74
+ <MlGeoJsonLayer
75
+ geojson={accuracyGeoJson}
76
+ type={"fill"}
77
+ paint={{
78
+ "fill-color": "#ee7700",
79
+ "fill-opacity": 0.5,
80
+ ...props.accuracyPaint,
81
+ }}
82
+ insertBeforeLayer={"MlFollowGpsMarker"}
83
+ />
84
+ )}
85
+
86
+ {isFollowed && geoJson && (
87
+ <MlImageMarkerLayer
88
+ layerId={"MlFollowGpsMarker"}
89
+ options={{
90
+ type: "symbol",
91
+ source: {
92
+ type: "geojson",
93
+ data: geoJson,
94
+ },
95
+ layout: {
96
+ "icon-size": 0.1,
97
+ "icon-offset": [0, -340],
98
+ ...props.markerLayout,
99
+ },
100
+ }}
101
+ imgSrc={props.markerImage || marker}
102
+ />
103
+ )}
119
104
 
120
105
  <Button
121
- sx={{zIndex: 1002, color: isFollowed ? "#bbb" : "#666", ...props.style}}
106
+ sx={{ zIndex: 1002, color: isFollowed ? props.onColor : props.offColor, ...props.style }}
122
107
  disabled={locationAccessDenied}
123
108
  onClick={() => {
124
- if (isFollowed) {
125
- navigator.geolocation.clearWatch(watchIdRef.current);
126
- } else {
127
- watchIdRef.current = navigator.geolocation.watchPosition(
128
- getLocationSuccess,
129
- getLocationError
130
- );
131
- }
132
109
  setIsFollowed(!isFollowed);
133
110
  }}
134
111
  >
135
112
  {" "}
136
- <RoomIcon sx={{}}/>{" "}
113
+ <RoomIcon sx={{ fontSize: props.style.fontSize }} />{" "}
137
114
  </Button>
138
115
  </>
139
116
  );
@@ -149,11 +126,14 @@ MlFollowGps.defaultProps = {
149
126
  backgroundColor: "#414141",
150
127
  borderRadius: "23%",
151
128
  margin: 0.15,
129
+ fontSize: "1.3em",
152
130
  ":hover": {
153
131
  backgroundColor: "#515151",
154
132
  color: "#ececec",
155
133
  },
156
134
  },
135
+ onColor: "#ececec",
136
+ offColor: "#666",
157
137
  };
158
138
 
159
139
  MlFollowGps.propTypes = {
@@ -165,5 +145,29 @@ MlFollowGps.propTypes = {
165
145
  * CSS style object that is applied to the button component
166
146
  */
167
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,
168
172
  };
169
173
  export default MlFollowGps;
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "MlFollowGPS",
3
- "title": "GPS-Tracker",
2
+ "name": "MlFollowGps",
3
+ "title": "GPS tracker",
4
4
  "description": "",
5
5
  "i18n": {
6
6
  "de": {
@@ -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(1));
61
+ await waitFor(() => expect(mockGeolocation.clearWatch).toHaveBeenCalledTimes(2));
64
62
  });
65
63
  });