@vcmap/core 5.0.0-rc.22 → 5.0.0-rc.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/index.d.ts +631 -98
  2. package/index.js +24 -9
  3. package/package.json +2 -2
  4. package/src/category/category.js +1 -1
  5. package/src/featureProvider/abstractFeatureProvider.js +1 -18
  6. package/src/featureProvider/wmsFeatureProvider.js +1 -1
  7. package/src/interaction/eventHandler.js +14 -0
  8. package/src/layer/cesium/clusterContext.js +12 -0
  9. package/src/layer/cesium/vectorCesiumImpl.js +2 -2
  10. package/src/layer/cesium/vectorContext.js +115 -7
  11. package/src/layer/cesiumTilesetLayer.js +0 -14
  12. package/src/layer/czmlLayer.js +1 -1
  13. package/src/layer/dataSourceLayer.js +1 -53
  14. package/src/layer/featureLayer.js +0 -44
  15. package/src/layer/featureStoreLayer.js +0 -15
  16. package/src/layer/layer.js +0 -11
  17. package/src/layer/vectorHelpers.js +0 -85
  18. package/src/layer/vectorLayer.js +0 -9
  19. package/src/layer/vectorProperties.js +150 -8
  20. package/src/layer/vectorTileLayer.js +0 -9
  21. package/src/layer/wmsHelpers.js +2 -0
  22. package/src/map/baseOLMap.js +12 -6
  23. package/src/map/cesiumMap.js +48 -38
  24. package/src/map/openlayersMap.js +6 -5
  25. package/src/map/vcsMap.js +22 -0
  26. package/src/style/arcStyle.js +316 -0
  27. package/src/style/arrowStyle.js +269 -0
  28. package/src/util/editor/createFeatureSession.js +3 -1
  29. package/src/util/editor/editFeaturesSession.js +315 -0
  30. package/src/util/editor/editGeometrySession.js +5 -1
  31. package/src/util/editor/editorHelpers.js +118 -14
  32. package/src/util/editor/editorSessionHelpers.js +12 -0
  33. package/src/util/editor/editorSymbols.js +6 -0
  34. package/src/util/editor/interactions/editFeaturesMouseOverInteraction.js +120 -0
  35. package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +1 -3
  36. package/src/util/editor/interactions/ensureHandlerSelectionInteraction.js +48 -0
  37. package/src/util/editor/interactions/mapInteractionController.js +5 -2
  38. package/src/util/editor/interactions/selectMultiFeatureInteraction.js +146 -0
  39. package/src/util/editor/interactions/translateVertexInteraction.js +2 -2
  40. package/src/util/editor/transformation/create2DHandlers.js +294 -0
  41. package/src/util/editor/transformation/create3DHandlers.js +575 -0
  42. package/src/util/editor/transformation/extrudeInteraction.js +91 -0
  43. package/src/util/editor/transformation/rotateInteraction.js +188 -0
  44. package/src/util/editor/transformation/scaleInteraction.js +185 -0
  45. package/src/util/editor/transformation/transformationHandler.js +168 -0
  46. package/src/util/editor/transformation/transformationTypes.js +83 -0
  47. package/src/util/editor/transformation/translateInteraction.js +209 -0
  48. package/src/util/featureconverter/arcToCesium.js +87 -0
  49. package/src/util/featureconverter/convert.js +7 -1
  50. package/src/util/featureconverter/extent3D.js +64 -1
  51. package/src/util/featureconverter/lineStringToCesium.js +103 -2
  52. package/src/util/featureconverter/pointHelpers.js +341 -0
  53. package/src/util/featureconverter/pointToCesium.js +27 -76
  54. package/src/util/geometryHelpers.js +11 -8
  55. package/src/util/mapCollection.js +25 -0
  56. package/src/util/math.js +99 -2
  57. package/tests/unit/helpers/cesiumHelpers.js +17 -5
  58. package/tests/unit/helpers/helpers.js +13 -0
  59. package/src/featureProvider/featureProviderHelpers.js +0 -50
@@ -27,6 +27,7 @@ import {
27
27
  KeyboardEventModifier,
28
28
  ScreenSpaceEventType,
29
29
  Cesium3DTileset,
30
+ Cartographic,
30
31
  } from '@vcmap/cesium';
31
32
 
32
33
  import { check, checkMaybe } from '@vcsuite/check';
@@ -50,6 +51,12 @@ import { mapClassRegistry } from '../classRegistry.js';
50
51
  * @api
51
52
  */
52
53
 
54
+ /**
55
+ * @typedef {Object} CesiumMapEvent
56
+ * @property {import("@vcmap/cesium").Scene} scene
57
+ * @property {import("@vcmap/cesium").JulianDate} time
58
+ */
59
+
53
60
  /**
54
61
  * Ensures, a primitive/imageryLayer/entity is part of a collection and placed at the correct location
55
62
  * @param {import("@vcmap/cesium").PrimitiveCollection|import("@vcmap/cesium").ImageryLayerCollection} cesiumCollection
@@ -315,21 +322,11 @@ class CesiumMap extends VcsMap {
315
322
  * @private
316
323
  */
317
324
  this._cameraLimiterOptions = options.cameraLimiter || defaultOptions.cameraLimiter;
318
- /**
319
- * @type {Function}
320
- * @private
321
- */
322
- this._terrainProviderChangedListener = null;
323
325
  /**
324
326
  * @type {Function}
325
327
  * @private
326
328
  */
327
329
  this._preUpdateListener = null;
328
- /**
329
- * @type {Function}
330
- * @private
331
- */
332
- this._clockTickListener = null;
333
330
  /**
334
331
  * @type {Function}
335
332
  * @private
@@ -337,10 +334,10 @@ class CesiumMap extends VcsMap {
337
334
  this._clockSyncListener = null;
338
335
 
339
336
  /**
340
- * @type {Function}
337
+ * @type {Array<function():void>}
341
338
  * @private
342
339
  */
343
- this._removeClusterClockTickListener = null;
340
+ this._listeners = [];
344
341
 
345
342
  /**
346
343
  * @type {boolean}
@@ -513,11 +510,11 @@ class CesiumMap extends VcsMap {
513
510
 
514
511
  const { clock } = this._cesiumWidget;
515
512
  clock.shouldAnimate = true;
516
- this._clockTickListener = clock.onTick.addEventListener(() => {
513
+ this._listeners.push(clock.onTick.addEventListener(() => {
517
514
  this.dataSourceDisplayClock.tick();
518
515
  const time = this.dataSourceDisplayClock.currentTime;
519
516
  this.dataSourceDisplay.update(time);
520
- });
517
+ }));
521
518
 
522
519
  // deactivate cesium Requestthrottling let the browser manage that
523
520
  // RequestScheduler.throttleRequests = false;
@@ -556,8 +553,12 @@ class CesiumMap extends VcsMap {
556
553
 
557
554
  this.defaultTerrainProvider = this._cesiumWidget.scene.terrainProvider;
558
555
  this._terrainProvider = this.defaultTerrainProvider;
559
- this._terrainProviderChangedListener =
560
- this._cesiumWidget.scene.terrainProviderChanged.addEventListener(this._terrainProviderChanged.bind(this));
556
+ this._listeners.push(this._cesiumWidget.scene.terrainProviderChanged
557
+ .addEventListener(this._terrainProviderChanged.bind(this)));
558
+
559
+ this._listeners.push(this._cesiumWidget.scene.postRender.addEventListener((eventScene, time) => {
560
+ this.postRender.raiseEvent({ map: this, originalEvent: { scene: eventScene, time } });
561
+ }));
561
562
  if (this._debug) {
562
563
  this._setDebug();
563
564
  }
@@ -739,15 +740,14 @@ class CesiumMap extends VcsMap {
739
740
  }
740
741
 
741
742
  /**
742
- * @inheritDoc
743
- * @param {import("ol/coordinate").Coordinate} coordinate - in mercator
743
+ * @param {import("@vcmap/cesium").Cartesian3} cartesian
744
+ * @param {number} latitude - in radians
744
745
  * @returns {number}
746
+ * @private
745
747
  */
746
- getCurrentResolution(coordinate) {
748
+ _getCurrentResolutionFromCartesianLatitude(cartesian, latitude) {
747
749
  const cam = this._cesiumWidget.scene.camera;
748
- const wgs84Coordinate = Projection.mercatorToWgs84(coordinate);
749
- const distance = Cartesian3
750
- .distance(Cartesian3.fromDegrees(wgs84Coordinate[0], wgs84Coordinate[1], wgs84Coordinate[2]), cam.position);
750
+ const distance = Cartesian3.distance(cartesian, cam.position);
751
751
 
752
752
  const fov = Math.PI / 3.0;
753
753
  const width = this.mapElement.offsetWidth;
@@ -755,12 +755,31 @@ class CesiumMap extends VcsMap {
755
755
  const aspectRatio = width / height;
756
756
  const fovy = Math.atan(Math.tan(fov * 0.5) / aspectRatio) * 2.0;
757
757
  const visibleMeters = 2 * distance * Math.tan(fovy / 2);
758
- const relativeCircumference = Math.cos(Math.abs(CesiumMath.toRadians(wgs84Coordinate[1])));
758
+ const relativeCircumference = Math.cos(Math.abs(latitude));
759
759
  const visibleMapUnits = visibleMeters / relativeCircumference;
760
760
 
761
761
  return visibleMapUnits / height;
762
762
  }
763
763
 
764
+ /**
765
+ * @inheritDoc
766
+ * @param {import("ol/coordinate").Coordinate} coordinate - in mercator
767
+ * @returns {number}
768
+ */
769
+ getCurrentResolution(coordinate) {
770
+ const wgs84Coordinate = Projection.mercatorToWgs84(coordinate);
771
+ const cartesian = Cartesian3.fromDegrees(wgs84Coordinate[0], wgs84Coordinate[1], wgs84Coordinate[2]);
772
+ return this._getCurrentResolutionFromCartesianLatitude(cartesian, CesiumMath.toRadians(wgs84Coordinate[1]));
773
+ }
774
+
775
+ /**
776
+ * @param {import("@vcmap/cesium").Cartesian3} cartesian
777
+ * @returns {number}
778
+ */
779
+ getCurrentResolutionFromCartesian(cartesian) {
780
+ return this._getCurrentResolutionFromCartesianLatitude(cartesian, Cartographic.fromCartesian(cartesian).latitude);
781
+ }
782
+
764
783
  /**
765
784
  * @param {boolean} bool
766
785
  * @inheritDoc
@@ -880,10 +899,9 @@ class CesiumMap extends VcsMap {
880
899
  visualizersCallback,
881
900
  });
882
901
 
883
- this._removeClusterClockTickListener =
884
- this._cesiumWidget.clock.onTick.addEventListener((clock) => {
885
- this._clusterDataSourceDisplay.update(clock.currentTime);
886
- });
902
+ this._listeners.push(this._cesiumWidget.clock.onTick.addEventListener((clock) => {
903
+ this._clusterDataSourceDisplay.update(clock.currentTime);
904
+ }));
887
905
 
888
906
  return dataSourceCollection;
889
907
  }
@@ -1132,17 +1150,12 @@ class CesiumMap extends VcsMap {
1132
1150
  this.screenSpaceEventHandler.destroy();
1133
1151
  this.screenSpaceEventHandler = null;
1134
1152
  }
1135
- if (this._terrainProviderChangedListener) {
1136
- this._terrainProviderChangedListener();
1137
- this._terrainProviderChangedListener = null;
1138
- }
1153
+ this._listeners.forEach((cb) => { cb(); });
1154
+ this._listeners = [];
1155
+
1139
1156
  this._terrainProvider = null;
1140
1157
  this.defaultTerrainProvider = null;
1141
1158
 
1142
- if (this._clockTickListener) {
1143
- this._clockTickListener();
1144
- this._clockTickListener = null;
1145
- }
1146
1159
 
1147
1160
  if (this._clockSyncListener) {
1148
1161
  this._clockSyncListener();
@@ -1157,9 +1170,6 @@ class CesiumMap extends VcsMap {
1157
1170
  if (this._cameraLimiter) {
1158
1171
  this._cameraLimiter = null;
1159
1172
  }
1160
- if (this._removeClusterClockTickListener) {
1161
- this._removeClusterClockTickListener();
1162
- }
1163
1173
 
1164
1174
  [...this.layerCollection].forEach((l) => { l.removedFromMap(this); });
1165
1175
 
@@ -117,8 +117,9 @@ class OpenlayersMap extends BaseOLMap {
117
117
  if (this.movementDisabled || !viewpoint.isValid()) {
118
118
  return Promise.resolve();
119
119
  }
120
+ let { heading } = viewpoint;
120
121
  if (this.fixedNorthOrientation) {
121
- viewpoint.heading = 0;
122
+ heading = 0;
122
123
  }
123
124
  const view = this.olMap.getView();
124
125
  const fromLatLon = getTransform('EPSG:4326', view.getProjection());
@@ -148,8 +149,8 @@ class OpenlayersMap extends BaseOLMap {
148
149
 
149
150
  if (viewpoint.animate) {
150
151
  let rotation = 0;
151
- if (!this.fixedNorthOrientation && viewpoint.heading != null) {
152
- rotation = -CesiumMath.toRadians(viewpoint.heading);
152
+ if (!this.fixedNorthOrientation && heading != null) {
153
+ rotation = -CesiumMath.toRadians(heading);
153
154
  }
154
155
  return new Promise((resolve) => {
155
156
  view.animate({
@@ -163,8 +164,8 @@ class OpenlayersMap extends BaseOLMap {
163
164
  } else {
164
165
  view.setCenter(center);
165
166
  view.setResolution(resolution);
166
- if (!this.fixedNorthOrientation && viewpoint.heading != null) {
167
- view.setRotation(-CesiumMath.toRadians(viewpoint.heading));
167
+ if (!this.fixedNorthOrientation && heading != null) {
168
+ view.setRotation(-CesiumMath.toRadians(heading));
168
169
  }
169
170
  }
170
171
  return Promise.resolve();
package/src/map/vcsMap.js CHANGED
@@ -31,6 +31,12 @@ import { mapClassRegistry } from '../classRegistry.js';
31
31
  * @api stable
32
32
  */
33
33
 
34
+ /**
35
+ * @typedef {Object} VcsMapRenderEvent
36
+ * @property {VcsMap} map
37
+ * @property {import("ol").MapEvent|CesiumMapEvent} originalEvent
38
+ */
39
+
34
40
  /**
35
41
  * @type {Object}
36
42
  */
@@ -152,6 +158,12 @@ class VcsMap extends VcsObject {
152
158
  * @api
153
159
  */
154
160
  this.splitScreen = null;
161
+
162
+ /**
163
+ * @type {VcsEvent<VcsMapRenderEvent>}
164
+ * @private
165
+ */
166
+ this._postRender = new VcsEvent();
155
167
  }
156
168
 
157
169
  /**
@@ -217,6 +229,15 @@ class VcsMap extends VcsObject {
217
229
  this._setLayerCollectionListeners();
218
230
  }
219
231
 
232
+ /**
233
+ * An event raised on the maps post render
234
+ * @type {VcsEvent<VcsMapRenderEvent>}
235
+ * @readonly
236
+ */
237
+ get postRender() {
238
+ return this._postRender;
239
+ }
240
+
220
241
  /**
221
242
  * @private
222
243
  */
@@ -523,6 +544,7 @@ class VcsMap extends VcsObject {
523
544
  this.pointerInteractionEvent = null;
524
545
  }
525
546
  this._layerCollection = null;
547
+ this._postRender.destroy();
526
548
  }
527
549
  }
528
550
 
@@ -0,0 +1,316 @@
1
+ import { parseInteger, parseNumber } from '@vcsuite/parsers';
2
+ import { check } from '@vcsuite/check';
3
+ import { Cartesian2, Cartesian3, CatmullRomSpline, Matrix3 } from '@vcmap/cesium';
4
+ import { LineString } from 'ol/geom.js';
5
+ import { unByKey } from 'ol/Observable.js';
6
+ import { v4 as uuidv4 } from 'uuid';
7
+ import { cartesian2DDistance, getMidPoint, modulo } from '../util/math.js';
8
+ import ArrowStyle from './arrowStyle.js';
9
+
10
+ /**
11
+ * @typedef {ArrowStyleOptions} ArcStyleOptions
12
+ * @property {number} [arcFactor=0.15] - factor to calculate the 'height' of an arc, based on the distance from start to end
13
+ * @property {number} [numberOfSegments=64] - number of segments to interpolate the arc by
14
+ */
15
+
16
+ /**
17
+ * @typedef {Object} ArcStruct
18
+ * @property {import("ol/geom").LineString} [geometry] - undefined if not an arc
19
+ * @property {Array<import("ol/coordinate").Coordinate>} [coordinates] - undefined if not an arc
20
+ * @property {function():void} destroy
21
+ */
22
+
23
+ /**
24
+ * Added to feature to hold there respective arc structure
25
+ * @type {symbol}
26
+ */
27
+ export const featureArcStruct = Symbol('FeatureArcStruct');
28
+
29
+ /**
30
+ * Added to features to indicate to which arc style there ArcStruct belongs. If the style changes or a new ArcStyle is applied
31
+ * to the feature, its value will change, recalculating the ArcStruct
32
+ * @type {symbol}
33
+ */
34
+ const featureArcStyleId = Symbol('ArcStyleId');
35
+
36
+ /**
37
+ * @param {import("ol/coordinate").Coordinate} p1
38
+ * @param {import("ol/coordinate").Coordinate} p2
39
+ * @param {number} factor
40
+ * @returns {number}
41
+ */
42
+ function determineArcHeight(p1, p2, factor) {
43
+ const distance = cartesian2DDistance(p1, p2);
44
+ return distance * factor;
45
+ }
46
+
47
+ /**
48
+ * Gets the radius of the circle covering p1, p2, p3. see https://math.stackexchange.com/a/1460096
49
+ * @param {import("ol/coordinate").Coordinate} p1
50
+ * @param {import("ol/coordinate").Coordinate} p2
51
+ * @param {import("ol/coordinate").Coordinate} p3
52
+ * @returns {{ center: import("ol/coordinate").Coordinate, radius: number }|null}
53
+ */
54
+ function determineCircle(p1, p2, p3) {
55
+ const m11 = Matrix3.determinant(new Matrix3(
56
+ p1[0], p1[1], 1,
57
+ p2[0], p2[1], 1,
58
+ p3[0], p3[1], 1,
59
+ ));
60
+ if (m11 === 0) {
61
+ return null;
62
+ }
63
+ const m12 = Matrix3.determinant(new Matrix3(
64
+ (p1[0] ** 2) + (p1[1] ** 2), p1[1], 1,
65
+ (p2[0] ** 2) + (p2[1] ** 2), p2[1], 1,
66
+ (p3[0] ** 2) + (p3[1] ** 2), p3[1], 1,
67
+ ));
68
+
69
+ const m13 = Matrix3.determinant(new Matrix3(
70
+ (p1[0] ** 2) + (p1[1] ** 2), p1[0], 1,
71
+ (p2[0] ** 2) + (p2[1] ** 2), p2[0], 1,
72
+ (p3[0] ** 2) + (p3[1] ** 2), p3[0], 1,
73
+ ));
74
+
75
+ const center = [0.5 * (m12 / m11), -0.5 * (m13 / m11)];
76
+
77
+ return {
78
+ center,
79
+ radius: cartesian2DDistance(center, p1),
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Determines the midpoint of a line with a distance. see https://gamedev.stackexchange.com/questions/70075/how-can-i-find-the-perpendicular-to-a-2d-vector
85
+ * @param {import("ol/coordinate").Coordinate} p1
86
+ * @param {import("ol/coordinate").Coordinate} p2
87
+ * @param {number} arcHeight
88
+ * @returns {import("ol/coordinate").Coordinate}
89
+ */
90
+ function getMidPointOnArc(p1, p2, arcHeight) {
91
+ const lineVector = new Cartesian2(p2[0] - p1[0], p2[1] - p1[1]);
92
+ let perp = Cartesian2.normalize(lineVector, new Cartesian2());
93
+ const { x, y } = perp;
94
+ perp = new Cartesian2(y, -x);
95
+ Cartesian2.multiplyByScalar(perp, arcHeight, perp);
96
+ const midPoint = getMidPoint(p1, p2);
97
+ Cartesian2.add(perp, new Cartesian2(midPoint[0], midPoint[1]), perp);
98
+ return [perp.x, perp.y];
99
+ }
100
+
101
+ /**
102
+ * @param {import("ol/coordinate").Coordinate} center
103
+ * @param {import("ol/coordinate").Coordinate} coordinate
104
+ * @param {number} angle
105
+ * @returns {number}
106
+ */
107
+ function determineQuadrantOffset(center, coordinate, angle) {
108
+ if (center[1] <= coordinate[1]) {
109
+ return angle;
110
+ }
111
+ return Math.PI + (Math.PI - angle);
112
+ }
113
+
114
+ /**
115
+ * @param {import("ol/coordinate").Coordinate} p1
116
+ * @param {import("ol/coordinate").Coordinate} p2
117
+ * @param {import("ol/coordinate").Coordinate} center
118
+ * @param {number} radius
119
+ * @param {number} numberOfSegments
120
+ * @returns {Array<import("ol/coordinate").Coordinate>}
121
+ */
122
+ function interpolateBetweenAngles(p1, p2, center, radius, numberOfSegments) {
123
+ const zeroVector = Cartesian2.UNIT_X;
124
+ const p1V = new Cartesian2(p1[0] - center[0], p1[1] - center[1]);
125
+ const p2V = new Cartesian2(p2[0] - center[0], p2[1] - center[1]);
126
+ let startAngle = Cartesian2.angleBetween(zeroVector, p1V);
127
+ startAngle = determineQuadrantOffset(center, p1, startAngle);
128
+ const distance = Cartesian2.angleBetween(p1V, p2V);
129
+ const coordinates = new Array(numberOfSegments);
130
+
131
+ for (let i = 0; i < numberOfSegments; ++i) {
132
+ const angle = startAngle + (modulo(i, numberOfSegments) * distance) / numberOfSegments;
133
+ coordinates[i] = [
134
+ center[0] + radius * Math.cos(angle),
135
+ center[1] + radius * Math.sin(angle),
136
+ 0,
137
+ ];
138
+ }
139
+ coordinates.push([p2[0], p2[1], 0]);
140
+ return coordinates;
141
+ }
142
+
143
+ /**
144
+ * @param {import("ol/coordinate").Coordinate} p1
145
+ * @param {import("ol/coordinate").Coordinate} p2
146
+ * @param {number} arcHeight
147
+ * @param {number} numberOfSegments
148
+ * @param {number} arcFactor
149
+ * @returns {Array<import("ol/coordinate").Coordinate>}
150
+ */
151
+ function getArcCoordinates(p1, p2, arcHeight, numberOfSegments, arcFactor) {
152
+ const midPoint = getMidPoint(p1, p2);
153
+ midPoint[2] += (arcHeight / 2);
154
+ const distance = cartesian2DDistance(p1, p2) / numberOfSegments;
155
+ const spline = new CatmullRomSpline({
156
+ times: [0, 0.5, 1],
157
+ points: [
158
+ new Cartesian3(p1[0], p1[1], p1[2] ?? 0),
159
+ new Cartesian3(midPoint[0], midPoint[1], midPoint[2] ?? 0),
160
+ new Cartesian3(p2[0], p2[1], p2[2] ?? 0),
161
+ ],
162
+ firstTangent: new Cartesian3(0, 0, arcFactor * distance),
163
+ lastTangent: new Cartesian3(0, 0, -arcFactor * distance),
164
+ });
165
+
166
+ const coordinates = new Array(numberOfSegments + 1);
167
+ let scratchCartesian = new Cartesian3();
168
+ for (let i = 0; i <= numberOfSegments; i++) {
169
+ scratchCartesian = spline.evaluate(i / numberOfSegments, scratchCartesian);
170
+ coordinates[i] = [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z];
171
+ }
172
+ return coordinates;
173
+ }
174
+
175
+ /**
176
+ * Set the ArcStruct on the feature
177
+ * @param {number} arcFactor
178
+ * @param {import("ol").Feature} feature
179
+ * @param {number} numberOfSegments
180
+ */
181
+ function createFeatureArc(arcFactor, feature, numberOfSegments) {
182
+ if (feature[featureArcStruct]) {
183
+ feature[featureArcStruct].destroy();
184
+ }
185
+ const geometry = feature.getGeometry();
186
+ const listeners = [];
187
+ const destroy = () => {
188
+ unByKey(listeners);
189
+ };
190
+ listeners.push(feature.on('change:geometry', () => { createFeatureArc(arcFactor, feature, numberOfSegments); }));
191
+
192
+ if (geometry instanceof LineString) {
193
+ listeners.push(geometry.on('change', () => { createFeatureArc(arcFactor, feature, numberOfSegments); }));
194
+ const p1 = geometry.getFirstCoordinate();
195
+ const p2 = geometry.getLastCoordinate();
196
+ const arcHeight = determineArcHeight(p1, p2, arcFactor);
197
+ const midPoint = getMidPointOnArc(p1, p2, arcHeight);
198
+ const { center, radius } = determineCircle(p1, p2, midPoint);
199
+
200
+ const coordinates = interpolateBetweenAngles(p1, p2, center, radius, numberOfSegments);
201
+ feature[featureArcStruct] = {
202
+ geometry: new LineString(coordinates),
203
+ coordinates: getArcCoordinates(p1, p2, arcHeight, numberOfSegments, arcFactor),
204
+ destroy,
205
+ };
206
+ } else {
207
+ feature[featureArcStruct] = {
208
+ geometry,
209
+ destroy,
210
+ };
211
+ }
212
+ }
213
+
214
+ /**
215
+ * A style which applies an arc to LineString geometries depending on their first and last coordinates.
216
+ * All other coordinates will be ignored. This still will
217
+ * not render non-LineString geometries (same as {@see ArrowStyle}).
218
+ * @class
219
+ * @extends {ArrowStyle}
220
+ */
221
+ class ArcStyle extends ArrowStyle {
222
+ /**
223
+ * @param {ArcStyleOptions} [options={}]
224
+ */
225
+ constructor(options = {}) {
226
+ super(options);
227
+ /**
228
+ * @type {number}
229
+ * @private
230
+ */
231
+ this._arcFactor = parseNumber(options.arcFactor, 0.15);
232
+ /**
233
+ * This styles revision ID. This will enforce an update of a features arc struct, if the feature get applied a new ArcStyle
234
+ * @type {string}
235
+ * @private
236
+ */
237
+ this._revisionId = uuidv4();
238
+ /**
239
+ * The number of line segments to interpolate by
240
+ * @type {number}
241
+ * @private
242
+ */
243
+ this._numberOfSegments = parseInteger(options.numberOfSegments, 64);
244
+
245
+ this.setGeometry(this._getFeatureArcGeometry.bind(this));
246
+ }
247
+
248
+ /**
249
+ * The number of segments to render the arc with.
250
+ * @type {number}
251
+ */
252
+ get numberOfSegments() {
253
+ return this._numberOfSegments;
254
+ }
255
+
256
+ /**
257
+ * @param {number} value
258
+ */
259
+ set numberOfSegments(value) {
260
+ check(value, Number);
261
+ if (value !== this._numberOfSegments && value > 0 && Number.isInteger(value)) {
262
+ this._numberOfSegments = value;
263
+ this._revisionId = uuidv4();
264
+ }
265
+ }
266
+
267
+ /**
268
+ * The factor with which to calculate the 'height' of an arc using the distance from start to end of the LineString.
269
+ * @type {number}
270
+ */
271
+ get arcFactor() { return this._arcFactor; }
272
+
273
+ /**
274
+ * @param {number} value
275
+ */
276
+ set arcFactor(value) {
277
+ check(value, Number);
278
+ if (value !== this._arcFactor) {
279
+ this._arcFactor = value;
280
+ this._revisionId = uuidv4();
281
+ }
282
+ }
283
+
284
+ /**
285
+ * @param {import("ol").Feature} feature
286
+ * @returns {import("ol/geom").LineString}
287
+ * @private
288
+ */
289
+ _getFeatureArcGeometry(feature) {
290
+ if (!feature[featureArcStruct] || feature[featureArcStyleId] !== this._revisionId) {
291
+ createFeatureArc(this._arcFactor, feature, this._numberOfSegments);
292
+ feature[featureArcStyleId] = this._revisionId;
293
+ }
294
+ return feature[featureArcStruct].geometry;
295
+ }
296
+
297
+ /**
298
+ * @returns {ArcStyleOptions}
299
+ * @protected
300
+ */
301
+ _getCloneOptions() {
302
+ const options = /** @type {ArcStyleOptions} */ (super._getCloneOptions());
303
+ options.arcFactor = this._arcFactor;
304
+ options.numberOfSegments = this._numberOfSegments;
305
+ return options;
306
+ }
307
+
308
+ /**
309
+ * @returns {ArcStyle}
310
+ */
311
+ clone() {
312
+ return new ArcStyle(this._getCloneOptions());
313
+ }
314
+ }
315
+
316
+ export default ArcStyle;