@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.
- package/index.d.ts +631 -98
- package/index.js +24 -9
- package/package.json +2 -2
- package/src/category/category.js +1 -1
- package/src/featureProvider/abstractFeatureProvider.js +1 -18
- package/src/featureProvider/wmsFeatureProvider.js +1 -1
- package/src/interaction/eventHandler.js +14 -0
- package/src/layer/cesium/clusterContext.js +12 -0
- package/src/layer/cesium/vectorCesiumImpl.js +2 -2
- package/src/layer/cesium/vectorContext.js +115 -7
- package/src/layer/cesiumTilesetLayer.js +0 -14
- package/src/layer/czmlLayer.js +1 -1
- package/src/layer/dataSourceLayer.js +1 -53
- package/src/layer/featureLayer.js +0 -44
- package/src/layer/featureStoreLayer.js +0 -15
- package/src/layer/layer.js +0 -11
- package/src/layer/vectorHelpers.js +0 -85
- package/src/layer/vectorLayer.js +0 -9
- package/src/layer/vectorProperties.js +150 -8
- package/src/layer/vectorTileLayer.js +0 -9
- package/src/layer/wmsHelpers.js +2 -0
- package/src/map/baseOLMap.js +12 -6
- package/src/map/cesiumMap.js +48 -38
- package/src/map/openlayersMap.js +6 -5
- package/src/map/vcsMap.js +22 -0
- package/src/style/arcStyle.js +316 -0
- package/src/style/arrowStyle.js +269 -0
- package/src/util/editor/createFeatureSession.js +3 -1
- package/src/util/editor/editFeaturesSession.js +315 -0
- package/src/util/editor/editGeometrySession.js +5 -1
- package/src/util/editor/editorHelpers.js +118 -14
- package/src/util/editor/editorSessionHelpers.js +12 -0
- package/src/util/editor/editorSymbols.js +6 -0
- package/src/util/editor/interactions/editFeaturesMouseOverInteraction.js +120 -0
- package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +1 -3
- package/src/util/editor/interactions/ensureHandlerSelectionInteraction.js +48 -0
- package/src/util/editor/interactions/mapInteractionController.js +5 -2
- package/src/util/editor/interactions/selectMultiFeatureInteraction.js +146 -0
- package/src/util/editor/interactions/translateVertexInteraction.js +2 -2
- package/src/util/editor/transformation/create2DHandlers.js +294 -0
- package/src/util/editor/transformation/create3DHandlers.js +575 -0
- package/src/util/editor/transformation/extrudeInteraction.js +91 -0
- package/src/util/editor/transformation/rotateInteraction.js +188 -0
- package/src/util/editor/transformation/scaleInteraction.js +185 -0
- package/src/util/editor/transformation/transformationHandler.js +168 -0
- package/src/util/editor/transformation/transformationTypes.js +83 -0
- package/src/util/editor/transformation/translateInteraction.js +209 -0
- package/src/util/featureconverter/arcToCesium.js +87 -0
- package/src/util/featureconverter/convert.js +7 -1
- package/src/util/featureconverter/extent3D.js +64 -1
- package/src/util/featureconverter/lineStringToCesium.js +103 -2
- package/src/util/featureconverter/pointHelpers.js +341 -0
- package/src/util/featureconverter/pointToCesium.js +27 -76
- package/src/util/geometryHelpers.js +11 -8
- package/src/util/mapCollection.js +25 -0
- package/src/util/math.js +99 -2
- package/tests/unit/helpers/cesiumHelpers.js +17 -5
- package/tests/unit/helpers/helpers.js +13 -0
- package/src/featureProvider/featureProviderHelpers.js +0 -50
package/src/map/cesiumMap.js
CHANGED
|
@@ -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 {
|
|
337
|
+
* @type {Array<function():void>}
|
|
341
338
|
* @private
|
|
342
339
|
*/
|
|
343
|
-
this.
|
|
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.
|
|
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.
|
|
560
|
-
|
|
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
|
-
* @
|
|
743
|
-
* @param {
|
|
743
|
+
* @param {import("@vcmap/cesium").Cartesian3} cartesian
|
|
744
|
+
* @param {number} latitude - in radians
|
|
744
745
|
* @returns {number}
|
|
746
|
+
* @private
|
|
745
747
|
*/
|
|
746
|
-
|
|
748
|
+
_getCurrentResolutionFromCartesianLatitude(cartesian, latitude) {
|
|
747
749
|
const cam = this._cesiumWidget.scene.camera;
|
|
748
|
-
const
|
|
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(
|
|
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.
|
|
884
|
-
this.
|
|
885
|
-
|
|
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
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
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
|
|
package/src/map/openlayersMap.js
CHANGED
|
@@ -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
|
-
|
|
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 &&
|
|
152
|
-
rotation = -CesiumMath.toRadians(
|
|
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 &&
|
|
167
|
-
view.setRotation(-CesiumMath.toRadians(
|
|
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;
|