@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.
- package/LICENSE.md +21 -0
- package/README.md +44 -0
- package/build/postinstall.js +44 -0
- package/index.js +139 -0
- package/package.json +92 -0
- package/src/cesium/cesium3DTileFeature.js +9 -0
- package/src/cesium/cesium3DTilePointFeature.js +9 -0
- package/src/cesium/cesiumVcsCameraPrimitive.js +146 -0
- package/src/cesium/wallpaperMaterial.js +64 -0
- package/src/ol/feature.js +47 -0
- package/src/ol/geom/circle.js +24 -0
- package/src/ol/geom/geometryCollection.js +33 -0
- package/src/ol/render/canvas/canvasTileRenderer.js +179 -0
- package/src/ol/source/ClusterEnhancedVectorSource.js +39 -0
- package/src/ol/source/VcsCluster.js +37 -0
- package/src/vcs/vcm/classRegistry.js +106 -0
- package/src/vcs/vcm/event/vcsEvent.js +89 -0
- package/src/vcs/vcm/globalCollections.js +11 -0
- package/src/vcs/vcm/interaction/abstractInteraction.js +149 -0
- package/src/vcs/vcm/interaction/coordinateAtPixel.js +102 -0
- package/src/vcs/vcm/interaction/eventHandler.js +425 -0
- package/src/vcs/vcm/interaction/featureAtPixelInteraction.js +286 -0
- package/src/vcs/vcm/interaction/featureProviderInteraction.js +54 -0
- package/src/vcs/vcm/interaction/interactionChain.js +124 -0
- package/src/vcs/vcm/interaction/interactionType.js +114 -0
- package/src/vcs/vcm/layer/buildings.js +17 -0
- package/src/vcs/vcm/layer/cesium/cesiumTilesetCesium.js +359 -0
- package/src/vcs/vcm/layer/cesium/clusterContext.js +95 -0
- package/src/vcs/vcm/layer/cesium/dataSourceCesium.js +171 -0
- package/src/vcs/vcm/layer/cesium/openStreetMapCesium.js +29 -0
- package/src/vcs/vcm/layer/cesium/pointCloudCesium.js +58 -0
- package/src/vcs/vcm/layer/cesium/rasterLayerCesium.js +110 -0
- package/src/vcs/vcm/layer/cesium/singleImageCesium.js +49 -0
- package/src/vcs/vcm/layer/cesium/terrainCesium.js +80 -0
- package/src/vcs/vcm/layer/cesium/tmsCesium.js +54 -0
- package/src/vcs/vcm/layer/cesium/vectorCesium.js +255 -0
- package/src/vcs/vcm/layer/cesium/vectorContext.js +167 -0
- package/src/vcs/vcm/layer/cesium/vectorRasterTileCesium.js +116 -0
- package/src/vcs/vcm/layer/cesium/vectorTileImageryProvider.js +246 -0
- package/src/vcs/vcm/layer/cesium/wmsCesium.js +71 -0
- package/src/vcs/vcm/layer/cesium/wmtsCesium.js +101 -0
- package/src/vcs/vcm/layer/cesium/x3dmHelper.js +22 -0
- package/src/vcs/vcm/layer/cesiumTileset.js +376 -0
- package/src/vcs/vcm/layer/czml.js +141 -0
- package/src/vcs/vcm/layer/dataSource.js +259 -0
- package/src/vcs/vcm/layer/featureLayer.js +261 -0
- package/src/vcs/vcm/layer/featureStore.js +647 -0
- package/src/vcs/vcm/layer/featureStoreChanges.js +360 -0
- package/src/vcs/vcm/layer/featureStoreState.js +19 -0
- package/src/vcs/vcm/layer/featureVisibility.js +435 -0
- package/src/vcs/vcm/layer/geojson.js +185 -0
- package/src/vcs/vcm/layer/geojsonHelpers.js +450 -0
- package/src/vcs/vcm/layer/globalHider.js +157 -0
- package/src/vcs/vcm/layer/layer.js +752 -0
- package/src/vcs/vcm/layer/layerImplementation.js +102 -0
- package/src/vcs/vcm/layer/layerState.js +17 -0
- package/src/vcs/vcm/layer/layerSymbols.js +6 -0
- package/src/vcs/vcm/layer/oblique/layerOblique.js +76 -0
- package/src/vcs/vcm/layer/oblique/obliqueHelpers.js +175 -0
- package/src/vcs/vcm/layer/oblique/vectorOblique.js +469 -0
- package/src/vcs/vcm/layer/openStreetMap.js +194 -0
- package/src/vcs/vcm/layer/openlayers/layerOpenlayers.js +79 -0
- package/src/vcs/vcm/layer/openlayers/openStreetMapOpenlayers.js +27 -0
- package/src/vcs/vcm/layer/openlayers/rasterLayerOpenlayers.js +121 -0
- package/src/vcs/vcm/layer/openlayers/singleImageOpenlayers.js +49 -0
- package/src/vcs/vcm/layer/openlayers/tileDebugOpenlayers.js +39 -0
- package/src/vcs/vcm/layer/openlayers/tmsOpenlayers.js +62 -0
- package/src/vcs/vcm/layer/openlayers/vectorOpenlayers.js +118 -0
- package/src/vcs/vcm/layer/openlayers/vectorTileOpenlayers.js +177 -0
- package/src/vcs/vcm/layer/openlayers/wmsOpenlayers.js +55 -0
- package/src/vcs/vcm/layer/openlayers/wmtsOpenlayers.js +141 -0
- package/src/vcs/vcm/layer/pointCloud.js +162 -0
- package/src/vcs/vcm/layer/rasterLayer.js +294 -0
- package/src/vcs/vcm/layer/singleImage.js +119 -0
- package/src/vcs/vcm/layer/terrain.js +122 -0
- package/src/vcs/vcm/layer/terrainHelpers.js +123 -0
- package/src/vcs/vcm/layer/tileLoadedHelper.js +72 -0
- package/src/vcs/vcm/layer/tileProvider/mvtTileProvider.js +104 -0
- package/src/vcs/vcm/layer/tileProvider/staticGeojsonTileProvider.js +67 -0
- package/src/vcs/vcm/layer/tileProvider/tileProvider.js +584 -0
- package/src/vcs/vcm/layer/tileProvider/tileProviderFactory.js +28 -0
- package/src/vcs/vcm/layer/tileProvider/urlTemplateTileProvider.js +106 -0
- package/src/vcs/vcm/layer/tms.js +121 -0
- package/src/vcs/vcm/layer/vector.js +632 -0
- package/src/vcs/vcm/layer/vectorHelpers.js +206 -0
- package/src/vcs/vcm/layer/vectorProperties.js +1391 -0
- package/src/vcs/vcm/layer/vectorSymbols.js +40 -0
- package/src/vcs/vcm/layer/vectorTile.js +480 -0
- package/src/vcs/vcm/layer/wfs.js +165 -0
- package/src/vcs/vcm/layer/wms.js +270 -0
- package/src/vcs/vcm/layer/wmsHelpers.js +65 -0
- package/src/vcs/vcm/layer/wmts.js +235 -0
- package/src/vcs/vcm/maps/baseOLMap.js +257 -0
- package/src/vcs/vcm/maps/cameraLimiter.js +219 -0
- package/src/vcs/vcm/maps/cesium.js +1192 -0
- package/src/vcs/vcm/maps/map.js +511 -0
- package/src/vcs/vcm/maps/mapState.js +17 -0
- package/src/vcs/vcm/maps/oblique.js +536 -0
- package/src/vcs/vcm/maps/openlayers.js +205 -0
- package/src/vcs/vcm/object.js +92 -0
- package/src/vcs/vcm/oblique/ObliqueCollection.js +572 -0
- package/src/vcs/vcm/oblique/ObliqueDataSet.js +357 -0
- package/src/vcs/vcm/oblique/ObliqueImage.js +247 -0
- package/src/vcs/vcm/oblique/ObliqueImageMeta.js +126 -0
- package/src/vcs/vcm/oblique/ObliqueProvider.js +433 -0
- package/src/vcs/vcm/oblique/ObliqueView.js +130 -0
- package/src/vcs/vcm/oblique/ObliqueViewDirection.js +40 -0
- package/src/vcs/vcm/oblique/helpers.js +483 -0
- package/src/vcs/vcm/oblique/parseImageJson.js +248 -0
- package/src/vcs/vcm/util/clipping/clippingObject.js +386 -0
- package/src/vcs/vcm/util/clipping/clippingObjectManager.js +312 -0
- package/src/vcs/vcm/util/clipping/clippingPlaneHelper.js +413 -0
- package/src/vcs/vcm/util/collection.js +193 -0
- package/src/vcs/vcm/util/dateTime.js +60 -0
- package/src/vcs/vcm/util/exclusiveManager.js +135 -0
- package/src/vcs/vcm/util/extent.js +124 -0
- package/src/vcs/vcm/util/featureProvider/abstractFeatureProvider.js +196 -0
- package/src/vcs/vcm/util/featureProvider/featureProviderHelpers.js +51 -0
- package/src/vcs/vcm/util/featureProvider/featureProviderSymbols.js +11 -0
- package/src/vcs/vcm/util/featureProvider/tileProviderFeatureProvider.js +62 -0
- package/src/vcs/vcm/util/featureProvider/wmsFeatureProvider.js +280 -0
- package/src/vcs/vcm/util/featureconverter/circleToCesium.js +215 -0
- package/src/vcs/vcm/util/featureconverter/convert.js +83 -0
- package/src/vcs/vcm/util/featureconverter/extent3d.js +154 -0
- package/src/vcs/vcm/util/featureconverter/featureconverterHelper.js +591 -0
- package/src/vcs/vcm/util/featureconverter/lineStringToCesium.js +171 -0
- package/src/vcs/vcm/util/featureconverter/pointToCesium.js +359 -0
- package/src/vcs/vcm/util/featureconverter/polygonToCesium.js +229 -0
- package/src/vcs/vcm/util/geometryHelpers.js +172 -0
- package/src/vcs/vcm/util/indexedCollection.js +158 -0
- package/src/vcs/vcm/util/isMobile.js +12 -0
- package/src/vcs/vcm/util/layerCollection.js +216 -0
- package/src/vcs/vcm/util/locale.js +53 -0
- package/src/vcs/vcm/util/mapCollection.js +363 -0
- package/src/vcs/vcm/util/math.js +71 -0
- package/src/vcs/vcm/util/projection.js +348 -0
- package/src/vcs/vcm/util/splitScreen.js +233 -0
- package/src/vcs/vcm/util/style/declarativeStyleItem.js +631 -0
- package/src/vcs/vcm/util/style/shapesCategory.js +67 -0
- package/src/vcs/vcm/util/style/styleFactory.js +48 -0
- package/src/vcs/vcm/util/style/styleHelpers.js +555 -0
- package/src/vcs/vcm/util/style/styleItem.js +226 -0
- package/src/vcs/vcm/util/style/vectorStyleItem.js +927 -0
- package/src/vcs/vcm/util/style/writeStyle.js +48 -0
- package/src/vcs/vcm/util/urlHelpers.js +16 -0
- package/src/vcs/vcm/util/viewpoint.js +333 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { cartesian2DDistance } from '../util/math.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} ObliqueImageMetaOptions
|
|
5
|
+
* @property {import("ol/coordinate").Coordinate|undefined} "principal-point"
|
|
6
|
+
* @property {import("ol/coordinate").Coordinate|undefined} "pixel-size"
|
|
7
|
+
* @property {Array<number>|undefined} "radial-distorsion-expected-2-found"
|
|
8
|
+
* @property {Array<number>|undefined} "radial-distorsion-found-2-expected"
|
|
9
|
+
* @property {import("ol/size").Size} size
|
|
10
|
+
* @property {import("ol/size").Size} tileSize
|
|
11
|
+
* @property {Array<number>} tileResolution
|
|
12
|
+
* @property {import("ol/proj/Projection").default} projection
|
|
13
|
+
* @property {string} url
|
|
14
|
+
* @property {import("@vcmap/cesium").CesiumTerrainProvider} terrainProvider
|
|
15
|
+
* @property {string} name
|
|
16
|
+
* @property {string|undefined} [format='jpg']
|
|
17
|
+
* @api
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @class
|
|
23
|
+
* @export
|
|
24
|
+
*/
|
|
25
|
+
class ObliqueImageMeta {
|
|
26
|
+
/**
|
|
27
|
+
* @param {ObliqueImageMetaOptions} options
|
|
28
|
+
*/
|
|
29
|
+
constructor(options) {
|
|
30
|
+
/**
|
|
31
|
+
* The name of the camera associated with these meta data
|
|
32
|
+
* @type {string}
|
|
33
|
+
* @api
|
|
34
|
+
* @readonly
|
|
35
|
+
*/
|
|
36
|
+
this.name = options.name;
|
|
37
|
+
/** @type {import("ol/coordinate").Coordinate|undefined} */
|
|
38
|
+
this.principalPoint = options['principal-point'];
|
|
39
|
+
/** @type {import("ol/coordinate").Coordinate|undefined} */
|
|
40
|
+
this.pixelSize = options['pixel-size'];
|
|
41
|
+
/** @type {Array<number>|undefined} */
|
|
42
|
+
this.radialE2F = options['radial-distorsion-expected-2-found'];
|
|
43
|
+
/** @type {Array<number>|undefined} */
|
|
44
|
+
this.radialF2E = options['radial-distorsion-found-2-expected'];
|
|
45
|
+
/** @type {boolean} */
|
|
46
|
+
this.hasRadial = !!(this.pixelSize && (this.radialE2F && this.radialF2E));
|
|
47
|
+
/**
|
|
48
|
+
* The size of the images associated with this meta data
|
|
49
|
+
* @type {import("ol/size").Size}
|
|
50
|
+
* @api
|
|
51
|
+
*/
|
|
52
|
+
this.size = options.size;
|
|
53
|
+
/**
|
|
54
|
+
* The tile size of the images associated with this meta data
|
|
55
|
+
* @type {import("ol/size").Size}
|
|
56
|
+
* @api
|
|
57
|
+
*/
|
|
58
|
+
this.tileSize = options.tileSize;
|
|
59
|
+
/**
|
|
60
|
+
* The tile resolutions of the images associated with this meta data
|
|
61
|
+
* @type {Array<number>}
|
|
62
|
+
* @api
|
|
63
|
+
*/
|
|
64
|
+
this.tileResolution = options.tileResolution;
|
|
65
|
+
/**
|
|
66
|
+
* The world projection of the images associated with this meta
|
|
67
|
+
* @type {import("ol/proj/Projection").default}
|
|
68
|
+
* @api
|
|
69
|
+
*/
|
|
70
|
+
this.projection = options.projection;
|
|
71
|
+
/**
|
|
72
|
+
* @type {string}
|
|
73
|
+
* @api
|
|
74
|
+
*/
|
|
75
|
+
this.url = options.url;
|
|
76
|
+
/**
|
|
77
|
+
* An optional terrain provider
|
|
78
|
+
* @type {import("@vcmap/cesium").CesiumTerrainProvider}
|
|
79
|
+
* @api
|
|
80
|
+
*/
|
|
81
|
+
this.terrainProvider = options.terrainProvider;
|
|
82
|
+
/**
|
|
83
|
+
* @type {string}
|
|
84
|
+
* @api
|
|
85
|
+
*/
|
|
86
|
+
this.format = options.format || 'jpg';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Removes radial distortion in image coordinates. Radial coefficients must be provided
|
|
91
|
+
* @param {import("ol/coordinate").Coordinate} coordinate
|
|
92
|
+
* @param {boolean=} [useF2E=false] useFound2Expected, if not true expectedToFound is used
|
|
93
|
+
* @returns {import("ol/coordinate").Coordinate}
|
|
94
|
+
* @api
|
|
95
|
+
*/
|
|
96
|
+
radialDistortionCoordinate(coordinate, useF2E) {
|
|
97
|
+
if (this.hasRadial && this.principalPoint) {
|
|
98
|
+
const coefficientsArray = useF2E ? this.radialF2E : this.radialE2F;
|
|
99
|
+
|
|
100
|
+
const distC2PPInMM = cartesian2DDistance(this.principalPoint, coordinate) * this.pixelSize[0];
|
|
101
|
+
if (distC2PPInMM === 0) {
|
|
102
|
+
return coordinate.slice();
|
|
103
|
+
}
|
|
104
|
+
const diffX = coordinate[0] - this.principalPoint[0];
|
|
105
|
+
const diffY = coordinate[1] - this.principalPoint[1];
|
|
106
|
+
|
|
107
|
+
// get shift value
|
|
108
|
+
let shift = 0;
|
|
109
|
+
for (let i = 0; i < coefficientsArray.length; ++i) {
|
|
110
|
+
shift += coefficientsArray[i] * (distC2PPInMM ** i);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// get new position through spherical coordinates system - http://mathworld.wolfram.com/SphericalCoordinates.html
|
|
114
|
+
const newDistInPixel = (distC2PPInMM + shift) / this.pixelSize[0];
|
|
115
|
+
const angleTheta = Math.atan2(diffY, diffX);
|
|
116
|
+
return [
|
|
117
|
+
this.principalPoint[0] + (newDistInPixel * Math.cos(angleTheta)),
|
|
118
|
+
this.principalPoint[1] + (newDistInPixel * Math.sin(angleTheta)),
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return coordinate.slice();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default ObliqueImageMeta;
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { Event as CesiumEvent } from '@vcmap/cesium';
|
|
2
|
+
import { getTransform } from 'ol/proj.js';
|
|
3
|
+
import View from 'ol/View.js';
|
|
4
|
+
import { unByKey } from 'ol/Observable.js';
|
|
5
|
+
import { DataState } from './ObliqueDataSet.js';
|
|
6
|
+
import OLView from './ObliqueView.js';
|
|
7
|
+
import { destroyCesiumEvent, transformFromImage } from './helpers.js';
|
|
8
|
+
import { getHeightFromTerrainProvider } from '../layer/terrainHelpers.js';
|
|
9
|
+
import { mercatorProjection } from '../util/projection.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} ObliqueViewPoint
|
|
13
|
+
* @property {import("ol/coordinate").Coordinate} center - in mercator
|
|
14
|
+
* @property {number} zoom
|
|
15
|
+
* @property {import("@vcmap/core").ObliqueViewDirection} direction
|
|
16
|
+
* @api
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {number} number
|
|
21
|
+
* @param {number} max
|
|
22
|
+
* @returns {number}
|
|
23
|
+
*/
|
|
24
|
+
function withinBounds(number, max) {
|
|
25
|
+
if (number < 0) {
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (number > max) {
|
|
30
|
+
return max;
|
|
31
|
+
}
|
|
32
|
+
return number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @class
|
|
37
|
+
* @export
|
|
38
|
+
*/
|
|
39
|
+
class ObliqueProvider {
|
|
40
|
+
/**
|
|
41
|
+
* @param {import("ol").Map} olMap
|
|
42
|
+
*/
|
|
43
|
+
constructor(olMap) {
|
|
44
|
+
this._active = false;
|
|
45
|
+
/** @type {import("@vcmap/core").ObliqueImage|string|null} */
|
|
46
|
+
this._loadingImage = null;
|
|
47
|
+
/** @type {import("ol").Map} */
|
|
48
|
+
this._olMap = olMap;
|
|
49
|
+
this._viewCache = new Map();
|
|
50
|
+
/**
|
|
51
|
+
* @type {import("@vcmap/core").ObliqueImage|null}
|
|
52
|
+
*/
|
|
53
|
+
this._currentImage = null; // XXX defaultImage
|
|
54
|
+
/**
|
|
55
|
+
* @type {import("@vcmap/core").ObliqueView|null}
|
|
56
|
+
* @private
|
|
57
|
+
*/
|
|
58
|
+
this._currentView = null;
|
|
59
|
+
/** @type {import("@vcmap/core").ObliqueCollection} */
|
|
60
|
+
this._collection = null; // XXX should we also make a default collection?
|
|
61
|
+
/** @type {string} */
|
|
62
|
+
this._mapChangeEvent = 'postrender';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Event raised once a new image is set on the provider. Will be passed the new image as the only argument.
|
|
66
|
+
* @type {import("@vcmap/cesium").Event}
|
|
67
|
+
* @api
|
|
68
|
+
*/
|
|
69
|
+
this.imageChanged = new CesiumEvent();
|
|
70
|
+
/**
|
|
71
|
+
* Whether the post render handler should switch on image edge. Setting
|
|
72
|
+
* this to false will suspend all post render handler switches.
|
|
73
|
+
* @type {boolean}
|
|
74
|
+
* @api
|
|
75
|
+
*/
|
|
76
|
+
this.switchEnabled = true;
|
|
77
|
+
/**
|
|
78
|
+
* Threshold from 0 to 1 to define when to start switching to other images. Where 0 indicates
|
|
79
|
+
* to only switch, when the view center is outside of the image and 1 to always switch. 0.2 would start switching
|
|
80
|
+
* if the view center is within the outer 20% of the image.
|
|
81
|
+
* @type {number}
|
|
82
|
+
* @api
|
|
83
|
+
*/
|
|
84
|
+
this.switchThreshold = 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The event to listen to on the map. can be 'postrender' or 'moveend'. Default is 'postrender'
|
|
89
|
+
* @type {string}
|
|
90
|
+
* @api
|
|
91
|
+
*/
|
|
92
|
+
get mapChangeEvent() {
|
|
93
|
+
return this._mapChangeEvent;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} eventName
|
|
98
|
+
*/
|
|
99
|
+
set mapChangeEvent(eventName) {
|
|
100
|
+
this._mapChangeEvent = eventName;
|
|
101
|
+
if (this._active) {
|
|
102
|
+
if (this._postRenderListener) {
|
|
103
|
+
unByKey(this._postRenderListener);
|
|
104
|
+
}
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
this._postRenderListener = this._olMap.on(this._mapChangeEvent, this._postRenderHandler.bind(this));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @returns {boolean}
|
|
112
|
+
* @api
|
|
113
|
+
*/
|
|
114
|
+
get loading() {
|
|
115
|
+
return !!this._loadingImage;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @returns {boolean}
|
|
120
|
+
* @api
|
|
121
|
+
*/
|
|
122
|
+
get active() {
|
|
123
|
+
return this._active;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @readonly
|
|
128
|
+
* @type {import("@vcmap/core").ObliqueImage|null}
|
|
129
|
+
* @api
|
|
130
|
+
*/
|
|
131
|
+
get currentImage() { return this._currentImage; }
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @readonly
|
|
135
|
+
* @type {import("@vcmap/core").ObliqueCollection|null}
|
|
136
|
+
* @api
|
|
137
|
+
*/
|
|
138
|
+
get collection() { return this._collection; }
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Set a new collection. The collection must be loaded.
|
|
142
|
+
* If a previous collection was set, the current image and its resources will be removed from the olMap.
|
|
143
|
+
* @param {import("@vcmap/core").ObliqueCollection} collection
|
|
144
|
+
* @api
|
|
145
|
+
*/
|
|
146
|
+
setCollection(collection) {
|
|
147
|
+
this._loadingImage = null;
|
|
148
|
+
if (!collection.loaded) {
|
|
149
|
+
// eslint-disable-next-line no-console
|
|
150
|
+
console.error('cannot set an unloaded collection');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this._collection = collection;
|
|
154
|
+
this._removeCurrentView();
|
|
155
|
+
this._currentView = null;
|
|
156
|
+
this._currentImage = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Activate the provider, its current view and its post render handler
|
|
161
|
+
* @api
|
|
162
|
+
*/
|
|
163
|
+
activate() {
|
|
164
|
+
if (!this._collection) {
|
|
165
|
+
throw new Error('cannot activate provider without an oblique collection.');
|
|
166
|
+
}
|
|
167
|
+
if (!this._active) {
|
|
168
|
+
this._active = true;
|
|
169
|
+
this._setCurrentView();
|
|
170
|
+
if (!this._postRenderListener) {
|
|
171
|
+
// @ts-ignore
|
|
172
|
+
this._postRenderListener = this._olMap.on(this._mapChangeEvent, this._postRenderHandler.bind(this));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Deactivates the provider, removing the current view and post render handler from the map
|
|
179
|
+
* @api
|
|
180
|
+
*/
|
|
181
|
+
deactivate() {
|
|
182
|
+
if (this._currentView) {
|
|
183
|
+
this._removeCurrentView();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (this._postRenderListener) {
|
|
187
|
+
unByKey(this._postRenderListener);
|
|
188
|
+
this._postRenderListener = null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this._active = false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {import("ol/coordinate").Coordinate} coord
|
|
196
|
+
* @returns {import("ol/coordinate").Coordinate}
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
_pullCoordinateToImageCenter(coord) {
|
|
200
|
+
if (this.currentImage) {
|
|
201
|
+
const center = [this.currentImage.meta.size[0] / 2, this.currentImage.meta.size[1] / 2];
|
|
202
|
+
if (coord[0] < center[0]) {
|
|
203
|
+
coord[0] += 50;
|
|
204
|
+
} else {
|
|
205
|
+
coord[0] -= 50;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (coord[1] < center[1]) {
|
|
209
|
+
coord[1] += 50;
|
|
210
|
+
} else {
|
|
211
|
+
coord[1] -= 50;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return coord;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
_postRenderHandler() {
|
|
218
|
+
if (this._active && !this.loading && this.switchEnabled) {
|
|
219
|
+
const currentSize = this._currentImage ? this._currentImage.meta.size : null;
|
|
220
|
+
const imageCoordinates = this._olMap.getView().getCenter();
|
|
221
|
+
const ratioLower = this.switchThreshold; // XXX this.switchThreshold;
|
|
222
|
+
const ratioUpper = 1 - ratioLower;
|
|
223
|
+
if (
|
|
224
|
+
!this._currentImage || (
|
|
225
|
+
imageCoordinates[0] / currentSize[0] > ratioLower &&
|
|
226
|
+
imageCoordinates[0] / currentSize[0] < ratioUpper &&
|
|
227
|
+
imageCoordinates[1] / currentSize[1] > ratioLower &&
|
|
228
|
+
imageCoordinates[1] / currentSize[1] < ratioUpper
|
|
229
|
+
)
|
|
230
|
+
) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const pulledCenter = this._pullCoordinateToImageCenter(imageCoordinates.slice());
|
|
234
|
+
const worldCoords = this._currentImage.transformImage2RealWorld(pulledCenter).slice(0, 2);
|
|
235
|
+
const transform = getTransform(this._currentImage.meta.projection, 'EPSG:3857');
|
|
236
|
+
const mercatorCoords = transform(worldCoords);
|
|
237
|
+
const buffer = 200; // XXX make configurable?
|
|
238
|
+
const extent = [
|
|
239
|
+
mercatorCoords[0] - buffer, mercatorCoords[1] - buffer,
|
|
240
|
+
mercatorCoords[0] + buffer, mercatorCoords[1] + buffer,
|
|
241
|
+
];
|
|
242
|
+
const dataState = this._collection.getDataStateForExtent(extent);
|
|
243
|
+
if (dataState === DataState.READY) {
|
|
244
|
+
const image = this._collection.getImageForCoordinate(mercatorCoords, this._currentImage.viewDirection);
|
|
245
|
+
if (image && image.name !== this._currentImage.name) {
|
|
246
|
+
this._changeImage(image, imageCoordinates);
|
|
247
|
+
}
|
|
248
|
+
} else if (dataState === DataState.PENDING) {
|
|
249
|
+
this._collection.loadDataForExtent(extent);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async _changeImage(image, imageCoordinates) {
|
|
255
|
+
this._loadingImage = image;
|
|
256
|
+
const { coords } = await transformFromImage(this._currentImage, imageCoordinates);
|
|
257
|
+
if (this._loadingImage !== image) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
await this.setImage(image, coords);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Sets the current image
|
|
265
|
+
* @param {import("@vcmap/core").ObliqueImage} image
|
|
266
|
+
* @param {import("ol/coordinate").Coordinate=} optCenter - mercator coordinates of an optional center to use. uses the images center if undefined
|
|
267
|
+
* @returns {Promise<boolean>}
|
|
268
|
+
* @api
|
|
269
|
+
*/
|
|
270
|
+
async setImage(image, optCenter) {
|
|
271
|
+
if (!this._collection) {
|
|
272
|
+
throw new Error('cannot set an image without an oblique collection.');
|
|
273
|
+
}
|
|
274
|
+
this._loadingImage = image;
|
|
275
|
+
const isNewImage = !this._currentImage || this._currentImage.name !== image.name;
|
|
276
|
+
this._currentImage = image;
|
|
277
|
+
if (isNewImage) {
|
|
278
|
+
await image.calculateImageAverageHeight();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (image !== this._loadingImage) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let olView;
|
|
286
|
+
if (this._viewCache.has(image.meta)) {
|
|
287
|
+
olView = this._viewCache.get(image.meta);
|
|
288
|
+
} else {
|
|
289
|
+
olView = new OLView(image.meta, this._collection.viewOptions);
|
|
290
|
+
this._viewCache.set(image.meta, olView);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const previousView = this._currentView;
|
|
294
|
+
this._currentView = olView;
|
|
295
|
+
if (isNewImage) {
|
|
296
|
+
this._currentView.setImageName(this._currentImage.name);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const [width, height] = this._currentImage.meta.size;
|
|
300
|
+
let center = [width / 2, height / 2];
|
|
301
|
+
if (optCenter) {
|
|
302
|
+
const worldCenter = getTransform('EPSG:3857', this._currentImage.meta.projection)(optCenter.slice(0, 2));
|
|
303
|
+
const imageCenter = this._currentImage.transformRealWorld2Image(worldCenter, optCenter[2]);
|
|
304
|
+
imageCenter[0] = withinBounds(imageCenter[0], width);
|
|
305
|
+
imageCenter[1] = withinBounds(imageCenter[1], height);
|
|
306
|
+
center = imageCenter;
|
|
307
|
+
}
|
|
308
|
+
this._currentView.view.setCenter(center);
|
|
309
|
+
|
|
310
|
+
if (this._active) {
|
|
311
|
+
this._setCurrentView(previousView);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this._loadingImage = null;
|
|
315
|
+
if (isNewImage) {
|
|
316
|
+
this.imageChanged.raiseEvent(image);
|
|
317
|
+
}
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @param {import("@vcmap/core").ObliqueView=} previousView
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
_setCurrentView(previousView) {
|
|
326
|
+
if (this._currentView) {
|
|
327
|
+
const isSame = previousView && previousView === this._currentView;
|
|
328
|
+
if (!isSame) {
|
|
329
|
+
if (previousView) {
|
|
330
|
+
this._olMap.removeLayer(previousView.layer);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (this._olMap.getView() && this._olMap.getView().getResolution()) {
|
|
334
|
+
this._currentView.view.setResolution(this._olMap.getView().getResolution());
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
this._olMap.setView(this._currentView.view);
|
|
338
|
+
this._olMap.getLayers().insertAt(0, this._currentView.layer);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
_removeCurrentView() {
|
|
344
|
+
if (this._currentView) {
|
|
345
|
+
if (this._olMap.getView() === this._currentView.view) {
|
|
346
|
+
this._olMap.setView(new View());
|
|
347
|
+
}
|
|
348
|
+
this._olMap.removeLayer(this._currentView.layer);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Sets a new image based on a ground coordinate and a direction.
|
|
354
|
+
* @param {import("ol/coordinate").Coordinate} coordinate
|
|
355
|
+
* @param {import("@vcmap/core").ObliqueViewDirection} direction
|
|
356
|
+
* @param {number} [zoom=2]
|
|
357
|
+
* @returns {Promise<void>}
|
|
358
|
+
* @api
|
|
359
|
+
*/
|
|
360
|
+
async setView(coordinate, direction, zoom = 2) {
|
|
361
|
+
if (!this._collection) {
|
|
362
|
+
throw new Error('cannot set the view without an oblique collection.');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const usedCoordinate = coordinate.slice();
|
|
366
|
+
const coordinateHash = `${coordinate.join('')}${direction}${zoom}`;
|
|
367
|
+
this._loadingImage = coordinateHash;
|
|
368
|
+
const image = await this._collection.loadImageForCoordinate(coordinate, direction);
|
|
369
|
+
if (image) {
|
|
370
|
+
if (this._loadingImage !== coordinateHash) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
this._loadingImage = image;
|
|
374
|
+
if (!usedCoordinate[2] && image.meta.terrainProvider) {
|
|
375
|
+
const transformResult = [usedCoordinate];
|
|
376
|
+
await getHeightFromTerrainProvider(
|
|
377
|
+
image.meta.terrainProvider,
|
|
378
|
+
transformResult,
|
|
379
|
+
mercatorProjection,
|
|
380
|
+
transformResult,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (this._loadingImage !== image) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const imageSet = await this.setImage(image, usedCoordinate);
|
|
387
|
+
if (imageSet) {
|
|
388
|
+
this._currentView.view.setZoom(zoom);
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
throw new Error('could not find an image for this direction');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Returns a viewpoint for the currently set view.
|
|
397
|
+
* @returns {Promise<ObliqueViewPoint>}
|
|
398
|
+
* @api
|
|
399
|
+
*/
|
|
400
|
+
async getView() {
|
|
401
|
+
if (this._currentView && this._currentImage) {
|
|
402
|
+
const imageCoord = this._currentView.view.getCenter();
|
|
403
|
+
const { coords: center } = await transformFromImage(this._currentImage, imageCoord);
|
|
404
|
+
return {
|
|
405
|
+
center,
|
|
406
|
+
direction: this._currentImage.viewDirection,
|
|
407
|
+
zoom: this._currentView.view.getZoom(),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Destroys all openlayers resources created by this oblique provider
|
|
415
|
+
* @api
|
|
416
|
+
*/
|
|
417
|
+
destroy() {
|
|
418
|
+
this._removeCurrentView();
|
|
419
|
+
[...this._viewCache.values()].forEach((ov) => { ov.destroy(); });
|
|
420
|
+
this._viewCache.clear();
|
|
421
|
+
this._loadingImage = null;
|
|
422
|
+
if (this._postRenderListener) {
|
|
423
|
+
unByKey(this._postRenderListener);
|
|
424
|
+
this._postRenderListener = null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
destroyCesiumEvent(this.imageChanged);
|
|
428
|
+
this._collection = null;
|
|
429
|
+
this._olMap = null;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export default ObliqueProvider;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import OLProjection from 'ol/proj/Projection.js';
|
|
3
|
+
import View from 'ol/View.js';
|
|
4
|
+
import TileGrid from 'ol/tilegrid/TileGrid.js';
|
|
5
|
+
import TileImage from 'ol/source/TileImage.js';
|
|
6
|
+
import Tile from 'ol/layer/Tile.js';
|
|
7
|
+
import { hasSameOrigin } from './helpers.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} ObliqueViewOptions
|
|
11
|
+
* @property {number} minZoom
|
|
12
|
+
* @property {number} maxZoom
|
|
13
|
+
* @property {number} scaleFactor
|
|
14
|
+
* @property {number} hideLevels
|
|
15
|
+
* @api
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @class
|
|
20
|
+
* @export
|
|
21
|
+
*/
|
|
22
|
+
class ObliqueView {
|
|
23
|
+
/**
|
|
24
|
+
* @param {import("@vcmap/core").ObliqueImageMeta} imageMeta
|
|
25
|
+
* @param {ObliqueViewOptions} options
|
|
26
|
+
*/
|
|
27
|
+
constructor(imageMeta, options) {
|
|
28
|
+
/** @type {string} */
|
|
29
|
+
this.id = uuidv4();
|
|
30
|
+
/** @type {import("ol/size").Size} */
|
|
31
|
+
this.size = imageMeta.size;
|
|
32
|
+
/** @type {string} */
|
|
33
|
+
this.url = imageMeta.url;
|
|
34
|
+
/** @type {import("ol/size").Size} */
|
|
35
|
+
this.tileSize = imageMeta.tileSize;
|
|
36
|
+
/** @type {string} */
|
|
37
|
+
this.format = imageMeta.format;
|
|
38
|
+
/** @type {number} */
|
|
39
|
+
this.minZoom = options.minZoom;
|
|
40
|
+
/** @type {number} */
|
|
41
|
+
this.maxZoom = options.maxZoom;
|
|
42
|
+
/** @type {number} */
|
|
43
|
+
this.scaleFactor = options.scaleFactor;
|
|
44
|
+
const { tileResolution } = imageMeta;
|
|
45
|
+
/** @type {Array<number>} */
|
|
46
|
+
this.tileResolution = tileResolution.slice(0, tileResolution.length - options.hideLevels);
|
|
47
|
+
this._createViewAndLayer();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_createViewAndLayer() {
|
|
51
|
+
const extent = /** @type {import("ol/extent").Extent} */ ([0, 0, ...this.size]);
|
|
52
|
+
const zoomifyProjection = new OLProjection({
|
|
53
|
+
code: 'ZOOMIFY',
|
|
54
|
+
units: 'pixels',
|
|
55
|
+
extent,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const maxZoom = this.maxZoom > 0 ? this.maxZoom : this.tileResolution.length + 4;
|
|
59
|
+
const zoomMultiplier = Math.log(2) / Math.log(this.scaleFactor);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The view for these oblique images.
|
|
63
|
+
* @type {import("ol/View").default}
|
|
64
|
+
* @api
|
|
65
|
+
*/
|
|
66
|
+
this.view = new View({
|
|
67
|
+
projection: zoomifyProjection,
|
|
68
|
+
center: [this.size[0] / 2, this.size[1] / 2],
|
|
69
|
+
constrainOnlyCenter: true,
|
|
70
|
+
minZoom: this.minZoom * zoomMultiplier,
|
|
71
|
+
maxZoom: maxZoom * zoomMultiplier,
|
|
72
|
+
extent: /** @type {import("ol/extent").Extent} */ ([
|
|
73
|
+
-2000,
|
|
74
|
+
-2000,
|
|
75
|
+
this.size[0] + 2000,
|
|
76
|
+
this.size[1] + 2000,
|
|
77
|
+
]),
|
|
78
|
+
zoom: this.minZoom * zoomMultiplier,
|
|
79
|
+
zoomFactor: this.scaleFactor,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const tileImageOptions = {
|
|
83
|
+
projection: zoomifyProjection,
|
|
84
|
+
tileGrid: new TileGrid({
|
|
85
|
+
origin: [0, 0],
|
|
86
|
+
extent,
|
|
87
|
+
resolutions: this.tileResolution,
|
|
88
|
+
tileSize: this.tileSize,
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
if (!hasSameOrigin(this.url)) {
|
|
92
|
+
tileImageOptions.crossOrigin = 'anonymous';
|
|
93
|
+
}
|
|
94
|
+
/** @type {import("ol/source/TileImage").default} */
|
|
95
|
+
this.tileImageSource = new TileImage(tileImageOptions);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The layer of these images.
|
|
99
|
+
* @type {import("ol/layer/Tile").default<import("ol/source/TileImage").default>}
|
|
100
|
+
* @api
|
|
101
|
+
*/
|
|
102
|
+
this.layer = new Tile({
|
|
103
|
+
source: this.tileImageSource,
|
|
104
|
+
extent,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Sets the layers source to request data for this image
|
|
110
|
+
* @param {string} name
|
|
111
|
+
* @api
|
|
112
|
+
*/
|
|
113
|
+
setImageName(name) {
|
|
114
|
+
this.tileImageSource.setTileUrlFunction((coords) => {
|
|
115
|
+
const [z, x, yInverted] = coords;
|
|
116
|
+
const y = -yInverted - 1;
|
|
117
|
+
return `${this.url}/${name}/${z}/${x}/${y}.${this.format}`;
|
|
118
|
+
});
|
|
119
|
+
this.tileImageSource.refresh();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
destroy() {
|
|
123
|
+
this.view = null;
|
|
124
|
+
this.layer = null;
|
|
125
|
+
this.tileImageSource.clear();
|
|
126
|
+
this.tileImageSource = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default ObliqueView;
|