@vcmap/core 5.0.0-rc.0

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 (146) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +44 -0
  3. package/build/postinstall.js +44 -0
  4. package/index.js +139 -0
  5. package/package.json +92 -0
  6. package/src/cesium/cesium3DTileFeature.js +9 -0
  7. package/src/cesium/cesium3DTilePointFeature.js +9 -0
  8. package/src/cesium/cesiumVcsCameraPrimitive.js +146 -0
  9. package/src/cesium/wallpaperMaterial.js +64 -0
  10. package/src/ol/feature.js +47 -0
  11. package/src/ol/geom/circle.js +24 -0
  12. package/src/ol/geom/geometryCollection.js +33 -0
  13. package/src/ol/render/canvas/canvasTileRenderer.js +179 -0
  14. package/src/ol/source/ClusterEnhancedVectorSource.js +39 -0
  15. package/src/ol/source/VcsCluster.js +37 -0
  16. package/src/vcs/vcm/classRegistry.js +106 -0
  17. package/src/vcs/vcm/event/vcsEvent.js +89 -0
  18. package/src/vcs/vcm/globalCollections.js +11 -0
  19. package/src/vcs/vcm/interaction/abstractInteraction.js +149 -0
  20. package/src/vcs/vcm/interaction/coordinateAtPixel.js +102 -0
  21. package/src/vcs/vcm/interaction/eventHandler.js +425 -0
  22. package/src/vcs/vcm/interaction/featureAtPixelInteraction.js +286 -0
  23. package/src/vcs/vcm/interaction/featureProviderInteraction.js +54 -0
  24. package/src/vcs/vcm/interaction/interactionChain.js +124 -0
  25. package/src/vcs/vcm/interaction/interactionType.js +114 -0
  26. package/src/vcs/vcm/layer/buildings.js +17 -0
  27. package/src/vcs/vcm/layer/cesium/cesiumTilesetCesium.js +359 -0
  28. package/src/vcs/vcm/layer/cesium/clusterContext.js +95 -0
  29. package/src/vcs/vcm/layer/cesium/dataSourceCesium.js +171 -0
  30. package/src/vcs/vcm/layer/cesium/openStreetMapCesium.js +29 -0
  31. package/src/vcs/vcm/layer/cesium/pointCloudCesium.js +58 -0
  32. package/src/vcs/vcm/layer/cesium/rasterLayerCesium.js +110 -0
  33. package/src/vcs/vcm/layer/cesium/singleImageCesium.js +49 -0
  34. package/src/vcs/vcm/layer/cesium/terrainCesium.js +80 -0
  35. package/src/vcs/vcm/layer/cesium/tmsCesium.js +54 -0
  36. package/src/vcs/vcm/layer/cesium/vectorCesium.js +255 -0
  37. package/src/vcs/vcm/layer/cesium/vectorContext.js +167 -0
  38. package/src/vcs/vcm/layer/cesium/vectorRasterTileCesium.js +116 -0
  39. package/src/vcs/vcm/layer/cesium/vectorTileImageryProvider.js +246 -0
  40. package/src/vcs/vcm/layer/cesium/wmsCesium.js +71 -0
  41. package/src/vcs/vcm/layer/cesium/wmtsCesium.js +101 -0
  42. package/src/vcs/vcm/layer/cesium/x3dmHelper.js +22 -0
  43. package/src/vcs/vcm/layer/cesiumTileset.js +376 -0
  44. package/src/vcs/vcm/layer/czml.js +141 -0
  45. package/src/vcs/vcm/layer/dataSource.js +259 -0
  46. package/src/vcs/vcm/layer/featureLayer.js +261 -0
  47. package/src/vcs/vcm/layer/featureStore.js +647 -0
  48. package/src/vcs/vcm/layer/featureStoreChanges.js +360 -0
  49. package/src/vcs/vcm/layer/featureStoreState.js +19 -0
  50. package/src/vcs/vcm/layer/featureVisibility.js +435 -0
  51. package/src/vcs/vcm/layer/geojson.js +185 -0
  52. package/src/vcs/vcm/layer/geojsonHelpers.js +450 -0
  53. package/src/vcs/vcm/layer/globalHider.js +157 -0
  54. package/src/vcs/vcm/layer/layer.js +752 -0
  55. package/src/vcs/vcm/layer/layerImplementation.js +102 -0
  56. package/src/vcs/vcm/layer/layerState.js +17 -0
  57. package/src/vcs/vcm/layer/layerSymbols.js +6 -0
  58. package/src/vcs/vcm/layer/oblique/layerOblique.js +76 -0
  59. package/src/vcs/vcm/layer/oblique/obliqueHelpers.js +175 -0
  60. package/src/vcs/vcm/layer/oblique/vectorOblique.js +469 -0
  61. package/src/vcs/vcm/layer/openStreetMap.js +194 -0
  62. package/src/vcs/vcm/layer/openlayers/layerOpenlayers.js +79 -0
  63. package/src/vcs/vcm/layer/openlayers/openStreetMapOpenlayers.js +27 -0
  64. package/src/vcs/vcm/layer/openlayers/rasterLayerOpenlayers.js +121 -0
  65. package/src/vcs/vcm/layer/openlayers/singleImageOpenlayers.js +49 -0
  66. package/src/vcs/vcm/layer/openlayers/tileDebugOpenlayers.js +39 -0
  67. package/src/vcs/vcm/layer/openlayers/tmsOpenlayers.js +62 -0
  68. package/src/vcs/vcm/layer/openlayers/vectorOpenlayers.js +118 -0
  69. package/src/vcs/vcm/layer/openlayers/vectorTileOpenlayers.js +177 -0
  70. package/src/vcs/vcm/layer/openlayers/wmsOpenlayers.js +55 -0
  71. package/src/vcs/vcm/layer/openlayers/wmtsOpenlayers.js +141 -0
  72. package/src/vcs/vcm/layer/pointCloud.js +162 -0
  73. package/src/vcs/vcm/layer/rasterLayer.js +294 -0
  74. package/src/vcs/vcm/layer/singleImage.js +119 -0
  75. package/src/vcs/vcm/layer/terrain.js +122 -0
  76. package/src/vcs/vcm/layer/terrainHelpers.js +123 -0
  77. package/src/vcs/vcm/layer/tileLoadedHelper.js +72 -0
  78. package/src/vcs/vcm/layer/tileProvider/mvtTileProvider.js +104 -0
  79. package/src/vcs/vcm/layer/tileProvider/staticGeojsonTileProvider.js +67 -0
  80. package/src/vcs/vcm/layer/tileProvider/tileProvider.js +584 -0
  81. package/src/vcs/vcm/layer/tileProvider/tileProviderFactory.js +28 -0
  82. package/src/vcs/vcm/layer/tileProvider/urlTemplateTileProvider.js +106 -0
  83. package/src/vcs/vcm/layer/tms.js +121 -0
  84. package/src/vcs/vcm/layer/vector.js +632 -0
  85. package/src/vcs/vcm/layer/vectorHelpers.js +206 -0
  86. package/src/vcs/vcm/layer/vectorProperties.js +1391 -0
  87. package/src/vcs/vcm/layer/vectorSymbols.js +40 -0
  88. package/src/vcs/vcm/layer/vectorTile.js +480 -0
  89. package/src/vcs/vcm/layer/wfs.js +165 -0
  90. package/src/vcs/vcm/layer/wms.js +270 -0
  91. package/src/vcs/vcm/layer/wmsHelpers.js +65 -0
  92. package/src/vcs/vcm/layer/wmts.js +235 -0
  93. package/src/vcs/vcm/maps/baseOLMap.js +257 -0
  94. package/src/vcs/vcm/maps/cameraLimiter.js +219 -0
  95. package/src/vcs/vcm/maps/cesium.js +1192 -0
  96. package/src/vcs/vcm/maps/map.js +511 -0
  97. package/src/vcs/vcm/maps/mapState.js +17 -0
  98. package/src/vcs/vcm/maps/oblique.js +536 -0
  99. package/src/vcs/vcm/maps/openlayers.js +205 -0
  100. package/src/vcs/vcm/object.js +92 -0
  101. package/src/vcs/vcm/oblique/ObliqueCollection.js +572 -0
  102. package/src/vcs/vcm/oblique/ObliqueDataSet.js +357 -0
  103. package/src/vcs/vcm/oblique/ObliqueImage.js +247 -0
  104. package/src/vcs/vcm/oblique/ObliqueImageMeta.js +126 -0
  105. package/src/vcs/vcm/oblique/ObliqueProvider.js +433 -0
  106. package/src/vcs/vcm/oblique/ObliqueView.js +130 -0
  107. package/src/vcs/vcm/oblique/ObliqueViewDirection.js +40 -0
  108. package/src/vcs/vcm/oblique/helpers.js +483 -0
  109. package/src/vcs/vcm/oblique/parseImageJson.js +248 -0
  110. package/src/vcs/vcm/util/clipping/clippingObject.js +386 -0
  111. package/src/vcs/vcm/util/clipping/clippingObjectManager.js +312 -0
  112. package/src/vcs/vcm/util/clipping/clippingPlaneHelper.js +413 -0
  113. package/src/vcs/vcm/util/collection.js +193 -0
  114. package/src/vcs/vcm/util/dateTime.js +60 -0
  115. package/src/vcs/vcm/util/exclusiveManager.js +135 -0
  116. package/src/vcs/vcm/util/extent.js +124 -0
  117. package/src/vcs/vcm/util/featureProvider/abstractFeatureProvider.js +196 -0
  118. package/src/vcs/vcm/util/featureProvider/featureProviderHelpers.js +51 -0
  119. package/src/vcs/vcm/util/featureProvider/featureProviderSymbols.js +11 -0
  120. package/src/vcs/vcm/util/featureProvider/tileProviderFeatureProvider.js +62 -0
  121. package/src/vcs/vcm/util/featureProvider/wmsFeatureProvider.js +280 -0
  122. package/src/vcs/vcm/util/featureconverter/circleToCesium.js +215 -0
  123. package/src/vcs/vcm/util/featureconverter/convert.js +83 -0
  124. package/src/vcs/vcm/util/featureconverter/extent3d.js +154 -0
  125. package/src/vcs/vcm/util/featureconverter/featureconverterHelper.js +591 -0
  126. package/src/vcs/vcm/util/featureconverter/lineStringToCesium.js +171 -0
  127. package/src/vcs/vcm/util/featureconverter/pointToCesium.js +359 -0
  128. package/src/vcs/vcm/util/featureconverter/polygonToCesium.js +229 -0
  129. package/src/vcs/vcm/util/geometryHelpers.js +172 -0
  130. package/src/vcs/vcm/util/indexedCollection.js +158 -0
  131. package/src/vcs/vcm/util/isMobile.js +12 -0
  132. package/src/vcs/vcm/util/layerCollection.js +216 -0
  133. package/src/vcs/vcm/util/locale.js +53 -0
  134. package/src/vcs/vcm/util/mapCollection.js +363 -0
  135. package/src/vcs/vcm/util/math.js +71 -0
  136. package/src/vcs/vcm/util/projection.js +348 -0
  137. package/src/vcs/vcm/util/splitScreen.js +233 -0
  138. package/src/vcs/vcm/util/style/declarativeStyleItem.js +631 -0
  139. package/src/vcs/vcm/util/style/shapesCategory.js +67 -0
  140. package/src/vcs/vcm/util/style/styleFactory.js +48 -0
  141. package/src/vcs/vcm/util/style/styleHelpers.js +555 -0
  142. package/src/vcs/vcm/util/style/styleItem.js +226 -0
  143. package/src/vcs/vcm/util/style/vectorStyleItem.js +927 -0
  144. package/src/vcs/vcm/util/style/writeStyle.js +48 -0
  145. package/src/vcs/vcm/util/urlHelpers.js +16 -0
  146. package/src/vcs/vcm/util/viewpoint.js +333 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Enumeration of view directions.
3
+ * @enum {number}
4
+ * @property {number} NORTH
5
+ * @property {number} EAST
6
+ * @property {number} SOUTH
7
+ * @property {number} WEST
8
+ * @property {number} NADIR
9
+ * @export
10
+ * @api
11
+ */
12
+ export const ObliqueViewDirection = {
13
+ NORTH: 1,
14
+ EAST: 2,
15
+ SOUTH: 3,
16
+ WEST: 4,
17
+ NADIR: 5,
18
+ };
19
+
20
+ /**
21
+ * @type {Object<string, ObliqueViewDirection>}
22
+ * @export
23
+ */
24
+ export const obliqueViewDirectionNames = {
25
+ north: ObliqueViewDirection.NORTH,
26
+ east: ObliqueViewDirection.EAST,
27
+ south: ObliqueViewDirection.SOUTH,
28
+ west: ObliqueViewDirection.WEST,
29
+ nadir: ObliqueViewDirection.NADIR,
30
+ };
31
+
32
+ /**
33
+ * @param {number} direction
34
+ * @returns {string|undefined}
35
+ * @export
36
+ */
37
+ export function getDirectionName(direction) {
38
+ return Object.keys(obliqueViewDirectionNames)
39
+ .find((name => obliqueViewDirectionNames[name] === direction));
40
+ }
@@ -0,0 +1,483 @@
1
+ /* eslint no-underscore-dangle: ["error", { "allow": ["_listeners", "_scopes", "_toRemove"] }] */
2
+ /* eslint-disable no-continue */
3
+ import { boundingExtent, getBottomLeft, getBottomRight, getTopLeft, getTopRight } from 'ol/extent.js';
4
+ import { get as getProjection, transform } from 'ol/proj.js';
5
+ import { Cartesian2 } from '@vcmap/cesium';
6
+ import { ObliqueViewDirection } from './ObliqueViewDirection.js';
7
+ import { getHeightFromTerrainProvider } from '../layer/terrainHelpers.js';
8
+ import { cartesian2DDistance } from '../util/math.js';
9
+
10
+ /**
11
+ * @type {import("@vcmap/cesium").Cartesian2}
12
+ */
13
+ let scratchCartesian2A = new Cartesian2();
14
+ /**
15
+ * @type {import("@vcmap/cesium").Cartesian2}
16
+ */
17
+ let scratchCartesian2B = new Cartesian2();
18
+
19
+ /**
20
+ * @typedef {Object} PickTerrainReturn
21
+ * @property {boolean} estimate
22
+ * @property {import("ol/coordinate").Coordinate} coords
23
+ */
24
+
25
+ /**
26
+ * sorts the corner points of the json after [lower left, lower right, upper right, upper left]
27
+ * 3----2 ^
28
+ * | | |
29
+ * 0----1 north
30
+ * @param {Array<import("ol/coordinate").Coordinate>} inputCornerPoints
31
+ * @param {(ObliqueViewDirection|boolean)=} [sortDirection=false]
32
+ * @returns {Array<import("ol/coordinate").Coordinate>}
33
+ */
34
+ export function sortRealWordEdgeCoordinates(inputCornerPoints, sortDirection = false) {
35
+ const cornerPoints = inputCornerPoints.slice();
36
+ const extent = boundingExtent(cornerPoints);
37
+ const extentPoints = [
38
+ getBottomLeft(extent),
39
+ getBottomRight(extent),
40
+ getTopRight(extent),
41
+ getTopLeft(extent),
42
+ ];
43
+
44
+ let sorted = extentPoints.map((extentPoint) => {
45
+ let closest = 0;
46
+ let distance = Infinity;
47
+ cornerPoints.forEach((cornerPoint, cornerIndex) => {
48
+ const currentDistance = cartesian2DDistance(extentPoint, cornerPoint);
49
+ if (currentDistance < distance) {
50
+ distance = currentDistance;
51
+ closest = cornerIndex;
52
+ }
53
+ });
54
+ return cornerPoints.splice(closest, 1)[0];
55
+ });
56
+ if (sortDirection === ObliqueViewDirection.EAST) {
57
+ sorted = [sorted[3], sorted[0], sorted[1], sorted[2]];
58
+ } else if (sortDirection === ObliqueViewDirection.SOUTH) {
59
+ sorted = [sorted[2], sorted[3], sorted[0], sorted[1]];
60
+ } else if (sortDirection === ObliqueViewDirection.WEST) {
61
+ sorted = [sorted[1], sorted[2], sorted[3], sorted[0]];
62
+ }
63
+ return sorted;
64
+ }
65
+
66
+ /**
67
+ * @param {import("ol/coordinate").Coordinate} v1
68
+ * @param {import("ol/coordinate").Coordinate} v2
69
+ * @returns {number|null}
70
+ */
71
+ function angleBetweenTwo2DVectors(v1, v2) {
72
+ scratchCartesian2A = Cartesian2.fromElements(v1[0], v1[1], scratchCartesian2A);
73
+ scratchCartesian2B = Cartesian2.fromElements(v2[0], v2[1], scratchCartesian2B);
74
+ return Cartesian2.angleBetween(scratchCartesian2A, scratchCartesian2B);
75
+ }
76
+
77
+ /**
78
+ * taken from http://jsfiddle.net/justin_c_rounds/Gd2S2/
79
+ * @param {Array<import("ol/coordinate").Coordinate>} segment1
80
+ * @param {Array<import("ol/coordinate").Coordinate>} segment2
81
+ * @returns {{x: (number|null), y:(number|null), onLine1: boolean, onLine2: boolean}}
82
+ * @export
83
+ */
84
+ export function checkLineIntersection(segment1, segment2) {
85
+ // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
86
+ const [[line1StartX, line1StartY], [line1EndX, line1EndY]] = segment1;
87
+ const [[line2StartX, line2StartY], [line2EndX, line2EndY]] = segment2;
88
+ let a;
89
+ let b;
90
+
91
+ const result = {
92
+ x: null,
93
+ y: null,
94
+ onLine1: false,
95
+ onLine2: false,
96
+ };
97
+
98
+ const denominator =
99
+ ((line2EndY - line2StartY) * (line1EndX - line1StartX)) -
100
+ ((line2EndX - line2StartX) * (line1EndY - line1StartY));
101
+
102
+ if (denominator === 0) {
103
+ return result;
104
+ }
105
+ a = line1StartY - line2StartY;
106
+ b = line1StartX - line2StartX;
107
+ const numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
108
+ const numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
109
+ a = numerator1 / denominator;
110
+ b = numerator2 / denominator;
111
+
112
+ // if we cast these lines infinitely in both directions, they intersect here:
113
+ result.x = line1StartX + (a * (line1EndX - line1StartX));
114
+ result.y = line1StartY + (a * (line1EndY - line1StartY));
115
+ /*
116
+ // it is worth noting that this should be the same as:
117
+ x = line2StartX + (b * (line2EndX - line2StartX));
118
+ y = line2StartX + (b * (line2EndY - line2StartY));
119
+ */
120
+ // if line1 is a segment and line2 is infinite, they intersect if:
121
+ if (a > 0 && a < 1) {
122
+ result.onLine1 = true;
123
+ }
124
+ // if line2 is a segment and line1 is infinite, they intersect if:
125
+ if (b > 0 && b < 1) {
126
+ result.onLine2 = true;
127
+ }
128
+ // if line1 and line2 are segments, they intersect if both of the above are true
129
+ return result;
130
+ }
131
+
132
+ /**
133
+ * transforms coordinate with intersection from corner
134
+ * @param {Array<import("ol/coordinate").Coordinate>} inputOrigin
135
+ * @param {Array<import("ol/coordinate").Coordinate>} inputTarget
136
+ * @param {boolean} originIsImage
137
+ * @param {import("ol/coordinate").Coordinate} coordinate2Transform
138
+ * @param {ObliqueViewDirection} viewDirection
139
+ * @returns {{x: (number|null), y:(number|null), onLine1: boolean, onLine2: boolean}|null}
140
+ */
141
+ export function transformCWIFC(inputOrigin, inputTarget, originIsImage, coordinate2Transform, viewDirection) {
142
+ const origin = sortRealWordEdgeCoordinates(inputOrigin, originIsImage ? false : viewDirection);
143
+ const target = sortRealWordEdgeCoordinates(inputTarget, originIsImage ? viewDirection : false);
144
+
145
+ // test intersections from all corner points over coordinate to all non neighbouring borders
146
+ // and non side borders too - so actually remains only upper and lower border
147
+ // and store:
148
+ // - from which corner point,
149
+ // - intersection,
150
+ // - angle of intersection
151
+ // - as well as edge (ccw) and
152
+ // - distance ratio while negative means negative direction
153
+ // to be able to recreate the situation in the target system
154
+ const intersections = [];
155
+ for (let cp = 0; cp < origin.length; ++cp) { // TODO write into a proper map and function
156
+ const intrCurrCP = [];
157
+
158
+ for (let sp = 0; sp < origin.length; ++sp) {
159
+ const ep = sp === origin.length - 1 ? 0 : sp + 1; // end point of edge
160
+
161
+ // skip if cp is sp or ep - neighbouring edge
162
+ if (cp === sp || cp === ep) { continue; }
163
+
164
+ // skip also if sp is 3 and ep is 0 and also skip sp is 1 and ep is 2 since does only work over the upper and lower boundary
165
+ if ((sp === 3 && ep === 0) || (sp === 1 && ep === 2)) { continue; }
166
+
167
+ // get intersection from cp over coordinate2Transform to current border edge
168
+ const currIntr = checkLineIntersection([origin[cp], coordinate2Transform], [origin[sp], origin[ep]]);
169
+ if (currIntr.x == null || currIntr.y == null) { continue; }
170
+
171
+ // get vector from current cp to coordinate2Transform to be able to determine if the intersection is in same direction
172
+ // might be in different direction when point is outside - then we dont need this data
173
+ const vectorCP2Coordinate = [coordinate2Transform[0] - origin[cp][0], coordinate2Transform[1] - origin[cp][1]];
174
+ const vectorCP2Intr = [currIntr.x - origin[cp][0], currIntr.y - origin[cp][1]];
175
+ const angleDirectionCheck = angleBetweenTwo2DVectors(vectorCP2Coordinate, vectorCP2Intr);
176
+ if (angleDirectionCheck == null) { continue; }
177
+
178
+ if (angleDirectionCheck / (Math.PI * 180.0) > 5) { continue; }
179
+
180
+ const sp2ep = [origin[sp][0] - origin[ep][0], origin[sp][1] - origin[ep][1]];
181
+ const ep2sp = [origin[ep][0] - origin[sp][0], origin[ep][1] - origin[sp][1]];
182
+ // regarding the angle find the smallest
183
+ const angleStart2End = angleBetweenTwo2DVectors(vectorCP2Coordinate, sp2ep);
184
+ if (angleStart2End == null) { continue; }
185
+
186
+ const angleEnd2Start = angleBetweenTwo2DVectors(vectorCP2Coordinate, ep2sp);
187
+ if (angleEnd2Start == null) { continue; }
188
+
189
+ // regarding ratioStart2End get ratio and then direction
190
+ const distStartEnd = cartesian2DDistance(origin[sp], origin[ep]);
191
+ if (distStartEnd === 0) { continue; }
192
+ const tempRatioStartEnd = cartesian2DDistance(origin[sp], [currIntr.x, currIntr.y]) / distStartEnd;
193
+ let angleEdge2Intr = 0;
194
+ if (tempRatioStartEnd !== 0) {
195
+ angleEdge2Intr = angleBetweenTwo2DVectors(ep2sp, [currIntr.x - origin[sp][0], currIntr.y - origin[sp][1]]);
196
+ if (angleEdge2Intr == null) { continue; }
197
+ }
198
+
199
+ intrCurrCP.push({
200
+ cornerPoint: cp,
201
+ intrX: currIntr.x,
202
+ intrY: currIntr.y,
203
+ angle: angleStart2End <= angleEnd2Start ? angleStart2End : angleEnd2Start,
204
+ edgeStart: sp,
205
+ edgeEnd: ep,
206
+ ratioStart2End: (angleEdge2Intr / Math.PI) * 180.0 > 5 ? tempRatioStartEnd * (-1) : tempRatioStartEnd,
207
+ });
208
+ }
209
+
210
+ // after getting the data find the intersection with largest of smallest angles
211
+ let largestAngle = -1;
212
+ let indLargestAngle = -1;
213
+ for (let i = 0; i < intrCurrCP.length; ++i) {
214
+ if (intrCurrCP[i].angle > largestAngle) {
215
+ largestAngle = intrCurrCP[i].angle;
216
+ indLargestAngle = i;
217
+ }
218
+ }
219
+ if (indLargestAngle !== -1) { intersections.push(intrCurrCP[indLargestAngle]); }
220
+ }
221
+
222
+ // if we don not have enough data to recreate the situation in target system stop
223
+ if (intersections.length < 2) { return null; }
224
+
225
+ // make list with intersection combinations and sort after strength (add angles together)
226
+ const intrCombis = []; // will contain [addedAngle, intersectionsIndex_i, intersectionsIndex_j]
227
+ for (let i = 0; i < intersections.length; ++i) {
228
+ for (let j = i + 1; j < intersections.length; ++j) {
229
+ intrCombis.push([intersections[i].angle + intersections[j].angle, i, j]);
230
+ }
231
+ }
232
+
233
+ let intrCross = null;
234
+ intrCombis
235
+ .sort()
236
+ .reverse()
237
+ .find((intersection) => {
238
+ const intersectionsSorted = [intersections[intersection[1]], intersections[intersection[2]]];
239
+
240
+ const targetEdgeEnd0 = target[intersectionsSorted[0].edgeEnd];
241
+ const targetEdgeStart0 = target[intersectionsSorted[0].edgeStart];
242
+ // test angle of lines with that the intersection will be calculated - if two small skip
243
+ const targetEdgeVectorFor0 = [
244
+ targetEdgeEnd0[0] - targetEdgeStart0[0],
245
+ targetEdgeEnd0[1] - targetEdgeStart0[1],
246
+ ];
247
+
248
+ const intrFor0InTarget = [
249
+ targetEdgeStart0[0] + (targetEdgeVectorFor0[0] * intersectionsSorted[0].ratioStart2End),
250
+ targetEdgeStart0[1] + (targetEdgeVectorFor0[1] * intersectionsSorted[0].ratioStart2End),
251
+ ];
252
+
253
+ const targetEdgeEnd1 = target[intersectionsSorted[1].edgeEnd];
254
+ const targetEdgeStart1 = target[intersectionsSorted[1].edgeStart];
255
+
256
+ const targetEdgeVectorFor1 = [targetEdgeEnd1[0] - targetEdgeStart1[0], targetEdgeEnd1[1] - targetEdgeStart1[1]];
257
+ const intrFor1InTarget = [
258
+ targetEdgeStart1[0] + (targetEdgeVectorFor1[0] * intersectionsSorted[1].ratioStart2End),
259
+ targetEdgeStart1[1] + (targetEdgeVectorFor1[1] * intersectionsSorted[1].ratioStart2End),
260
+ ];
261
+
262
+ const vecCP0ToIntr0 = [
263
+ intrFor0InTarget[0] - target[intersectionsSorted[0].cornerPoint][0],
264
+ intrFor0InTarget[1] - target[intersectionsSorted[0].cornerPoint][1],
265
+ ];
266
+ const vecCP1ToIntr1 = [
267
+ intrFor1InTarget[0] - target[intersectionsSorted[1].cornerPoint][0],
268
+ intrFor1InTarget[1] - target[intersectionsSorted[1].cornerPoint][1],
269
+ ];
270
+
271
+ const angleCross = angleBetweenTwo2DVectors(vecCP0ToIntr0, vecCP1ToIntr1);
272
+ if (angleCross == null) { return false; }
273
+ /* var thresholdInDegree = 3;
274
+ if (angleCross/Math.PI*180.0 < thresholdInDegree || angleCross/Math.PI*180.0 > 180-thresholdInDegree)
275
+ continue; */
276
+
277
+ // get point in target
278
+ intrCross = checkLineIntersection(
279
+ [target[intersectionsSorted[0].cornerPoint], intrFor0InTarget],
280
+ [target[intersectionsSorted[1].cornerPoint], intrFor1InTarget],
281
+ );
282
+ if (intrCross.x == null || intrCross.y == null) {
283
+ return false;
284
+ }
285
+
286
+ return true;
287
+ });
288
+
289
+ return intrCross;
290
+ }
291
+
292
+ /**
293
+ * @typedef {Object} ImageTransformationOptions
294
+ * @property {boolean|undefined} [dontUseTerrain]
295
+ * @property {import("ol/proj/Projection").default|undefined} [dataProjection] - the projection of the input/output coordinates, assumes image source projection
296
+ * @property {number|undefined} [terrainErrorThreshold=1] - the transformToWorld process iterativly calculates a new Height Value from the terrainProvider until the difference to the new height value is smaller
297
+ * @property {number|undefined} [terrainErrorCountThreshold=3] - how often the transformToWorld process iterativly calculates a new Height Value from the terrainProvider
298
+ * @api
299
+ */
300
+
301
+ /**
302
+ * Always returns a Promise. When the input coordinates contain a height, it will use this height to compute the image coordinates
303
+ * When not, it will try to get the terrainHeight in case a terrain is defined and use the height from there, to compute the image coordinates
304
+ * @param {import("@vcmap/core").ObliqueImage} image
305
+ * @param {import("ol/coordinate").Coordinate} worldCoordinate if not in web mercatpr, specify data-projection in options
306
+ * @param {ImageTransformationOptions=} options
307
+ * @returns {Promise<{coords: import("ol/coordinate").Coordinate, height: number, estimate: (boolean|undefined)}>}
308
+ * @export
309
+ */
310
+ export function transformToImage(image, worldCoordinate, options = {}) {
311
+ let gpInternalCoordinates;
312
+ if (options.dataProjection && options.dataProjection === image.meta.projection) {
313
+ gpInternalCoordinates = worldCoordinate;
314
+ } else {
315
+ gpInternalCoordinates = options.dataProjection ?
316
+ transform(worldCoordinate, options.dataProjection, image.meta.projection) :
317
+ transform(worldCoordinate, 'EPSG:3857', image.meta.projection);
318
+ }
319
+
320
+ function useAverageHeight() {
321
+ const coords = image.transformRealWorld2Image(gpInternalCoordinates);
322
+ return { coords, height: image.averageHeight, estimate: true };
323
+ }
324
+
325
+ if (worldCoordinate[2]) {
326
+ const coords = image.transformRealWorld2Image(gpInternalCoordinates, worldCoordinate[2]);
327
+ return Promise.resolve({ coords, height: worldCoordinate[2], estimate: false });
328
+ }
329
+
330
+ if (!options.dontUseTerrain && image.meta.terrainProvider) {
331
+ return getHeightFromTerrainProvider(image.meta.terrainProvider, [gpInternalCoordinates], image.meta.projection)
332
+ .then(([gpWithHeight]) => {
333
+ if (gpWithHeight[2]) {
334
+ const imageCoordinates = image.transformRealWorld2Image(gpInternalCoordinates, gpWithHeight[2]);
335
+ return { coords: imageCoordinates, height: gpInternalCoordinates[2], estimate: false };
336
+ }
337
+ // eslint-disable-next-line no-console
338
+ console.warn('The configured terrain on the oblique layer could not be queried, position might be inaccurate');
339
+ return useAverageHeight();
340
+ })
341
+ .catch(() => useAverageHeight());
342
+ }
343
+ return Promise.resolve(useAverageHeight());
344
+ }
345
+
346
+ /**
347
+ * @typedef {Object} PickTerrainOptions
348
+ * @property {import("@vcmap/core").ObliqueImage} image
349
+ * @property {import("ol/coordinate").Coordinate} worldCoordinate
350
+ * @property {import("ol/coordinate").Coordinate} imageCoordinate
351
+ * @property {number} height
352
+ * @property {number} terrainErrorThreshold
353
+ * @property {number} terrainErrorCountThreshold
354
+ * @property {number} count
355
+ */
356
+
357
+ /**
358
+ * @param {PickTerrainOptions} pickTerrainOptions
359
+ * @returns {Promise<PickTerrainReturn>}
360
+ */
361
+ function pickTerrain(pickTerrainOptions) {
362
+ pickTerrainOptions.count += 1;
363
+ const {
364
+ image,
365
+ worldCoordinate,
366
+ imageCoordinate,
367
+ terrainErrorThreshold,
368
+ terrainErrorCountThreshold,
369
+ count,
370
+ height,
371
+ } = pickTerrainOptions;
372
+ const wgs84Projection = getProjection('EPSG:4326');
373
+ return getHeightFromTerrainProvider(image.meta.terrainProvider, [worldCoordinate])
374
+ .then(([worldCoordinateWithHeights]) => {
375
+ if (worldCoordinateWithHeights[2] != null) {
376
+ const newWorldCoords = transform(
377
+ image.transformImage2RealWorld(imageCoordinate, worldCoordinateWithHeights[2]),
378
+ image.meta.projection,
379
+ wgs84Projection,
380
+ );
381
+ newWorldCoords[2] = worldCoordinateWithHeights[2];
382
+ if (
383
+ Math.abs(height - worldCoordinateWithHeights[2]) < terrainErrorThreshold ||
384
+ (count > terrainErrorCountThreshold)
385
+ ) {
386
+ return { coords: newWorldCoords, estimate: false };
387
+ }
388
+ pickTerrainOptions.height = newWorldCoords[2];
389
+ pickTerrainOptions.worldCoordinate = worldCoordinateWithHeights;
390
+ return pickTerrain(pickTerrainOptions);
391
+ }
392
+ // eslint-disable-next-line no-console
393
+ console.log('The configured terrain on the oblique layer could not be queried, position might be inaccurate');
394
+ return { coords: worldCoordinateWithHeights, estimate: true };
395
+ })
396
+ .catch(() => ({ coords: worldCoordinate, estimate: true }));
397
+ }
398
+
399
+ /**
400
+ * Always returns a deferred.
401
+ * it will try to get the terrainHeight in case a terrain is defined.
402
+ * @param {import("@vcmap/core").ObliqueImage} image
403
+ * @param {import("ol/coordinate").Coordinate} imageCoordinate
404
+ * @param {ImageTransformationOptions=} options
405
+ * @returns {Promise<PickTerrainReturn>} return coordinates are in mercator if not specified in options
406
+ * @export
407
+ */
408
+ export async function transformFromImage(image, imageCoordinate, options = {}) {
409
+ const wgs84Projection = getProjection('EPSG:4326');
410
+ const initialWorldCoords = transform(
411
+ image.transformImage2RealWorld(imageCoordinate, image.averageHeight),
412
+ image.meta.projection,
413
+ wgs84Projection,
414
+ );
415
+
416
+ const terrainErrorThreshold = options.terrainErrorThreshold || 1;
417
+ const terrainErrorCountThreshold = options.terrainErrorCountThreshold || 3;
418
+
419
+ let coordsObj = { coords: initialWorldCoords, estimate: true };
420
+ if (!options.dontUseTerrain && image.meta.terrainProvider) {
421
+ coordsObj = await pickTerrain({
422
+ worldCoordinate: initialWorldCoords,
423
+ imageCoordinate,
424
+ image,
425
+ count: 0,
426
+ height: image.averageHeight,
427
+ terrainErrorThreshold,
428
+ terrainErrorCountThreshold,
429
+ });
430
+ }
431
+
432
+ coordsObj.coords = options.dataProjection ?
433
+ transform(coordsObj.coords, wgs84Projection, options.dataProjection) :
434
+ transform(coordsObj.coords, wgs84Projection, getProjection('EPSG:3857'));
435
+ return coordsObj;
436
+ }
437
+
438
+ /**
439
+ * @param {string} url
440
+ * @returns {boolean}
441
+ */
442
+ export function hasSameOrigin(url) {
443
+ // relative Url, return true;
444
+ if (!/^[a-z][a-z0-9+.-]*:/.test(url)) {
445
+ return true;
446
+ }
447
+ // data uri, return true
448
+ if (/^data:/.test(url)) {
449
+ return true;
450
+ }
451
+
452
+ const currentUri = new URL(window.location.href);
453
+ const uri = new URL(url);
454
+ return currentUri.host.toLowerCase() === uri.host.toLocaleLowerCase();
455
+ }
456
+
457
+ /**
458
+ * destroys a cesium Event Emitter, (removes all listeners)
459
+ * @param {import("@vcmap/cesium").Event} cesiumEvent
460
+ */
461
+ export function destroyCesiumEvent(cesiumEvent) {
462
+ // @ts-ignore
463
+ for (let i = 0; i < cesiumEvent._listeners.length; i++) {
464
+ // @ts-ignore
465
+ cesiumEvent._listeners[i] = undefined;
466
+ }
467
+ // @ts-ignore
468
+ for (let i = 0; i < cesiumEvent._scopes.length; i++) {
469
+ // @ts-ignore
470
+ cesiumEvent._scopes[i] = undefined;
471
+ }
472
+ // @ts-ignore
473
+ for (let i = 0; i < cesiumEvent._toRemove.length; i++) {
474
+ // @ts-ignore
475
+ cesiumEvent._toRemove[i] = undefined;
476
+ }
477
+ // @ts-ignore
478
+ cesiumEvent._listeners.length = 0;
479
+ // @ts-ignore
480
+ cesiumEvent._scopes.length = 0;
481
+ // @ts-ignore
482
+ cesiumEvent._toRemove.length = 0;
483
+ }