@mapcomponents/react-maplibre 0.1.60 → 0.1.62

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 (114) hide show
  1. package/.eslintignore +2 -1
  2. package/CHANGELOG.md +10 -0
  3. package/coverage/clover.xml +127 -48
  4. package/coverage/coverage-final.json +4 -3
  5. package/coverage/lcov-report/index.html +38 -23
  6. package/coverage/lcov-report/src/components/MapLibreMap/MapLibreMap.tsx.html +1 -1
  7. package/coverage/lcov-report/src/components/MapLibreMap/index.html +1 -1
  8. package/coverage/lcov-report/src/components/MlCenterPosition/MlCenterPosition.tsx.html +1 -1
  9. package/coverage/lcov-report/src/components/MlCenterPosition/index.html +1 -1
  10. package/coverage/lcov-report/src/components/MlCreatePdfButton/MlCreatePdfButton.tsx.html +1 -1
  11. package/coverage/lcov-report/src/components/MlCreatePdfButton/index.html +1 -1
  12. package/coverage/lcov-report/src/components/MlCreatePdfForm/MlCreatePdfForm.tsx.html +2 -8
  13. package/coverage/lcov-report/src/components/MlCreatePdfForm/index.html +1 -1
  14. package/coverage/lcov-report/src/components/MlFeatureEditor/MlFeatureEditor.tsx.html +1 -1
  15. package/coverage/lcov-report/src/components/MlFeatureEditor/index.html +1 -1
  16. package/coverage/lcov-report/src/components/MlFillExtrusionLayer/MlFillExtrusionLayer.tsx.html +1 -1
  17. package/coverage/lcov-report/src/components/MlFillExtrusionLayer/index.html +1 -1
  18. package/coverage/lcov-report/src/components/MlFollowGps/MlFollowGps.tsx.html +1 -1
  19. package/coverage/lcov-report/src/components/MlFollowGps/index.html +1 -1
  20. package/coverage/lcov-report/src/components/MlGPXViewer/MlGPXViewer.tsx.html +1 -1
  21. package/coverage/lcov-report/src/components/MlGPXViewer/gpxConverter.js.html +1 -1
  22. package/coverage/lcov-report/src/components/MlGPXViewer/index.html +1 -1
  23. package/coverage/lcov-report/src/components/MlGeoJsonLayer/MlGeoJsonLayer.tsx.html +1 -1
  24. package/coverage/lcov-report/src/components/MlGeoJsonLayer/index.html +1 -1
  25. package/coverage/lcov-report/src/components/MlGeojsonLayerWithSource/MlGeojsonLayerWithSource.tsx.html +1 -1
  26. package/coverage/lcov-report/src/components/MlGeojsonLayerWithSource/index.html +1 -1
  27. package/coverage/lcov-report/src/components/MlImageMarkerLayer/MlImageMarkerLayer.tsx.html +1 -1
  28. package/coverage/lcov-report/src/components/MlImageMarkerLayer/index.html +1 -1
  29. package/coverage/lcov-report/src/components/MlLayer/MlLayer.tsx.html +1 -1
  30. package/coverage/lcov-report/src/components/MlLayer/index.html +1 -1
  31. package/coverage/lcov-report/src/components/MlLayerMagnify/MlLayerMagnify.tsx.html +1 -1
  32. package/coverage/lcov-report/src/components/MlLayerMagnify/index.html +1 -1
  33. package/coverage/lcov-report/src/components/MlLayerSwipe/MlLayerSwipe.tsx.html +1 -1
  34. package/coverage/lcov-report/src/components/MlLayerSwipe/index.html +1 -1
  35. package/coverage/lcov-report/src/components/MlLayerSwitcher/MlLayerSwitcher.js.html +1 -1
  36. package/coverage/lcov-report/src/components/MlLayerSwitcher/components/LayerBox.js.html +1 -1
  37. package/coverage/lcov-report/src/components/MlLayerSwitcher/components/index.html +1 -1
  38. package/coverage/lcov-report/src/components/MlLayerSwitcher/index.html +1 -1
  39. package/coverage/lcov-report/src/components/MlMarker/MlMarker.tsx.html +1 -1
  40. package/coverage/lcov-report/src/components/MlMarker/index.html +1 -1
  41. package/coverage/lcov-report/src/components/MlMeasureTool/MlMeasureTool.tsx.html +1 -1
  42. package/coverage/lcov-report/src/components/MlMeasureTool/index.html +1 -1
  43. package/coverage/lcov-report/src/components/MlNavigationCompass/MlNavigationCompass.tsx.html +1 -1
  44. package/coverage/lcov-report/src/components/MlNavigationCompass/index.html +1 -1
  45. package/coverage/lcov-report/src/components/MlNavigationTools/MlNavigationTools.tsx.html +1 -1
  46. package/coverage/lcov-report/src/components/MlNavigationTools/index.html +1 -1
  47. package/coverage/lcov-report/src/components/MlOsmLayer/MlOsmLayer.js.html +1 -1
  48. package/coverage/lcov-report/src/components/MlOsmLayer/MlOsmLayer.stories_.js.html +1 -1
  49. package/coverage/lcov-report/src/components/MlOsmLayer/index.html +1 -1
  50. package/coverage/lcov-report/src/components/MlScaleReference/MlScaleReference.js.html +1 -1
  51. package/coverage/lcov-report/src/components/MlScaleReference/index.html +1 -1
  52. package/coverage/lcov-report/src/components/MlShareMapState/MlShareMapState.js.html +1 -1
  53. package/coverage/lcov-report/src/components/MlShareMapState/index.html +1 -1
  54. package/coverage/lcov-report/src/components/MlSpatialElevationProfile/MlSpatialElevationProfile.js.html +1 -1
  55. package/coverage/lcov-report/src/components/MlSpatialElevationProfile/index.html +1 -1
  56. package/coverage/lcov-report/src/components/MlThreeJsLayer/MlThreeJsLayer.js.html +1 -1
  57. package/coverage/lcov-report/src/components/MlThreeJsLayer/index.html +1 -1
  58. package/coverage/lcov-report/src/components/MlTransitionGeoJsonLayer/MlTransitionGeoJsonLayer.tsx.html +1 -1
  59. package/coverage/lcov-report/src/components/MlTransitionGeoJsonLayer/index.html +1 -1
  60. package/coverage/lcov-report/src/components/MlUseMapDebugger/MlUseMapDebugger.js.html +1 -1
  61. package/coverage/lcov-report/src/components/MlUseMapDebugger/index.html +1 -1
  62. package/coverage/lcov-report/src/components/MlVectorTileLayer/MlVectorTileLayer.tsx.html +1 -1
  63. package/coverage/lcov-report/src/components/MlVectorTileLayer/index.html +1 -1
  64. package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/MlWmsFeatureInfoPopup.js.html +1 -1
  65. package/coverage/lcov-report/src/components/MlWmsFeatureInfoPopup/index.html +1 -1
  66. package/coverage/lcov-report/src/components/MlWmsLayer/MlWmsLayer.tsx.html +1 -1
  67. package/coverage/lcov-report/src/components/MlWmsLayer/index.html +1 -1
  68. package/coverage/lcov-report/src/components/MlWmsLoader/MlWmsLoader.tsx.html +1 -1
  69. package/coverage/lcov-report/src/components/MlWmsLoader/index.html +1 -1
  70. package/coverage/lcov-report/src/contexts/MapContext.tsx.html +1 -1
  71. package/coverage/lcov-report/src/contexts/SimpleDataContext.js.html +1 -1
  72. package/coverage/lcov-report/src/contexts/SimpleDataProvider.js.html +1 -1
  73. package/coverage/lcov-report/src/contexts/index.html +1 -1
  74. package/coverage/lcov-report/src/hooks/index.html +11 -11
  75. package/coverage/lcov-report/src/hooks/useCameraFollowPath/index.html +116 -0
  76. package/coverage/lcov-report/src/hooks/useCameraFollowPath/useCameraFollowPath.tsx.html +661 -0
  77. package/coverage/lcov-report/src/hooks/useExportMap/index.html +14 -14
  78. package/coverage/lcov-report/src/hooks/useExportMap/index.ts.html +1 -1
  79. package/coverage/lcov-report/src/hooks/useExportMap/lib.ts.html +21 -108
  80. package/coverage/lcov-report/src/hooks/useLayer.ts.html +1 -1
  81. package/coverage/lcov-report/src/hooks/useLayerEvent.js.html +9 -9
  82. package/coverage/lcov-report/src/hooks/useMap.ts.html +1 -1
  83. package/coverage/lcov-report/src/hooks/useMapState.ts.html +1 -1
  84. package/coverage/lcov-report/src/hooks/useSource.ts.html +1 -1
  85. package/coverage/lcov-report/src/hooks/useWms.js.html +1 -1
  86. package/coverage/lcov-report/src/index.html +1 -1
  87. package/coverage/lcov-report/src/index.ts.html +8 -2
  88. package/coverage/lcov.info +222 -72
  89. package/dist/components/MlCreatePdfForm/MlCreatePdfForm.stories.d.ts +1 -0
  90. package/dist/components/MlCreatePdfForm/lib/PdfContext.d.ts +3 -16
  91. package/dist/components/MlCreatePdfForm/lib/PdfPreview.d.ts +19 -6
  92. package/dist/components/MlCreatePdfForm/lib/_PdfPreview.d.ts +13 -0
  93. package/dist/hooks/useCameraFollowPath/useCameraFollowPath.d.ts +35 -0
  94. package/dist/hooks/useCameraFollowPath/useCameraFollowPath.stories.d.ts +17 -0
  95. package/dist/index.d.ts +2 -0
  96. package/dist/index.esm.js +656 -597
  97. package/dist/index.esm.js.map +1 -1
  98. package/package.json +3 -1
  99. package/scripts/build-catalogue-meta.js +1 -1
  100. package/src/components/MlCreatePdfForm/MlCreatePdfForm.stories.tsx +16 -1
  101. package/src/components/MlCreatePdfForm/MlCreatePdfForm.tsx +0 -2
  102. package/src/components/MlCreatePdfForm/lib/PdfContext.tsx +16 -18
  103. package/src/components/MlCreatePdfForm/lib/PdfForm.tsx +80 -7
  104. package/src/components/MlCreatePdfForm/lib/PdfPreview.tsx +285 -357
  105. package/src/components/MlCreatePdfForm/lib/_PdfPreview.tsx +399 -0
  106. package/src/components/MlCreatePdfForm/lib/pdfContext.d.ts +24 -0
  107. package/src/components/MlCreatePdfForm/lib/preview.css +114 -0
  108. package/src/hooks/useCameraFollowPath/useCameraFollowPath.doc.de.md +4 -0
  109. package/src/hooks/useCameraFollowPath/useCameraFollowPath.doc.en.md +1 -0
  110. package/src/hooks/useCameraFollowPath/useCameraFollowPath.meta.json +15 -0
  111. package/src/hooks/useCameraFollowPath/useCameraFollowPath.stories.tsx +198 -0
  112. package/src/hooks/useCameraFollowPath/useCameraFollowPath.tsx +192 -0
  113. package/src/hooks/useExportMap/lib.ts +14 -43
  114. package/src/index.ts +2 -0
@@ -1,399 +1,327 @@
1
- import React, { useContext, useRef, useState, useEffect } from 'react';
2
- import MlGeoJsonLayer from '../../MlGeoJsonLayer/MlGeoJsonLayer';
3
- import * as turf from '@turf/turf';
1
+ import React, { useRef, useEffect, useMemo } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import Moveable from 'react-moveable';
4
4
  import useMap from '../../../hooks/useMap';
5
- import useLayerEvent from '../../../hooks/useLayerEvent';
6
- import { BBox, Feature, Polygon } from '@turf/turf';
7
- import PdfContext from './PdfContext';
8
- import { MapMouseEvent, MapTouchEvent, MapLayerMouseEvent, LngLat } from 'maplibre-gl';
9
-
10
- const createPreviewGeojson = (
11
- geojsonProps: { center: LngLat; distance: number; bearing: number },
12
- orientation: string
13
- ) => {
14
- const topLeftAngle = orientation === 'portrait' ? -35.3 : -54.7;
15
- const bottomRightAngle = orientation === 'portrait' ? 144.7 : 125.3;
16
- const topLeft = turf.destination(
17
- [geojsonProps.center.lng, geojsonProps.center.lat],
18
- geojsonProps.distance,
19
- topLeftAngle
20
- );
21
- const bottomRight = turf.destination(
22
- [geojsonProps.center.lng, geojsonProps.center.lat],
23
- geojsonProps.distance,
24
- bottomRightAngle
25
- );
26
- const bbox = [
27
- topLeft.geometry.coordinates[0],
28
- topLeft.geometry.coordinates[1],
29
- bottomRight.geometry.coordinates[0],
30
- bottomRight.geometry.coordinates[1],
31
- ];
32
-
33
- let _previewGeojson = turf.bboxPolygon(bbox as BBox);
34
- _previewGeojson = turf.transformRotate(_previewGeojson, geojsonProps.bearing);
35
- if (!_previewGeojson?.properties) {
36
- _previewGeojson.properties = {};
37
- }
38
- _previewGeojson.properties.bearing = geojsonProps.bearing;
39
- return _previewGeojson;
40
- };
5
+ import useMapState from '../../../hooks/useMapState';
6
+ import * as turf from '@turf/turf';
7
+ import { PdfPreviewOptions } from './pdfContext';
8
+ import { LngLatLike, Map as MapType, PointLike } from 'maplibre-gl';
9
+ import { Feature, Units } from '@turf/turf';
41
10
 
42
- interface PdfPreviewProps {
11
+ type Props = {
43
12
  /**
44
13
  * Id of the target MapLibre instance in mapContext
45
14
  */
46
15
  mapId?: string;
47
16
  /**
48
- * Id of an existing layer in the mapLibre instance to help specify the layer order
49
- * This layer will be visually beneath the layer with the "insertBeforeLayer" id.
17
+ * Polygon GeoJson Feature representing the printing area
50
18
  */
51
- insertBeforeLayer?: string;
52
- }
19
+ geojsonRef: React.MutableRefObject<
20
+ | Feature
21
+ | undefined
22
+ >;
23
+ /**
24
+ * a state variable containing the PDF previews current state
25
+ */
26
+ options: PdfPreviewOptions;
27
+ /**
28
+ * setter function to update the current PDF preview state
29
+ */
30
+ setOptions: (arg1: (val: PdfPreviewOptions) => PdfPreviewOptions) => void;
31
+ };
32
+
33
+ function getTargetRotationAngle(target: HTMLDivElement) {
34
+ const el_style = window.getComputedStyle(target, null);
35
+ const el_transform = el_style.getPropertyValue('transform');
36
+
37
+ let deg = 0;
53
38
 
54
- interface geojsonProps {
55
- center: { lng: number; lat: number };
56
- distance: number;
57
- bearing: number;
58
- geojson: Feature<Polygon> | undefined;
39
+ if (el_transform !== 'none') {
40
+ const values = el_transform.split('(')[1].split(')')[0].split(',');
41
+ const a = parseFloat(values[0]);
42
+ const b = parseFloat(values[1]);
43
+ deg = Math.round(Math.atan2(b, a) * (180 / Math.PI));
44
+ }
45
+
46
+ return deg < 0 ? deg + 360 : deg;
59
47
  }
60
48
 
61
- export default function PdfPreview(props: PdfPreviewProps) {
62
- const pdfContext = useContext(PdfContext);
63
- const initializedRef = useRef(false);
64
- const activeFeature = useRef();
49
+ function calcElemTransformedPoint(
50
+ el: HTMLDivElement,
51
+ point: [number, number],
52
+ transformOrigin: [number, number]
53
+ ): PointLike {
54
+ const style = getComputedStyle(el);
55
+ const p = [point[0] - transformOrigin[0], point[1] - transformOrigin[1]];
65
56
 
66
- const dragging = useRef(false);
57
+ const matrix = new DOMMatrixReadOnly(style.transform);
67
58
 
68
- const draggingResizeHandle = useRef(false);
59
+ // transform pixel coordinates according to the css transform state of "el" (target)
60
+ return [
61
+ p[0] * matrix.a + p[1] * matrix.c + matrix.e + transformOrigin[0],
62
+ p[0] * matrix.b + p[1] * matrix.d + matrix.f + transformOrigin[1],
63
+ ];
64
+ }
69
65
 
70
- const draggingRotationHandle = useRef(false);
66
+ // measure distance in pixels that is used to determine the current css transform.scale relative to the maps viewport.zoom
67
+ const scaleAnchorInPixels = 10;
71
68
 
72
- const [geojsonProps, setGeojsonProps] = useState<geojsonProps>({
73
- center: { lng: 0, lat: 0 },
74
- distance: 10,
75
- bearing: 0,
76
- geojson: undefined,
77
- });
69
+ // used to determine the MapZoomScale modifier which is multiplied with props.options.scale to relate the scale to the current map viewport.zoom
70
+ function getMapZoomScaleModifier(point: [number, number], _map: MapType) {
71
+ const left = _map.unproject(point);
72
+ const right = _map.unproject([point[0] + scaleAnchorInPixels, point[1]]);
73
+ const maxMeters = left.distanceTo(right);
74
+ return scaleAnchorInPixels / maxMeters;
75
+ }
78
76
 
77
+ /**
78
+ * PdfPreview component renders a transformable (drag, scale, rotate) preview of the desired export or print content
79
+ */
80
+ export default function PdfPreview(props: Props) {
81
+ const mapState = useMapState({ mapId: props.mapId, watch: { layers: false, viewport: true } });
82
+ const targetRef = useRef<HTMLDivElement>(null);
83
+ const fixedScaleRef = useRef<number | undefined>();
84
+ const moveableRef = useRef<Moveable>(null);
85
+ const mapContainerRef = useRef<HTMLDivElement>(document.querySelector('.mapContainer'));
86
+ //const [transform, setTransform] = useState('translate(452.111px, 15.6148px)');
79
87
  const mapHook = useMap({
80
88
  mapId: props.mapId,
81
- waitForLayer: props.insertBeforeLayer,
82
89
  });
83
90
 
84
91
  useEffect(() => {
85
- if (
86
- !mapHook.map ||
87
- !pdfContext.geojsonRef ||
88
- !pdfContext.orientation ||
89
- !pdfContext.template ||
90
- initializedRef.current
91
- )
92
- return;
93
-
94
- initializedRef.current = true;
95
-
96
- const center = mapHook.map.map.getCenter();
97
- const canvasHeight = mapHook.map.map._canvas.height;
98
- const canvasWidth = mapHook.map.map._canvas.width;
99
- const bboxPixelHeight = Math.ceil(canvasHeight / 2);
100
- const bboxPixelWidth = Math.ceil(
101
- (pdfContext.template.width / pdfContext.template.height) * bboxPixelHeight
102
- );
92
+ if (!mapState?.viewport?.zoom || !mapHook.map) return;
93
+ // if the component was initialized with scale or center as undefined derive those values from the current map view state
94
+
95
+ //initialize props if not defined
96
+ const _centerX = Math.round(mapHook.map.map._container.clientWidth / 2);
97
+ const _centerY = Math.round(mapHook.map.map._container.clientHeight / 2);
98
+
99
+ if (!props.options.scale) {
100
+ //const scale = parseFloat(/(14/mapState.viewport.zoom));
101
+ const scale = 1 / getMapZoomScaleModifier([_centerX, _centerY], mapHook.map.map);
102
+
103
+ props.setOptions((val: PdfPreviewOptions) => ({ ...val, scale: [scale, scale] }));
104
+ }
105
+ if (!props.options.center) {
106
+ const _center = mapHook.map.map.unproject([_centerX, _centerY]);
107
+ props.setOptions((val: PdfPreviewOptions) => ({
108
+ ...val,
109
+ center: [_center.lng, _center.lat],
110
+ }));
111
+ }
112
+ }, [mapHook.map, mapState.viewport?.zoom]);
103
113
 
104
- const topLeft = mapHook.map.map.unproject([
105
- Math.floor(canvasWidth / 2 - bboxPixelWidth / 2),
106
- Math.floor(canvasHeight / 2 - bboxPixelHeight / 2),
107
- ]);
108
-
109
- const distance = turf.distance([center.lng, center.lat], [topLeft.lng, topLeft.lat]);
114
+ useEffect(() => {
115
+ if (!mapHook.map) return;
110
116
 
111
- const tmpGeojsonProps = {
112
- center,
113
- distance,
114
- bearing: 0,
115
- geojson: createPreviewGeojson({ center, distance, bearing: 0 }, pdfContext.orientation),
117
+ mapHook.map.map.setPitch(0);
118
+ const _maxPitch = mapHook.map.map.getMaxPitch();
119
+ mapHook.map.map.setMaxPitch(0);
120
+ return () => {
121
+ mapHook.map?.map.setMaxPitch(_maxPitch);
116
122
  };
117
- setGeojsonProps(tmpGeojsonProps);
118
- pdfContext.geojsonRef.current = tmpGeojsonProps.geojson;
119
123
  }, [mapHook.map]);
120
124
 
121
- useEffect(() => {
122
- if (!pdfContext.orientation || !pdfContext.geojsonRef) return;
123
-
124
- const tmpGeojsonProps = JSON.parse(JSON.stringify(geojsonProps));
125
- tmpGeojsonProps.geojson = createPreviewGeojson(tmpGeojsonProps, pdfContext.orientation);
126
- setGeojsonProps(tmpGeojsonProps);
127
- pdfContext.geojsonRef.current = tmpGeojsonProps.geojson;
128
- }, [pdfContext.orientation]);
129
-
130
- // Resize handle events
131
- useLayerEvent({
132
- event: 'mouseenter',
133
- layerId: 'pdfPreviewGeojsonResizeHandle',
134
- eventHandler: function () {
135
- if (!mapHook.map) return;
136
-
137
- mapHook.map.map._canvas.style.cursor = 'nwse-resize';
138
- mapHook.map.map.dragPan.disable();
139
- },
140
- });
141
- useLayerEvent({
142
- event: 'mouseleave',
143
- layerId: 'pdfPreviewGeojsonResizeHandle',
144
- eventHandler: function () {
145
- if (!mapHook.map) return;
146
-
147
- mapHook.map.map._canvas.style.cursor = '';
148
- mapHook.map.map.dragPan.enable();
149
- },
150
- });
151
- useLayerEvent({
152
- event: 'mousedown',
153
- layerId: 'pdfPreviewGeojsonResizeHandle',
154
- addTouchEvents: true,
155
- eventHandler: function (e: MapMouseEvent | MapTouchEvent) {
156
- e.preventDefault();
157
- if (!mapHook.map) return;
158
-
159
- dragging.current = false;
160
- draggingRotationHandle.current = false;
161
- draggingResizeHandle.current = true;
162
- mapHook.map.map._canvas.style.cursor = 'move';
163
-
164
- function onMove(e: MapMouseEvent | MapTouchEvent) {
165
- if (!pdfContext.geojsonRef || !draggingResizeHandle.current || !pdfContext.orientation)
166
- return;
167
-
168
- const tmpGeojsonProps = JSON.parse(JSON.stringify(geojsonProps));
169
- let _distance = turf.distance(
170
- [tmpGeojsonProps.center.lng, tmpGeojsonProps.center.lat],
171
- [e.lngLat.lng, e.lngLat.lat]
172
- );
173
- // limit max diagonal distance of PDF area to 120km as larger area lead to distortions for northern and southern areas
174
- if (_distance > 60) {
175
- _distance = 60;
176
- }
177
- tmpGeojsonProps.distance = _distance;
178
- tmpGeojsonProps.geojson = createPreviewGeojson(tmpGeojsonProps, pdfContext.orientation);
179
- pdfContext.geojsonRef.current = tmpGeojsonProps.geojson;
180
- setGeojsonProps(tmpGeojsonProps);
181
- }
182
- function onUp() {
183
- if (!draggingResizeHandle.current || !mapHook.map) return;
125
+ const transformOrigin = useMemo<[number, number]>(() => {
126
+ if (props.options.orientation === 'portrait') {
127
+ return [props.options.width / 2, props.options.height / 2];
128
+ } else {
129
+ return [props.options.height / 2, props.options.width / 2];
130
+ }
131
+ }, [props.options.orientation, props.options.width, props.options.height]);
184
132
 
185
- mapHook.map.map._canvas.style.cursor = '';
186
- draggingResizeHandle.current = false;
133
+ const transform = useMemo(() => {
134
+ if (!mapHook.map || !props.options.scale) return 'none';
187
135
 
188
- mapHook.map.map.dragPan.enable();
189
- // Unbind mouse events
190
- mapHook.map.map.off('mousemove', onMove);
191
- mapHook.map.map.off('touchmove', onMove);
192
- }
136
+ const centerInPixels = mapHook.map.map.project(props.options.center as LngLatLike);
193
137
 
194
- // Mouse events
195
- mapHook.map.map.on('mousemove', onMove);
196
- mapHook.map.map.on('touchmove', onMove);
197
- mapHook.map.map.once('mouseup', onUp);
198
- mapHook.map.map.once('touchend', onUp);
199
- },
200
- });
138
+ const x = centerInPixels.x;
139
+ const y = centerInPixels.y;
140
+ const scale = props.options.scale[0] * getMapZoomScaleModifier([x, y], mapHook.map.map);
201
141
 
202
- // Rotation handle events
203
- useLayerEvent({
204
- event: 'mouseenter',
205
- layerId: 'pdfPreviewGeojsonRotationHandle',
206
- eventHandler: function () {
207
- if (!mapHook.map) return;
142
+ const viewportBearing = mapState?.viewport?.bearing ? mapState.viewport?.bearing : 0;
208
143
 
209
- mapHook.map.map._canvas.style.cursor = 'nwse-resize';
210
- mapHook.map.map.dragPan.disable();
211
- },
212
- });
213
- useLayerEvent({
214
- event: 'mouseleave',
215
- layerId: 'pdfPreviewGeojsonRotationHandle',
216
- eventHandler: function () {
217
- if (!mapHook.map) return;
218
-
219
- mapHook.map.map._canvas.style.cursor = '';
220
- mapHook.map.map.dragPan.enable();
221
- },
222
- });
223
- useLayerEvent({
224
- event: 'mousedown',
225
- layerId: 'pdfPreviewGeojsonRotationHandle',
226
- addTouchEvents: true,
227
- eventHandler: function (e: MapMouseEvent | MapTouchEvent) {
228
- e.preventDefault();
229
- if (!mapHook.map || !pdfContext.orientation) return;
230
-
231
- dragging.current = false;
232
- draggingResizeHandle.current = false;
233
- draggingRotationHandle.current = true;
234
- mapHook.map.map._canvas.style.cursor = 'move';
235
-
236
- function onMove(e: MapMouseEvent | MapTouchEvent) {
237
- e.preventDefault();
238
- if (!draggingRotationHandle.current || !pdfContext.orientation || !pdfContext.geojsonRef)
239
- return;
240
-
241
- const tmpGeojsonProps = JSON.parse(JSON.stringify(geojsonProps));
242
- const _bearing = turf.bearing(
243
- [tmpGeojsonProps.center.lng, tmpGeojsonProps.center.lat],
244
- [e.lngLat.lng, e.lngLat.lat]
245
- );
246
- tmpGeojsonProps.bearing = 144.7 + _bearing;
247
- tmpGeojsonProps.geojson = createPreviewGeojson(tmpGeojsonProps, pdfContext.orientation);
248
- pdfContext.geojsonRef.current = tmpGeojsonProps.geojson;
249
- setGeojsonProps(tmpGeojsonProps);
250
- }
251
- function onUp() {
252
- if (!draggingRotationHandle.current || !mapHook.map) return;
144
+ const _transform = `translate(${Math.floor(
145
+ centerInPixels.x - transformOrigin[0]
146
+ )}px,${Math.floor(centerInPixels.y - transformOrigin[1])}px) rotate(${
147
+ props.options.rotate - viewportBearing
148
+ }deg) scale(${scale},${scale})`;
253
149
 
254
- mapHook.map.map._canvas.style.cursor = '';
255
- draggingRotationHandle.current = false;
150
+ if (targetRef.current) targetRef.current.style.transform = _transform;
256
151
 
257
- mapHook.map.map.dragPan.enable();
258
- // Unbind mouse events
259
- mapHook.map.map.off('mousemove', onMove);
260
- mapHook.map.map.off('touchmove', onMove);
261
- }
152
+ return _transform;
153
+ }, [
154
+ mapHook.map,
155
+ mapState.viewport,
156
+ props.options.scale,
157
+ props.options.rotate,
158
+ props.options.center,
159
+ transformOrigin,
160
+ ]);
262
161
 
263
- // Mouse events
264
- mapHook.map.map.on('mousemove', onMove);
265
- mapHook.map.map.on('touchmove', onMove);
266
- mapHook.map.map.once('mouseup', onUp);
267
- mapHook.map.map.once('touchend', onUp);
268
- },
269
- });
162
+ useEffect(() => {
163
+ moveableRef.current?.updateTarget();
164
+ }, [transform]);
270
165
 
271
- // drag & drop events
272
- useLayerEvent({
273
- event: 'mouseenter',
274
- layerId: 'pdfPreviewGeojson',
275
- eventHandler: function (e: MapLayerMouseEvent) {
276
- if (!mapHook.map || !e?.features?.length) return;
277
- mapHook.map.map._canvas.style.cursor = 'move';
278
- activeFeature.current = e.features[0];
279
- },
280
- });
281
- useLayerEvent({
282
- event: 'mouseleave',
283
- layerId: 'pdfPreviewGeojson',
284
- eventHandler: function () {
285
- if (!mapHook.map) return;
286
-
287
- mapHook.map.map._canvas.style.cursor = '';
288
- mapHook.map.map.dragPan.enable();
289
- activeFeature.current = undefined;
290
- },
291
- });
292
- useLayerEvent({
293
- event: 'mousedown',
294
- addTouchEvents: true,
295
- layerId: 'pdfPreviewGeojson',
296
- eventHandler: function (e: MapMouseEvent | MapTouchEvent) {
297
- e.preventDefault();
298
- console.log('mousedown');
299
- if (!mapHook.map) return;
300
-
301
- draggingResizeHandle.current = false;
302
- draggingRotationHandle.current = false;
303
- dragging.current = true;
304
- mapHook.map.map._canvas.style.cursor = 'move';
305
-
306
- function onMove(e: MapMouseEvent | MapTouchEvent) {
307
- e.preventDefault();
308
- if (!dragging.current || !pdfContext.geojsonRef || !pdfContext.orientation) return;
309
-
310
- const tmpGeojsonProps = JSON.parse(JSON.stringify(geojsonProps));
311
- tmpGeojsonProps.center = e.lngLat;
312
- tmpGeojsonProps.geojson = createPreviewGeojson(tmpGeojsonProps, pdfContext.orientation);
313
- pdfContext.geojsonRef.current = tmpGeojsonProps.geojson;
314
- setGeojsonProps(tmpGeojsonProps);
315
- }
316
- function onUp() {
317
- if (!dragging.current || !mapHook.map) return;
166
+ useEffect(() => {
167
+ // update props.options.scale if fixedScale was changed
168
+ if (
169
+ !mapHook.map ||
170
+ !props.options.center ||
171
+ !props.options.fixedScale ||
172
+ (typeof props.options.fixedScale !== 'undefined' &&
173
+ fixedScaleRef.current === props.options.fixedScale)
174
+ )
175
+ return;
318
176
 
319
- mapHook.map.map._canvas.style.cursor = '';
320
- dragging.current = false;
177
+ fixedScaleRef.current = props.options.fixedScale;
178
+ const point = turf.point(props.options.center);
179
+ const distance = props.options.fixedScale * (props.options.width / 1000);
321
180
 
322
- mapHook.map.map.dragPan.enable();
323
- // Unbind mouse events
324
- mapHook.map.map.off('mousemove', onMove);
325
- mapHook.map.map.off('touchmove', onMove);
326
- }
181
+ const bearing = 90;
182
+ const options = { units: 'meters' as Units };
183
+ const destination = turf.destination(point, distance, bearing, options);
327
184
 
328
- // Mouse events
329
- mapHook.map.map.on('mousemove', onMove);
330
- mapHook.map.map.on('touchmove', onMove);
331
- mapHook.map.map.once('mouseup', onUp);
332
- mapHook.map.map.once('touchend', onUp);
333
- },
334
- });
185
+ const centerInPixels = mapHook.map.map.project(point.geometry.coordinates as LngLatLike);
186
+ const destinationInPixels = mapHook.map.map.project(
187
+ destination.geometry.coordinates as LngLatLike
188
+ );
335
189
 
336
- //map.on('mouseleave', 'point', function() {
337
- // map.setPaintProperty('point', 'circle-color', '#3887be');
338
- // canvas.style.cursor = '';
339
- // isCursorOverPoint = false;
340
- // map.dragPan.enable();
341
- //});
190
+ const scaleFactor =
191
+ (Math.round(destinationInPixels.x - centerInPixels.x) / props.options.width) *
192
+ (1 / getMapZoomScaleModifier([centerInPixels.x, centerInPixels.y], mapHook.map.map));
193
+ props.setOptions((val: PdfPreviewOptions) => ({ ...val, scale: [scaleFactor, scaleFactor] }));
194
+ }, [mapHook.map, props.options.width, props.options.center, props.options.fixedScale]);
342
195
 
343
- return (
196
+ // update props.geoJsonRef
197
+ useEffect(() => {
198
+ if (targetRef.current && mapHook.map) {
199
+ // apply orientation
200
+ let _width = props.options.width;
201
+ let _height = props.options.height;
202
+ if (props.options.orientation === 'portrait') {
203
+ targetRef.current.style.width = props.options.width + 'px';
204
+ targetRef.current.style.height = props.options.height + 'px';
205
+ } else {
206
+ targetRef.current.style.width = props.options.height + 'px';
207
+ targetRef.current.style.height = props.options.width + 'px';
208
+ _width = props.options.height;
209
+ _height = props.options.width;
210
+ }
211
+ moveableRef.current?.updateTarget();
212
+
213
+ const topLeft = mapHook.map.map.unproject(
214
+ calcElemTransformedPoint(targetRef.current, [0, 0], transformOrigin)
215
+ );
216
+ const topRight = mapHook.map.map.unproject(
217
+ calcElemTransformedPoint(targetRef.current, [_width, 0], transformOrigin)
218
+ );
219
+ const bottomLeft = mapHook.map.map.unproject(
220
+ calcElemTransformedPoint(targetRef.current, [0, _height], transformOrigin)
221
+ );
222
+ const bottomRight = mapHook.map.map.unproject(
223
+ calcElemTransformedPoint(targetRef.current, [_width, _height], transformOrigin)
224
+ );
225
+
226
+ const _geoJson = {
227
+ type: 'Feature',
228
+ bbox: [topLeft.lng, topLeft.lat, bottomRight.lng, bottomRight.lat],
229
+ geometry: {
230
+ type: 'Polygon',
231
+ coordinates: [
232
+ [
233
+ [topLeft.lng, topLeft.lat],
234
+ [topRight.lng, topRight.lat],
235
+ [bottomRight.lng, bottomRight.lat],
236
+ [bottomLeft.lng, bottomLeft.lat],
237
+ [topLeft.lng, topLeft.lat],
238
+ ],
239
+ ],
240
+ },
241
+ properties: { bearing: getTargetRotationAngle(targetRef.current) },
242
+ } as Feature;
243
+ props.geojsonRef.current = _geoJson;
244
+ }
245
+
246
+ return undefined;
247
+ }, [
248
+ mapHook,
249
+ transform,
250
+ props.options.orientation,
251
+ props.geojsonRef,
252
+ mapState,
253
+ targetRef.current,
254
+ transformOrigin,
255
+ ]);
256
+
257
+ return mapContainerRef.current ? ReactDOM.createPortal(
344
258
  <>
345
- {geojsonProps?.geojson?.bbox && (
346
- <>
347
- <MlGeoJsonLayer
348
- paint={{ 'line-color': '#616161', 'line-width': 4 }}
349
- type="line"
350
- layerId="pdfPreviewGeojsonOutline"
351
- geojson={geojsonProps.geojson}
352
- />
353
- <MlGeoJsonLayer
354
- paint={{ 'fill-opacity': 0 }}
355
- type="fill"
356
- layerId="pdfPreviewGeojson"
357
- geojson={geojsonProps.geojson}
358
- />
359
- <MlGeoJsonLayer
360
- layerId="pdfPreviewGeojsonResizeHandle"
361
- paint={{
362
- 'circle-radius': 10,
363
- 'circle-color': '#1976d2',
364
- 'circle-stroke-width': 2,
365
- 'circle-stroke-color': '#ffffff',
366
- }}
367
- geojson={{
368
- type: 'Feature',
369
- geometry: {
370
- type: 'Point',
371
- //coordinates: [geojsonProps.geojson.bbox[2], geojsonProps.geojson.bbox[3]],
372
- coordinates: geojsonProps.geojson.geometry.coordinates[0][2],
373
- },
374
- properties: {},
375
- }}
376
- />
377
- <MlGeoJsonLayer
378
- layerId="pdfPreviewGeojsonRotationHandle"
379
- paint={{
380
- 'circle-radius': 10,
381
- 'circle-color': '#86dd71',
382
- 'circle-stroke-width': 2,
383
- 'circle-stroke-color': '#ffffff',
384
- }}
385
- geojson={{
386
- type: 'Feature',
387
- geometry: {
388
- type: 'Point',
389
- //coordinates: [geojsonProps.geojson.bbox[0], geojsonProps.geojson.bbox[3]],
390
- coordinates: geojsonProps.geojson.geometry.coordinates[0][3],
391
- },
392
- properties: {},
393
- }}
394
- />
395
- </>
396
- )}
397
- </>
398
- );
259
+ <div
260
+ className="target"
261
+ ref={targetRef}
262
+ style={{ transform: transform, transformOrigin: 'center center' }}
263
+ ></div>
264
+ <Moveable
265
+ // eslint-disable-next-line
266
+ // @ts-ignore:
267
+ ref={moveableRef}
268
+ target={targetRef}
269
+ container={null}
270
+ origin={true}
271
+ keepRatio={true}
272
+ /* draggable */
273
+ draggable={true}
274
+ onDrag={(e) => {
275
+ if (mapHook.map) {
276
+
277
+ let _transformParts = e.transform.split('translate(');
278
+ _transformParts = _transformParts[1].split('px)')[0].split('px, ');
279
+ const _center = mapHook.map?.map.unproject([
280
+ parseInt(_transformParts[0]) + transformOrigin[0],
281
+ parseInt(_transformParts[1]) + transformOrigin[1],
282
+ ]);
283
+ props.setOptions((val: PdfPreviewOptions) => ({
284
+ ...val,
285
+ center: [_center.lng, _center.lat],
286
+ }));
287
+ }
288
+ }}
289
+ /* scalable */
290
+ scalable={props.options.fixedScale ? false : true}
291
+ onScale={(e) => {
292
+ if (mapHook.map) {
293
+ let _transformParts = e.drag.transform.split('scale(');
294
+ _transformParts = _transformParts[1].split(')')[0].split(', ');
295
+
296
+ const centerInPixels = mapHook.map.map.project(props.options.center as LngLatLike);
297
+
298
+ const x = centerInPixels.x;
299
+ const y = centerInPixels.y;
300
+
301
+ const scale =
302
+ parseFloat(_transformParts[0]) *
303
+ (1 / getMapZoomScaleModifier([x, y], mapHook.map.map));
304
+
305
+
306
+ props.setOptions((val: PdfPreviewOptions) => ({ ...val, scale: [scale, scale] }));
307
+ }
308
+ }}
309
+ /* rotatable */
310
+ rotatable={true}
311
+ onRotate={(e) => {
312
+ if (mapHook.map && mapState.viewport) {
313
+ const _transformParts = e.drag.transform.split('rotate(');
314
+ const _transformPartString = _transformParts[1].split('deg)')[0];
315
+ const viewportBearing = mapState?.viewport?.bearing ? mapState.viewport.bearing : 0;
316
+
317
+ props.setOptions((val: PdfPreviewOptions) => ({
318
+ ...val,
319
+ rotate: parseFloat(_transformPartString) + viewportBearing,
320
+ }));
321
+ }
322
+ }}
323
+ />
324
+ </>,
325
+ mapContainerRef.current
326
+ ):<></>;
399
327
  }