@vcmap/core 5.0.0-rc.23 → 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 +597 -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/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/map/cesiumMap.js +26 -7
- 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/math.js +99 -2
- package/tests/unit/helpers/cesiumHelpers.js +14 -4
- package/tests/unit/helpers/helpers.js +13 -0
- package/src/featureProvider/featureProviderHelpers.js +0 -50
|
@@ -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;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Cartesian3 } from '@vcmap/cesium';
|
|
2
|
+
import {
|
|
3
|
+
Circle,
|
|
4
|
+
Fill,
|
|
5
|
+
Icon,
|
|
6
|
+
Stroke,
|
|
7
|
+
Style,
|
|
8
|
+
Image as OlImage,
|
|
9
|
+
} from 'ol/style.js';
|
|
10
|
+
import { parseEnumValue, parseNumber } from '@vcsuite/parsers';
|
|
11
|
+
import { getCartesianBearing } from '../util/math.js';
|
|
12
|
+
import { PrimitiveOptionsType } from '../layer/vectorProperties.js';
|
|
13
|
+
import { getStringColor, parseColor } from './styleHelpers.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} ArrowStyleOptions
|
|
17
|
+
* @property {import("ol/color").Color|import("ol/colorlike").ColorLike} [color='#000000'] - color used to color in the line & the icon
|
|
18
|
+
* @property {number} [width=1] - passed to ol.Style.Stroke
|
|
19
|
+
* @property {number} [zIndex] - passed to ol.Style
|
|
20
|
+
* @property {import("ol/style/Icon").Options|import("ol/style").Image} [arrowIcon] - icon options to use. if none are provided, if is attempted to derive the arrow icon from the primitive options. if providing your own icon, the color will not be options.color by default
|
|
21
|
+
* @property {VectorPropertiesPrimitiveOptions} [primitiveOptions] - the default primitive options are a cylinder with a bottom radius of 1/3 its length and 0 top radius
|
|
22
|
+
* @property {ArrowEnd} [end=ArrowEnd.END] - end to place the arrow head at
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @enum {string}
|
|
27
|
+
*/
|
|
28
|
+
export const ArrowEnd = {
|
|
29
|
+
NONE: 'none',
|
|
30
|
+
BOTH: 'both',
|
|
31
|
+
START: 'start',
|
|
32
|
+
END: 'end',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {VectorPropertiesPrimitiveOptions} primitiveOptions
|
|
37
|
+
* @param {number} [twoDFactor=1.5]
|
|
38
|
+
* @returns {string}
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
export function getDefaultArrowIconSrc(primitiveOptions, twoDFactor = 1.5) {
|
|
42
|
+
let height = 13;
|
|
43
|
+
let width = 13;
|
|
44
|
+
let points = [[0, 13], [13, 13], [6, 0]];
|
|
45
|
+
|
|
46
|
+
if (primitiveOptions.type === PrimitiveOptionsType.SPHERE) {
|
|
47
|
+
const radius = Math.floor((primitiveOptions.geometryOptions?.radius ?? 1) * twoDFactor);
|
|
48
|
+
return `<svg height="${radius * 2}" width="${radius * 2}" xmlns="http://www.w3.org/2000/svg">
|
|
49
|
+
<circle cx="${radius}" cy="${radius}" r="${radius}" style="fill:white;" />
|
|
50
|
+
</svg>`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
primitiveOptions.type === PrimitiveOptionsType.BOX &&
|
|
55
|
+
primitiveOptions.geometryOptions.minimum &&
|
|
56
|
+
primitiveOptions.geometryOptions.maximum
|
|
57
|
+
) {
|
|
58
|
+
const min = Array.isArray(primitiveOptions.geometryOptions.minimum) ?
|
|
59
|
+
primitiveOptions.geometryOptions.minimum :
|
|
60
|
+
Cartesian3.pack(primitiveOptions.geometryOptions.minimum, []);
|
|
61
|
+
|
|
62
|
+
const max = Array.isArray(primitiveOptions.geometryOptions.maximum) ?
|
|
63
|
+
primitiveOptions.geometryOptions.maximum :
|
|
64
|
+
Cartesian3.pack(primitiveOptions.geometryOptions.maximum, []);
|
|
65
|
+
|
|
66
|
+
width = Math.floor((max[0] - min[0]) * twoDFactor);
|
|
67
|
+
height = Math.floor((max[1] - min[1]) * twoDFactor);
|
|
68
|
+
|
|
69
|
+
points = [
|
|
70
|
+
[0, 0],
|
|
71
|
+
[width, 0],
|
|
72
|
+
[width, height],
|
|
73
|
+
[0, height],
|
|
74
|
+
];
|
|
75
|
+
} else if (
|
|
76
|
+
primitiveOptions.type === PrimitiveOptionsType.CYLINDER &&
|
|
77
|
+
primitiveOptions.geometryOptions.length
|
|
78
|
+
) {
|
|
79
|
+
const topRadius = Math.floor(primitiveOptions.geometryOptions.topRadius * twoDFactor);
|
|
80
|
+
const bottomRadius = Math.floor(primitiveOptions.geometryOptions.bottomRadius * twoDFactor);
|
|
81
|
+
const maxRadius = Math.max(topRadius, bottomRadius);
|
|
82
|
+
|
|
83
|
+
height = Math.floor(primitiveOptions.geometryOptions.length * twoDFactor);
|
|
84
|
+
width = maxRadius * 2;
|
|
85
|
+
|
|
86
|
+
points = [
|
|
87
|
+
[(width / 2) - bottomRadius, height],
|
|
88
|
+
[(width / 2) + bottomRadius, height],
|
|
89
|
+
[(width / 2) + topRadius, 0],
|
|
90
|
+
[(width / 2) - topRadius, 0],
|
|
91
|
+
];
|
|
92
|
+
if (bottomRadius === 0) {
|
|
93
|
+
points.splice(1, 1);
|
|
94
|
+
} else if (topRadius === 0) {
|
|
95
|
+
points.splice(2, 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return `<svg height="${height}" width="${width}" xmlns="http://www.w3.org/2000/svg"><polygon points="${points.map(p => p.join(',')).join(' ')}" style="fill:white;" /></svg>`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @returns {VectorPropertiesPrimitiveOptions}
|
|
103
|
+
*/
|
|
104
|
+
function getDefaultArrowPrimitive() {
|
|
105
|
+
return {
|
|
106
|
+
type: PrimitiveOptionsType.CYLINDER,
|
|
107
|
+
geometryOptions: {
|
|
108
|
+
length: 9,
|
|
109
|
+
bottomRadius: 3,
|
|
110
|
+
topRadius: 0,
|
|
111
|
+
},
|
|
112
|
+
offset: [0, 0, -4.3],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A style which renders arrow heads at the ends of a line string. This style cannot be applied to non-LineString geometries.
|
|
118
|
+
* When setting this on a layer with heterogeneous geometry types, use a style function.
|
|
119
|
+
* @class
|
|
120
|
+
* @extends {Style}
|
|
121
|
+
*/
|
|
122
|
+
class ArrowStyle extends Style {
|
|
123
|
+
/**
|
|
124
|
+
* @param {ArrowStyleOptions} [options={}]
|
|
125
|
+
*/
|
|
126
|
+
constructor(options = {}) {
|
|
127
|
+
const color = options.color ?? '#000000';
|
|
128
|
+
const styleOptions = {
|
|
129
|
+
stroke: new Stroke({
|
|
130
|
+
color,
|
|
131
|
+
width: parseNumber(options.width, 1),
|
|
132
|
+
}),
|
|
133
|
+
zIndex: options.zIndex,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const primitiveOptions = options.primitiveOptions ?? getDefaultArrowPrimitive();
|
|
137
|
+
const iconOptions = options.arrowIcon ?? {
|
|
138
|
+
src: `data:image/svg+xml,${encodeURIComponent(getDefaultArrowIconSrc(primitiveOptions))}`,
|
|
139
|
+
color: parseColor(color),
|
|
140
|
+
};
|
|
141
|
+
styleOptions.image = iconOptions instanceof OlImage ? iconOptions : new Icon(iconOptions);
|
|
142
|
+
super(styleOptions);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @type {VectorPropertiesPrimitiveOptions}
|
|
146
|
+
*/
|
|
147
|
+
this.primitiveOptions = primitiveOptions;
|
|
148
|
+
/**
|
|
149
|
+
* @type {ArrowEnd}
|
|
150
|
+
*/
|
|
151
|
+
this.end = parseEnumValue(options.end, ArrowEnd, ArrowEnd.END);
|
|
152
|
+
this.setRenderer(this._render.bind(this));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Same as getStroke().getWidth() / getStroke().setWidth()
|
|
157
|
+
* @type {number}
|
|
158
|
+
*/
|
|
159
|
+
get width() {
|
|
160
|
+
return this.getStroke().getWidth();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {number} width
|
|
165
|
+
*/
|
|
166
|
+
set width(width) {
|
|
167
|
+
this.getStroke().setWidth(width);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The color of the stroke and icon styles. Setting the color will not re-apply the icons color.
|
|
172
|
+
* @type {import("ol/color").Color|import("ol/colorlike").ColorLike}
|
|
173
|
+
*/
|
|
174
|
+
get color() {
|
|
175
|
+
return this.getStroke().getColor();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {import("ol/color").Color|import("ol/colorlike").ColorLike} color
|
|
180
|
+
*/
|
|
181
|
+
set color(color) {
|
|
182
|
+
this.getStroke().setColor(color);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @param {CanvasRenderingContext2D} ctx
|
|
187
|
+
* @param {import("ol/coordinate").Coordinate} imagePosition
|
|
188
|
+
* @param {number} rotation
|
|
189
|
+
* @param {number} pixelRatio
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
_drawArrow(ctx, imagePosition, rotation, pixelRatio) {
|
|
193
|
+
ctx.save();
|
|
194
|
+
let scale = this.getImage().getScale();
|
|
195
|
+
scale = Array.isArray(scale) ? scale : [scale, scale];
|
|
196
|
+
ctx.setTransform(scale[0], 0, 0, scale[1], imagePosition[0], imagePosition[1]);
|
|
197
|
+
ctx.rotate(Math.PI - rotation);
|
|
198
|
+
const image = this.getImage().getImage(pixelRatio);
|
|
199
|
+
ctx.translate(0, Math.floor(image.height / 2));
|
|
200
|
+
ctx.drawImage(image, -image.width / 2, -image.height / 2);
|
|
201
|
+
ctx.restore();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {Array<import("ol/coordinate").Coordinate>} geom
|
|
206
|
+
* @param {import("ol/render").State} e
|
|
207
|
+
* @private
|
|
208
|
+
*/
|
|
209
|
+
_render(geom, e) {
|
|
210
|
+
if (e.geometry.getType() === 'LineString' && geom.length > 1) {
|
|
211
|
+
const ctx = e.context;
|
|
212
|
+
if (this.end !== ArrowEnd.NONE) {
|
|
213
|
+
if (this.end === ArrowEnd.START || this.end === ArrowEnd.BOTH) {
|
|
214
|
+
this._drawArrow(ctx, geom[0], getCartesianBearing(geom[1], geom[0]), e.pixelRatio);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.end === ArrowEnd.END || this.end === ArrowEnd.BOTH) {
|
|
218
|
+
this._drawArrow(ctx, geom.at(-1), getCartesianBearing(geom.at(-2), geom.at(-1)), e.pixelRatio);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
ctx.save();
|
|
222
|
+
ctx.lineJoin = 'round';
|
|
223
|
+
ctx.lineWidth = this.width;
|
|
224
|
+
ctx.strokeStyle = getStringColor(this.color);
|
|
225
|
+
ctx.beginPath();
|
|
226
|
+
ctx.moveTo(geom[0][0], geom[0][1]);
|
|
227
|
+
for (let i = 0; i < geom.length; i++) {
|
|
228
|
+
ctx.lineTo(geom[i][0], geom[i][1]);
|
|
229
|
+
}
|
|
230
|
+
ctx.stroke();
|
|
231
|
+
ctx.restore();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Returns the style used to render primitives (a style with a circle image and the color of this style).
|
|
237
|
+
* @returns {import("ol/style").Style}
|
|
238
|
+
*/
|
|
239
|
+
getOlcsStyle() {
|
|
240
|
+
return new Style({
|
|
241
|
+
image: new Circle({
|
|
242
|
+
radius: 2,
|
|
243
|
+
fill: new Fill({ color: this.color }),
|
|
244
|
+
}),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @returns {ArrowStyleOptions}
|
|
250
|
+
* @protected
|
|
251
|
+
*/
|
|
252
|
+
_getCloneOptions() {
|
|
253
|
+
return {
|
|
254
|
+
color: this.color,
|
|
255
|
+
width: this.width,
|
|
256
|
+
arrowIcon: this.getImage().clone(),
|
|
257
|
+
zIndex: this.getZIndex(),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @returns {ArrowStyle}
|
|
263
|
+
*/
|
|
264
|
+
clone() {
|
|
265
|
+
return new ArrowStyle(this._getCloneOptions());
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export default ArrowStyle;
|
|
@@ -30,7 +30,7 @@ import ObliqueMap from '../../map/obliqueMap.js';
|
|
|
30
30
|
* @param {GeometryType} geometryType
|
|
31
31
|
* @returns {CreateFeatureSession}
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
function startCreateFeatureSession(app, layer, geometryType) {
|
|
34
34
|
check(app, VcsApp);
|
|
35
35
|
check(layer, VectorLayer);
|
|
36
36
|
check(geometryType, Object.values(GeometryType));
|
|
@@ -180,3 +180,5 @@ export default function startCreateFeatureSession(app, layer, geometryType) {
|
|
|
180
180
|
stop,
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
|
+
|
|
184
|
+
export default startCreateFeatureSession;
|