@mapcomponents/react-maplibre 0.1.24 → 0.1.28

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 (112) hide show
  1. package/CHANGELOG.md +72 -7
  2. package/coverage/clover.xml +461 -489
  3. package/coverage/coverage-final.json +21 -20
  4. package/coverage/lcov-report/index.html +94 -79
  5. package/coverage/lcov-report/src/components/MapLibreMap/MapLibreMap.js.html +22 -22
  6. package/coverage/lcov-report/src/components/MapLibreMap/index.html +1 -1
  7. package/coverage/lcov-report/src/components/MlCreatePdfButton/MlCreatePdfButton.js.html +11 -11
  8. package/coverage/lcov-report/src/components/MlCreatePdfButton/index.html +11 -11
  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 +4 -4
  12. package/coverage/lcov-report/src/components/MlFillExtrusionLayer/index.html +1 -1
  13. package/coverage/lcov-report/src/components/MlFollowGps/MlFollowGps.js.html +212 -77
  14. package/coverage/lcov-report/src/components/MlFollowGps/index.html +19 -19
  15. package/coverage/lcov-report/src/components/MlGPXViewer/MlGPXViewer.js.html +129 -165
  16. package/coverage/lcov-report/src/components/MlGPXViewer/gpxConverter.js.html +8 -8
  17. package/coverage/lcov-report/src/components/MlGPXViewer/index.html +20 -20
  18. package/coverage/lcov-report/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js.html +45 -297
  19. package/coverage/lcov-report/src/components/MlGeoJsonLayer/index.html +19 -19
  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 +2 -2
  23. package/coverage/lcov-report/src/components/MlLayer/index.html +1 -1
  24. package/coverage/lcov-report/src/components/MlLayerMagnify/MlLayerMagnify.js.html +4 -4
  25. package/coverage/lcov-report/src/components/MlLayerMagnify/index.html +1 -1
  26. package/coverage/lcov-report/src/components/MlLayerSwipe/MlLayerSwipe.js.html +3 -3
  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 +38 -104
  35. package/coverage/lcov-report/src/components/MlNavigationCompass/index.html +19 -19
  36. package/coverage/lcov-report/src/components/MlNavigationTools/MlNavigationTools.js.html +40 -139
  37. package/coverage/lcov-report/src/components/MlNavigationTools/index.html +15 -15
  38. package/coverage/lcov-report/src/components/MlOsmLayer/MlOsmLayer.js.html +32 -155
  39. package/coverage/lcov-report/src/components/MlOsmLayer/index.html +19 -19
  40. package/coverage/lcov-report/src/components/MlScaleReference/MlScaleReference.js.html +39 -198
  41. package/coverage/lcov-report/src/components/MlScaleReference/index.html +9 -9
  42. package/coverage/lcov-report/src/components/MlShareMapState/MlShareMapState.js.html +1 -1
  43. package/coverage/lcov-report/src/components/MlShareMapState/index.html +1 -1
  44. package/coverage/lcov-report/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.js.html +4 -4
  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/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.js.html +580 -0
  49. package/coverage/lcov-report/src/components/MlTransitionGeoJsonLayer/index.html +116 -0
  50. package/coverage/lcov-report/src/components/MlUseMapDebugger/MlUseMapDebugger.js.html +1 -1
  51. package/coverage/lcov-report/src/components/MlUseMapDebugger/index.html +1 -1
  52. package/coverage/lcov-report/src/components/MlVectorTileLayer/MlVectorTileLayer.js.html +3 -3
  53. package/coverage/lcov-report/src/components/MlVectorTileLayer/index.html +1 -1
  54. package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/MlWmsFeatureInfoPopup.js.html +1 -1
  55. package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/index.html +1 -1
  56. package/coverage/lcov-report/src/components/MlWmsLayer/MlWmsLayer.js.html +8 -11
  57. package/coverage/lcov-report/src/components/MlWmsLayer/index.html +1 -1
  58. package/coverage/lcov-report/src/components/MlWmsLoader/MlWmsLoader.js.html +1 -1
  59. package/coverage/lcov-report/src/components/MlWmsLoader/index.html +1 -1
  60. package/coverage/lcov-report/src/hooks/index.html +6 -6
  61. package/coverage/lcov-report/src/hooks/useMap.js.html +38 -26
  62. package/coverage/lcov-report/src/hooks/useMapState.js.html +47 -38
  63. package/coverage/lcov-report/src/hooks/useWms.js.html +2 -2
  64. package/coverage/lcov-report/src/i18n.js.html +1 -1
  65. package/coverage/lcov-report/src/index.html +1 -1
  66. package/coverage/lcov-report/src/translations/english.js.html +1 -1
  67. package/coverage/lcov-report/src/translations/german.js.html +1 -1
  68. package/coverage/lcov-report/src/translations/index.html +1 -1
  69. package/coverage/lcov.info +826 -882
  70. package/dist/index.esm.js +288 -517
  71. package/dist/index.esm.js.map +1 -1
  72. package/jsdoc.json +3 -3
  73. package/package.json +9 -9
  74. package/src/components/MapLibreMap/lib/MapLibreGlWrapper.js +5 -1
  75. package/src/components/MapLibreMap/lib/MapLibreGlWrapper.test.js +3 -3
  76. package/src/components/MlFeatureEditor/MlFeatureEditor.test.js +2 -2
  77. package/src/components/MlFollowGps/MlFollowGps.js +99 -54
  78. package/src/components/MlFollowGps/MlFollowGps.test.js +1 -1
  79. package/src/components/MlGPXViewer/MlGPXViewer.js +69 -81
  80. package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.js +6 -90
  81. package/src/components/MlGeoJsonLayer/MlGeoJsonLayer.stories.js +4 -22
  82. package/src/components/MlGeoJsonLayer/util/getDefaultPaintPropsByType.js +2 -2
  83. package/src/components/MlNavigationCompass/MlNavigationCompass.js +17 -39
  84. package/src/components/MlNavigationCompass/MlNavigationCompass.test.js +3 -3
  85. package/src/components/MlNavigationTools/MlNavigationTools.js +30 -63
  86. package/src/components/MlOsmLayer/MlOsmLayer.js +15 -56
  87. package/src/components/MlOsmLayer/MlOsmLayer.stories.js +21 -10
  88. package/src/components/MlOsmLayer/MlOsmLayer.test.js +4 -4
  89. package/src/components/MlScaleReference/MlScaleReference.js +29 -82
  90. package/src/components/MlShareMapState/MlShareMapState.stories.js +1 -3
  91. package/src/components/MlThreeJsLayer/lib/GLTFLoader.js +2369 -2591
  92. package/src/components/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.doc.de.md +3 -0
  93. package/src/components/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.js +165 -0
  94. package/src/components/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.meta.json +15 -0
  95. package/src/components/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.stories.js +52 -0
  96. package/src/components/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.test.js +20 -0
  97. package/src/components/MlTransitionGeoJsonLayer/assets/sample_1.json +26 -0
  98. package/src/components/MlTransitionGeoJsonLayer/assets/sample_2.json +22 -0
  99. package/src/components/MlTransitionGeoJsonLayer/assets/sample_polygon_1.json +33 -0
  100. package/src/components/{MlGeoJsonLayer → MlTransitionGeoJsonLayer}/util/transitionFunctions.js +63 -97
  101. package/src/components/MlWmsLayer/MlWmsLayer.js +1 -2
  102. package/src/decorators/MapContextDecorator.js +5 -0
  103. package/src/decorators/MultiMapContextDecorator.js +6 -0
  104. package/src/hooks/useMap.js +8 -4
  105. package/src/hooks/useMapState.js +4 -1
  106. package/src/hooks/useWms.js +1 -1
  107. package/dist/b556faa3bc6829d2.png +0 -0
  108. package/src/components/MlFollowGps/assets/marker.png +0 -0
  109. package/src/decorators/EmptyMapContextDecorator.js +0 -25
  110. package/src/decorators/MapContext3DDecorator.js +0 -39
  111. package/src/decorators/MapContextDashboardDecorator.js +0 -19
  112. package/src/decorators/MapContextKlokantechBasicDecorator.js +0 -39
package/jsdoc.json CHANGED
@@ -8,9 +8,9 @@
8
8
  },
9
9
  "recurseDepth": 10,
10
10
  "opts": {
11
- "template": "node_modules/better-docs",
12
- "destination": "docs/"
13
- },
11
+ "template": "node_modules/better-docs",
12
+ "destination": "docs/jsdocs"
13
+ },
14
14
  "source": {
15
15
  "include": ["./src/"],
16
16
  "excludePattern": ".*/(utils|MlFeatureEditor\/lib)/.*"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapcomponents/react-maplibre",
3
- "version": "0.1.24",
3
+ "version": "0.1.28",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "module": "dist/index.esm.js",
@@ -13,17 +13,17 @@
13
13
  "@mapbox/mapbox-gl-draw": "^1.2.0",
14
14
  "@mapbox/mapbox-gl-sync-move": "^0.3.0",
15
15
  "@mapcomponents/react-core": "^0.1.7",
16
- "@mui/icons-material": "^5.0.1",
17
- "@mui/material": "5.0.0",
18
- "@mui/styles": "^5.0.1",
16
+ "@mui/icons-material": "^5.3.1",
17
+ "@mui/material": "5.4.0",
18
+ "@mui/styles": "^5.3.0",
19
19
  "@turf/turf": "^6.5.0",
20
- "jspdf": "^2.3.0",
21
- "maplibre-gl": "^1.13.0-rc.5",
20
+ "jspdf": "^2.5.1",
21
+ "maplibre-gl": "^2.1.6",
22
22
  "react-i18next": "^11.14.3",
23
- "three": "^0.126.1",
23
+ "three": "^0.137.5",
24
24
  "uuid": "^8.3.2",
25
25
  "wms-capabilities": "^0.5.1",
26
- "xmldom": "^0.5.0"
26
+ "xmldom": "^0.6.0"
27
27
  },
28
28
  "dependenciesComment": {
29
29
  "xmldom": "MlGPXLayer"
@@ -46,7 +46,7 @@
46
46
  "build-catalogue-meta": "node scripts/build-catalogue-meta.js",
47
47
  "build-catalogue-markdown-docs": "node scripts/build-catalogue-markdown-docs.js",
48
48
  "create-component": "./scripts/create-map-component.sh",
49
- "docs-create": "jsdoc -r -c ./jsdoc.json -d js-docs/react-map-components-maplibre/$npm_package_version",
49
+ "docs-create": "jsdoc -r -c ./jsdoc.json -d docs/jsdocs/react-map-components-maplibre/$npm_package_version",
50
50
  "docs-serve": "http-server ./js-docs/react-map-components-maplibre/$npm_package_version -p 3001"
51
51
  },
52
52
  "jest": {
@@ -413,7 +413,7 @@ const MapLibreGlWrapper = function (props) {
413
413
 
414
414
  this.addNativeMaplibreFunctionsAndProps = () => {
415
415
  // add MapLibre-gl functions
416
- Object.keys(this.map.__proto__).forEach((item) => {
416
+ Object.getOwnPropertyNames(Object.getPrototypeOf(this.map)).forEach((item) => {
417
417
  if (typeof this[item] === "undefined") {
418
418
  this[item] = (...props) => self.map[item](...props);
419
419
  }
@@ -504,6 +504,10 @@ const MapLibreGlWrapper = function (props) {
504
504
  self.wrapper.refreshViewport();
505
505
  self.wrapper.fire("viewportchange");
506
506
 
507
+ self.map.on("load", () => {
508
+ self.addNativeMaplibreFunctionsAndProps();
509
+ });
510
+
507
511
  self.map.on("move", () => {
508
512
  self.wrapper.viewportState = self.wrapper.getViewport();
509
513
  self.wrapper.fire("viewportchange");
@@ -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(4);
300
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(5);
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(4);
313
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(5);
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(6);
330
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(7);
331
331
 
332
332
  wrapper.find(".toggle_children_are_visible").simulate("click");
333
333
 
@@ -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(5));
85
+ await waitFor(() => expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(6));
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(5);
96
+ expect(mockMapLibreMethods.on).toHaveBeenCalledTimes(6);
97
97
 
98
98
  wrapper.find(".toggle_layer_visible").simulate("click");
99
99
 
@@ -1,14 +1,11 @@
1
- import React, { useRef, useEffect, useState, useCallback } from "react";
1
+ import React, { useEffect, useState, useCallback, useMemo } from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import useMap from "../../hooks/useMap";
4
4
 
5
5
  import Button from "@mui/material/Button";
6
- import RoomIcon from "@mui/icons-material/Room";
7
- import { point, circle } from "@turf/turf";
6
+ import GpsFixedIcon from "@mui/icons-material/GpsFixed";
7
+ import { point, circle, lineArc } from "@turf/turf";
8
8
  import MlGeoJsonLayer from "../MlGeoJsonLayer/MlGeoJsonLayer";
9
- import MlImageMarkerLayer from "../MlImageMarkerLayer/MlImageMarkerLayer";
10
-
11
- import marker from "./assets/marker.png";
12
9
 
13
10
  /**
14
11
  * Adds a button that makes the map follow the users GPS position using
@@ -23,31 +20,27 @@ const MlFollowGps = (props) => {
23
20
  const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
24
21
 
25
22
  const [isFollowed, setIsFollowed] = useState(false);
26
- const [geoJson, setGeoJson] = useState(undefined);
27
- const watchIdRef = useRef(undefined);
23
+ const [userLocationGeoJson, setUserLocationGeoJson] = useState(undefined);
28
24
  const [locationAccessDenied, setLocationAccessDenied] = useState(false);
29
-
30
25
  const [accuracyGeoJson, setAccuracyGeoJson] = useState();
31
-
32
- useEffect(() => {
33
- return () => {
34
- if (watchIdRef.current) {
35
- navigator.geolocation.clearWatch(watchIdRef.current);
36
- watchIdRef.current = undefined;
37
- }
38
- };
39
- }, []);
26
+ const [deviceOrientation, setDeviceOrientation] = useState(0);
40
27
 
41
28
  const getLocationSuccess = useCallback(
42
29
  (pos) => {
43
30
  if (!mapHook.map) return;
44
31
 
45
- mapHook.map.setCenter([pos.coords.longitude, pos.coords.latitude]);
32
+ mapHook.map.flyTo({
33
+ center: [pos.coords.longitude, pos.coords.latitude],
34
+ zoom: 18,
35
+ speed: 1,
36
+ curve: 1,
37
+ });
38
+ if (!props.showUserLocation) return;
46
39
  const geoJsonPoint = point([pos.coords.longitude, pos.coords.latitude]);
47
- setGeoJson(geoJsonPoint);
40
+ setUserLocationGeoJson(geoJsonPoint);
48
41
  setAccuracyGeoJson(circle(geoJsonPoint, pos.coords.accuracy / 1000));
49
42
  },
50
- [mapHook.map]
43
+ [mapHook.map, props]
51
44
  );
52
45
 
53
46
  const getLocationError = (err) => {
@@ -55,50 +48,89 @@ const MlFollowGps = (props) => {
55
48
  setLocationAccessDenied(true);
56
49
  };
57
50
 
51
+ const orientationCone = useMemo(
52
+ () => {
53
+ if (!userLocationGeoJson) {
54
+ return undefined;
55
+ }
56
+ let radius = 0.02;
57
+ let bearing1 = deviceOrientation - 15;
58
+ let bearing2 = deviceOrientation + 15;
59
+ const options = {steps: 65};
60
+ let arc = lineArc(userLocationGeoJson, radius, bearing1, bearing2, options);
61
+ let copy = arc;
62
+ copy.geometry.coordinates.push(userLocationGeoJson.geometry.coordinates);
63
+ copy.geometry.coordinates.slice(0, 0, userLocationGeoJson.geometry.coordinates);
64
+ return copy;
65
+ }, [deviceOrientation, userLocationGeoJson]
66
+ )
67
+
68
+ const handleOrientation = (event) => {
69
+ setDeviceOrientation(-event.alpha)
70
+ }
71
+
72
+ useEffect(() => {
73
+ if (isFollowed) {
74
+ let _handleOrientation = handleOrientation;
75
+ window.addEventListener('deviceorientation', _handleOrientation)
76
+ return () => {
77
+ window.removeEventListener('deviceorientation', _handleOrientation)
78
+ }
79
+ }
80
+ }, [isFollowed]);
81
+
58
82
  useEffect(() => {
59
83
  if (!mapHook.map) return;
60
84
 
61
85
  if (isFollowed) {
62
- watchIdRef.current = navigator.geolocation.watchPosition(
63
- getLocationSuccess,
64
- getLocationError
65
- );
66
- } else {
67
- navigator.geolocation.clearWatch(watchIdRef.current);
86
+ let _watchId = navigator.geolocation.watchPosition(getLocationSuccess, getLocationError);
87
+
88
+ return () => {
89
+ navigator.geolocation.clearWatch(_watchId);
90
+ };
68
91
  }
69
- }, [isFollowed, getLocationSuccess]);
92
+ }, [mapHook.map, isFollowed, getLocationSuccess]);
70
93
 
71
94
  return (
72
95
  <>
73
- {isFollowed && geoJson && (
96
+ {isFollowed && userLocationGeoJson && (
74
97
  <MlGeoJsonLayer
75
98
  geojson={accuracyGeoJson}
76
99
  type={"fill"}
77
100
  paint={{
78
- "fill-color": "#ee7700",
79
- "fill-opacity": 0.5,
101
+ "fill-color": "#cbd300",
102
+ "fill-opacity": 0.3,
80
103
  ...props.accuracyPaint,
81
104
  }}
82
- insertBeforeLayer={"MlFollowGpsMarker"}
105
+ insertBeforeLayer={props.insertBeforeLayer}
106
+ />
107
+ )}
108
+
109
+ {isFollowed && orientationCone && (
110
+ <MlGeoJsonLayer
111
+ geojson={orientationCone}
112
+ type={"fill"}
113
+ paint={{
114
+ "fill-color": "#0000ff",
115
+ "fill-antialias": false,
116
+ "fill-opacity": 0.3,
117
+ }}
118
+ insertBeforeLayer={props.insertBeforeLayer}
83
119
  />
84
120
  )}
85
121
 
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
- },
122
+ {isFollowed && userLocationGeoJson && (
123
+ <MlGeoJsonLayer
124
+ geojson={userLocationGeoJson}
125
+ type={"circle"}
126
+ paint={{
127
+ "circle-color": "#009ee0",
128
+ "circle-radius": 5,
129
+ "circle-stroke-color": "#fafaff",
130
+ "circle-stroke-width": 1,
131
+ ...props.circlePaint,
100
132
  }}
101
- imgSrc={props.markerImage || marker}
133
+ insertBeforeLayer={props.insertBeforeLayer}
102
134
  />
103
135
  )}
104
136
 
@@ -110,7 +142,7 @@ const MlFollowGps = (props) => {
110
142
  }}
111
143
  >
112
144
  {" "}
113
- <RoomIcon sx={{ fontSize: props.style.fontSize }} />{" "}
145
+ <GpsFixedIcon sx={{ fontSize: props.style.fontSize }} />{" "}
114
146
  </Button>
115
147
  </>
116
148
  );
@@ -134,6 +166,9 @@ MlFollowGps.defaultProps = {
134
166
  },
135
167
  onColor: "#ececec",
136
168
  offColor: "#666",
169
+ showAccuracyCircle: true,
170
+ showUserLocation: true,
171
+ showOrientation: true
137
172
  };
138
173
 
139
174
  MlFollowGps.propTypes = {
@@ -160,14 +195,24 @@ MlFollowGps.propTypes = {
160
195
  */
161
196
  accuracyPaint: PropTypes.object,
162
197
  /**
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
198
+ * position circle paint property object, that is passed to the MlGeoJsonLayer responsible for drawing the accuracy circle.
199
+ * Use any available paint prop from layer type "fill".
200
+ * https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/#fill
201
+ */
202
+ circlePaint: PropTypes.object,
203
+ /**
204
+ * By default, if showUserLocation is true, a transparent circle will be drawn around the user location
205
+ * indicating the accuracy (95% confidence level) of the user's location. Set to false to disable.
206
+ */
207
+ showAccuracyCircle: PropTypes.bool,
208
+ /**
209
+ * By default a dot will be shown on the map at the user's location. Set to false to disable.
166
210
  */
167
- markerLayout: PropTypes.object,
211
+ showUserLocation: PropTypes.bool,
168
212
  /**
169
- * Replace the default marker image with a custom one.
213
+ * By default a cone will be shown on the map at the user's location to indicate the device's orientation.
214
+ * Set to false to disable.
170
215
  */
171
- markerImage: PropTypes.string,
216
+ showOrientation: PropTypes.bool,
172
217
  };
173
218
  export default MlFollowGps;
@@ -58,6 +58,6 @@ describe("<MlFollowGps>", () => {
58
58
  wrapper.find("MlFollowGps button").simulate("click");
59
59
  //wrapper.find(".toggle_layer_visible").simulate("click");
60
60
 
61
- await waitFor(() => expect(mockGeolocation.clearWatch).toHaveBeenCalledTimes(2));
61
+ await waitFor(() => expect(mockGeolocation.clearWatch).toHaveBeenCalledTimes(1));
62
62
  });
63
63
  });
@@ -1,22 +1,20 @@
1
- import React, {useContext, useRef, useEffect, useState} from "react";
1
+ import React, { useContext, useRef, useEffect, useState } from "react";
2
2
  import PropTypes from "prop-types";
3
- import {MapContext} from "@mapcomponents/react-core";
4
- import {bbox} from "@turf/turf";
3
+ import { bbox } from "@turf/turf";
5
4
  import Divider from "@mui/material/Divider";
6
5
  import Typography from "@mui/material/Typography";
7
6
  import Drawer from "@mui/material/Drawer";
8
7
  import IconButton from "@mui/material/IconButton";
9
8
  import InfoIcon from "@mui/icons-material/Info";
10
9
  import FileCopy from "@mui/icons-material/FileCopy";
11
- import {Popup} from "maplibre-gl";
10
+ import { Popup } from "maplibre-gl";
12
11
  import List from "@mui/material/List";
13
12
  import ListItem from "@mui/material/ListItem";
14
13
  import ListItemText from "@mui/material/ListItemText";
15
14
  import GeoJsonContext from "./util/GeoJsonContext";
16
15
  import toGeoJSON from "./gpxConverter";
17
16
  import useMediaQuery from "@mui/material/useMediaQuery";
18
-
19
- import {v4 as uuidv4} from "uuid";
17
+ import useMap from "../../hooks/useMap";
20
18
 
21
19
  /**
22
20
  * MlGPXViewer returns a dropzone and a button to load a GPX Track into the map.
@@ -25,11 +23,8 @@ import {v4 as uuidv4} from "uuid";
25
23
  */
26
24
  const MlGPXViewer = (props) => {
27
25
  const dataSource = useContext(GeoJsonContext);
28
- const componentId = useRef((props.idPrefix ? props.idPrefix : "MlGpxViewer-") + uuidv4());
29
- const mapContext = useContext(MapContext);
30
- const mapId = props.mapId;
31
26
  const initializedRef = useRef(false);
32
- const mapRef = useRef(null);
27
+ const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer });
33
28
  const sourceName = "import-source";
34
29
  const layerNameLines = "importer-layer-lines";
35
30
  const layerNamePoints = "importer-layer-points";
@@ -49,36 +44,19 @@ const MlGPXViewer = (props) => {
49
44
  );
50
45
 
51
46
  useEffect(() => {
52
- let _componentId = componentId.current;
53
- let _popup = popup.current;
54
- return () => {
55
- // This is the cleanup function, it is called when this react component is removed from react-dom
56
- if (mapRef.current) {
57
- mapRef.current.cleanup(_componentId);
58
-
59
- mapRef.current.getCanvas().style.cursor = "";
60
-
61
- mapRef.current = null;
62
- }
63
- _popup.remove();
64
- };
65
- }, []);
66
-
67
- useEffect(() => {
68
- if (!mapContext.mapExists(mapId) || initializedRef.current) return;
47
+ if (!mapHook.map || initializedRef.current) return;
69
48
 
70
49
  initializedRef.current = true;
71
- mapRef.current = mapContext.getMap(mapId);
72
50
 
73
- mapRef.current.addSource(
51
+ mapHook.map.addSource(
74
52
  sourceName,
75
53
  {
76
54
  type: "geojson",
77
55
  data: dataSource.data,
78
56
  },
79
- componentId.current
57
+ mapHook.componentId
80
58
  );
81
- mapRef.current.addLayer(
59
+ mapHook.map.addLayer(
82
60
  {
83
61
  id: layerNameLines,
84
62
  source: sourceName,
@@ -89,9 +67,9 @@ const MlGPXViewer = (props) => {
89
67
  },
90
68
  },
91
69
  props.insertBeforeLayer,
92
- componentId.current
70
+ mapHook.componentId
93
71
  );
94
- mapRef.current.addLayer(
72
+ mapHook.map.addLayer(
95
73
  {
96
74
  id: layerNamePoints,
97
75
  source: sourceName,
@@ -103,40 +81,49 @@ const MlGPXViewer = (props) => {
103
81
  filter: ["==", "$type", "Point"],
104
82
  },
105
83
  props.insertBeforeLayer,
106
- componentId.current
84
+ mapHook.componentId
107
85
  );
108
86
 
109
87
  [layerNameLines, layerNamePoints].forEach((layerName) => {
110
- mapRef.current.setLayoutProperty(layerName, "visibility", "visible");
88
+ mapHook.map.setLayoutProperty(layerName, "visibility", "visible");
111
89
  });
112
- mapRef.current.on("mouseenter", layerNamePoints, (e) => {
113
- // Change the cursor style as a UI indicator.
114
- mapContext.getMap(props.mapId).getCanvas().style.cursor = "pointer";
115
-
116
- const coordinates = e.features[0].geometry.coordinates.slice();
117
- //const description = e.features[0].properties.desc;
118
- const name = e.features[0].properties.name;
119
-
120
- // Ensure that if the map is zoomed out such that multiple
121
- // copies of the feature are visible, the popup appears
122
- // over the copy being pointed to.
123
- while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
124
- coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
125
- }
90
+ mapHook.map.on(
91
+ "mouseenter",
92
+ layerNamePoints,
93
+ (e) => {
94
+ // Change the cursor style as a UI indicator.
95
+
96
+ const coordinates = e.features[0].geometry.coordinates.slice();
97
+ //const description = e.features[0].properties.desc;
98
+ const name = e.features[0].properties.name;
99
+
100
+ // Ensure that if the map is zoomed out such that multiple
101
+ // copies of the feature are visible, the popup appears
102
+ // over the copy being pointed to.
103
+ while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
104
+ coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
105
+ }
126
106
 
127
- // Populate the popup and set its coordinates
107
+ // Populate the popup and set its coordinates
128
108
 
129
- // based on the feature found.
130
- popup.current.setLngLat(coordinates).setHTML(name).addTo(mapRef.current);
131
- });
109
+ // based on the feature found.
110
+ popup.current.setLngLat(coordinates).setHTML(name).addTo(mapHook.map);
111
+ },
112
+ mapHook.componentId
113
+ );
132
114
 
133
- mapRef.current.on("mouseleave", "places", function () {
134
- mapRef.current.getCanvas().style.cursor = "";
135
- popup.current.remove();
136
- });
115
+ mapHook.map.on(
116
+ "mouseleave",
117
+ "places",
118
+ function () {
119
+ mapHook.map.getCanvas().style.cursor = "";
120
+ popup.current.remove();
121
+ },
122
+ mapHook.componentId
123
+ );
137
124
 
138
- mapRef.current.setZoom(10);
139
- }, [mapContext.mapIds, mapContext, dataSource.data, mapId, props]);
125
+ mapHook.map.setZoom(10);
126
+ }, [mapHook.map]);
140
127
 
141
128
  useEffect(() => {
142
129
  const dropZoneCurrent = dropZone.current;
@@ -162,9 +149,8 @@ const MlGPXViewer = (props) => {
162
149
  return () => {
163
150
  window.removeEventListener("dragenter", raiseDropZoneAndStopDefault);
164
151
  window.removeEventListener("dragover", stopDefault);
165
- window.removeEventListener("drop", stopDefault);
166
152
  dropZoneCurrent.removeEventListener("dragleave", lowerDropZone);
167
- window.removeEventListener("drop", (event) => lowerDropZoneAndStopDefault);
153
+ window.removeEventListener("drop", lowerDropZoneAndStopDefault);
168
154
  };
169
155
  });
170
156
 
@@ -174,12 +160,12 @@ const MlGPXViewer = (props) => {
174
160
  };
175
161
 
176
162
  useEffect(() => {
177
- if (!mapRef.current) return;
163
+ if (!mapHook.map) return;
178
164
 
179
165
  const visibility = props.visible ? "visible" : "none";
180
166
 
181
167
  [layerNameLines, layerNamePoints].forEach((layerName) => {
182
- mapRef.current.setLayoutProperty(layerName, "visibility", visibility);
168
+ mapHook.map.setLayoutProperty(layerName, "visibility", visibility);
183
169
  });
184
170
  }, [props.visible]);
185
171
 
@@ -205,7 +191,7 @@ const MlGPXViewer = (props) => {
205
191
  };
206
192
 
207
193
  const addGPXToMap = (gpxAsString) => {
208
- if (!mapRef.current) return;
194
+ if (!mapHook.map) return;
209
195
  try {
210
196
  setMetaData([]);
211
197
  const domParser = new DOMParser();
@@ -229,9 +215,9 @@ const MlGPXViewer = (props) => {
229
215
  });
230
216
  const data = toGeoJSON.gpx(gpxDoc);
231
217
  dataSource.setData(data);
232
- mapRef.current.getSource(sourceName).setData(data);
218
+ mapHook.map.getSource(sourceName).setData(data);
233
219
  const bounds = bbox(data);
234
- mapRef.current.fitBounds(bounds);
220
+ mapHook.map.fitBounds(bounds);
235
221
  } catch (e) {
236
222
  console.log(e);
237
223
  }
@@ -257,15 +243,17 @@ const MlGPXViewer = (props) => {
257
243
  };
258
244
  return (
259
245
  <>
260
- <div style={{
261
- position: "fixed",
262
- right: "5px",
263
- bottom: mediaIsMobile ? "40px" : "25px",
264
- display: "flex",
265
- flexDirection: "column",
266
- gap: "5px",
267
- zIndex: 1000,
268
- }}>
246
+ <div
247
+ style={{
248
+ position: "fixed",
249
+ right: "5px",
250
+ bottom: mediaIsMobile ? "40px" : "25px",
251
+ display: "flex",
252
+ flexDirection: "column",
253
+ gap: "5px",
254
+ zIndex: 1000,
255
+ }}
256
+ >
269
257
  <IconButton
270
258
  onClick={manualUpload}
271
259
  style={{
@@ -279,9 +267,9 @@ const MlGPXViewer = (props) => {
279
267
  type="file"
280
268
  id="input"
281
269
  multiple
282
- style={{display: "none"}}
270
+ style={{ display: "none" }}
283
271
  ></input>
284
- <FileCopy/>
272
+ <FileCopy />
285
273
  </IconButton>
286
274
  <IconButton
287
275
  onClick={toogleDrawer}
@@ -290,7 +278,7 @@ const MlGPXViewer = (props) => {
290
278
  }}
291
279
  size="large"
292
280
  >
293
- <InfoIcon/>
281
+ <InfoIcon />
294
282
  </IconButton>
295
283
  </div>
296
284
  <Drawer variant="persistent" anchor="left" open={open}>
@@ -304,11 +292,11 @@ const MlGPXViewer = (props) => {
304
292
  >
305
293
  Informationen zur Route
306
294
  </Typography>
307
- <Divider/>
295
+ <Divider />
308
296
  <List>
309
297
  {metaData.map((item) => (
310
298
  <ListItem key={`item--${item.id}`}>
311
- <ListItemText primary={item.value}/>
299
+ <ListItemText primary={item.value} />
312
300
  </ListItem>
313
301
  ))}
314
302
  </List>
@@ -350,7 +338,7 @@ MlGPXViewer.defaultProps = {
350
338
 
351
339
  MlGPXViewer.propTypes = {
352
340
  /**
353
- * Id of the target MapLibre instance in mapContext
341
+ * Id of the target MapLibre instance in mapHook
354
342
  */
355
343
  mapId: PropTypes.string,
356
344
  /**