@vcmap/core 5.0.0-rc.1 → 5.0.0-rc.10
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 +1 -1
- package/README.md +3 -2
- package/index.d.ts +2208 -2072
- package/index.js +137 -131
- package/package.json +10 -22
- package/src/category/appBackedCategory.js +76 -0
- package/src/category/category.js +401 -0
- package/src/category/categoryCollection.js +145 -0
- package/src/cesium/cesium3DTileFeature.js +1 -1
- package/src/classRegistry.js +168 -0
- package/src/context.js +73 -0
- package/src/{vcs/vcm/util/featureProvider → featureProvider}/abstractFeatureProvider.js +8 -8
- package/src/{vcs/vcm/util/featureProvider → featureProvider}/featureProviderHelpers.js +4 -5
- package/src/{vcs/vcm/util/featureProvider → featureProvider}/featureProviderSymbols.js +0 -0
- package/src/{vcs/vcm/util/featureProvider → featureProvider}/tileProviderFeatureProvider.js +4 -2
- package/src/{vcs/vcm/util/featureProvider → featureProvider}/wmsFeatureProvider.js +25 -17
- package/src/{vcs/vcm/interaction → interaction}/abstractInteraction.js +19 -17
- package/src/{vcs/vcm/interaction → interaction}/coordinateAtPixel.js +6 -9
- package/src/{vcs/vcm/interaction → interaction}/eventHandler.js +3 -3
- package/src/{vcs/vcm/interaction → interaction}/featureAtPixelInteraction.js +6 -19
- package/src/{vcs/vcm/interaction → interaction}/featureProviderInteraction.js +2 -13
- package/src/{vcs/vcm/interaction → interaction}/interactionChain.js +11 -31
- package/src/{vcs/vcm/interaction → interaction}/interactionType.js +0 -0
- package/src/{vcs/vcm/layer/cesium/cesiumTilesetCesium.js → layer/cesium/cesiumTilesetCesiumImpl.js} +8 -10
- package/src/{vcs/vcm/layer → layer}/cesium/clusterContext.js +0 -0
- package/src/{vcs/vcm/layer/cesium/dataSourceCesium.js → layer/cesium/dataSourceCesiumImpl.js} +5 -2
- package/src/{vcs/vcm/layer/cesium/openStreetMapCesium.js → layer/cesium/openStreetMapCesiumImpl.js} +6 -6
- package/src/{vcs/vcm/layer/cesium/rasterLayerCesium.js → layer/cesium/rasterLayerCesiumImpl.js} +5 -5
- package/src/{vcs/vcm/layer/cesium/singleImageCesium.js → layer/cesium/singleImageCesiumImpl.js} +5 -5
- package/src/{vcs/vcm/layer/cesium/terrainCesium.js → layer/cesium/terrainCesiumImpl.js} +4 -4
- package/src/{vcs/vcm/layer/cesium/tmsCesium.js → layer/cesium/tmsCesiumImpl.js} +6 -6
- package/src/{vcs/vcm/layer/cesium/vectorCesium.js → layer/cesium/vectorCesiumImpl.js} +3 -3
- package/src/{vcs/vcm/layer → layer}/cesium/vectorContext.js +0 -0
- package/src/{vcs/vcm/layer/cesium/vectorRasterTileCesium.js → layer/cesium/vectorRasterTileCesiumImpl.js} +5 -5
- package/src/{vcs/vcm/layer → layer}/cesium/vectorTileImageryProvider.js +1 -1
- package/src/{vcs/vcm/layer/cesium/wmsCesium.js → layer/cesium/wmsCesiumImpl.js} +6 -6
- package/src/{vcs/vcm/layer/cesium/wmtsCesium.js → layer/cesium/wmtsCesiumImpl.js} +6 -6
- package/src/{vcs/vcm/layer → layer}/cesium/x3dmHelper.js +0 -0
- package/src/{vcs/vcm/layer/cesiumTileset.js → layer/cesiumTilesetLayer.js} +38 -36
- package/src/{vcs/vcm/layer/czml.js → layer/czmlLayer.js} +14 -16
- package/src/{vcs/vcm/layer/dataSource.js → layer/dataSourceLayer.js} +13 -13
- package/src/{vcs/vcm/layer → layer}/featureLayer.js +17 -30
- package/src/{vcs/vcm/layer/featureStore.js → layer/featureStoreLayer.js} +53 -53
- package/src/{vcs/vcm/layer/featureStoreChanges.js → layer/featureStoreLayerChanges.js} +98 -82
- package/src/{vcs/vcm/layer/featureStoreState.js → layer/featureStoreLayerState.js} +1 -1
- package/src/{vcs/vcm/layer → layer}/featureVisibility.js +6 -5
- package/src/{vcs/vcm/layer → layer}/geojsonHelpers.js +15 -37
- package/src/{vcs/vcm/layer/geojson.js → layer/geojsonLayer.js} +19 -21
- package/src/{vcs/vcm/layer → layer}/globalHider.js +1 -11
- package/src/{vcs/vcm/layer → layer}/layer.js +11 -11
- package/src/{vcs/vcm/layer → layer}/layerImplementation.js +2 -2
- package/src/{vcs/vcm/layer → layer}/layerState.js +0 -0
- package/src/{vcs/vcm/layer → layer}/layerSymbols.js +0 -0
- package/src/{vcs/vcm/layer/oblique/layerOblique.js → layer/oblique/layerObliqueImpl.js} +4 -4
- package/src/{vcs/vcm/layer → layer}/oblique/obliqueHelpers.js +2 -2
- package/src/{vcs/vcm/layer/oblique/vectorOblique.js → layer/oblique/vectorObliqueImpl.js} +6 -6
- package/src/{vcs/vcm/layer/openStreetMap.js → layer/openStreetMapLayer.js} +32 -32
- package/src/{vcs/vcm/layer/openlayers/layerOpenlayers.js → layer/openlayers/layerOpenlayersImpl.js} +5 -5
- package/src/layer/openlayers/openStreetMapOpenlayersImpl.js +27 -0
- package/src/{vcs/vcm/layer/openlayers/rasterLayerOpenlayers.js → layer/openlayers/rasterLayerOpenlayersImpl.js} +15 -14
- package/src/{vcs/vcm/layer/openlayers/singleImageOpenlayers.js → layer/openlayers/singleImageOpenlayersImpl.js} +6 -6
- package/src/{vcs/vcm/layer/openlayers/tileDebugOpenlayers.js → layer/openlayers/tileDebugOpenlayersImpl.js} +5 -5
- package/src/{vcs/vcm/layer/openlayers/tmsOpenlayers.js → layer/openlayers/tmsOpenlayersImpl.js} +7 -7
- package/src/{vcs/vcm/layer/openlayers/vectorOpenlayers.js → layer/openlayers/vectorOpenlayersImpl.js} +6 -6
- package/src/{vcs/vcm/layer/openlayers/vectorTileOpenlayers.js → layer/openlayers/vectorTileOpenlayersImpl.js} +6 -6
- package/src/{vcs/vcm/layer/openlayers/wmsOpenlayers.js → layer/openlayers/wmsOpenlayersImpl.js} +7 -7
- package/src/{vcs/vcm/layer/openlayers/wmtsOpenlayers.js → layer/openlayers/wmtsOpenlayersImpl.js} +7 -7
- package/src/{vcs/vcm/layer/pointCloud.js → layer/pointCloudLayer.js} +26 -33
- package/src/{vcs/vcm/layer → layer}/rasterLayer.js +18 -18
- package/src/{vcs/vcm/layer/singleImage.js → layer/singleImageLayer.js} +20 -19
- package/src/{vcs/vcm/layer → layer}/terrainHelpers.js +13 -49
- package/src/{vcs/vcm/layer/terrain.js → layer/terrainLayer.js} +13 -13
- package/src/{vcs/vcm/layer → layer}/tileLoadedHelper.js +5 -5
- package/src/{vcs/vcm/layer → layer}/tileProvider/mvtTileProvider.js +22 -4
- package/src/layer/tileProvider/staticGeojsonTileProvider.js +82 -0
- package/src/{vcs/vcm/layer → layer}/tileProvider/tileProvider.js +16 -7
- package/src/{vcs/vcm/layer → layer}/tileProvider/urlTemplateTileProvider.js +20 -5
- package/src/{vcs/vcm/layer/tms.js → layer/tmsLayer.js} +19 -19
- package/src/{vcs/vcm/layer → layer}/vectorHelpers.js +5 -5
- package/src/{vcs/vcm/layer/vector.js → layer/vectorLayer.js} +38 -47
- package/src/{vcs/vcm/layer → layer}/vectorProperties.js +3 -3
- package/src/{vcs/vcm/layer → layer}/vectorSymbols.js +0 -0
- package/src/{vcs/vcm/layer/vectorTile.js → layer/vectorTileLayer.js} +32 -41
- package/src/{vcs/vcm/layer/wfs.js → layer/wfsLayer.js} +19 -19
- package/src/{vcs/vcm/layer → layer}/wmsHelpers.js +1 -1
- package/src/{vcs/vcm/layer/wms.js → layer/wmsLayer.js} +22 -22
- package/src/{vcs/vcm/layer/wmts.js → layer/wmtsLayer.js} +20 -20
- package/src/{vcs/vcm/maps → map}/baseOLMap.js +5 -5
- package/src/{vcs/vcm/maps → map}/cameraLimiter.js +11 -16
- package/src/{vcs/vcm/maps/cesium.js → map/cesiumMap.js} +21 -33
- package/src/{vcs/vcm/maps → map}/mapState.js +0 -0
- package/src/{vcs/vcm/maps/oblique.js → map/obliqueMap.js} +42 -56
- package/src/{vcs/vcm/maps/openlayers.js → map/openlayersMap.js} +17 -14
- package/src/{vcs/vcm/maps/map.js → map/vcsMap.js} +35 -20
- package/src/oblique/defaultObliqueCollection.js +62 -0
- package/src/{vcs/vcm/oblique → oblique}/helpers.js +13 -41
- package/src/{vcs/vcm/oblique/ObliqueCollection.js → oblique/obliqueCollection.js} +117 -36
- package/src/{vcs/vcm/oblique/ObliqueDataSet.js → oblique/obliqueDataSet.js} +67 -24
- package/src/{vcs/vcm/oblique/ObliqueImage.js → oblique/obliqueImage.js} +1 -1
- package/src/{vcs/vcm/oblique/ObliqueImageMeta.js → oblique/obliqueImageMeta.js} +4 -4
- package/src/{vcs/vcm/oblique/ObliqueProvider.js → oblique/obliqueProvider.js} +17 -11
- package/src/{vcs/vcm/oblique/ObliqueView.js → oblique/obliqueView.js} +31 -1
- package/src/{vcs/vcm/oblique/ObliqueViewDirection.js → oblique/obliqueViewDirection.js} +0 -0
- package/src/{vcs/vcm/oblique → oblique}/parseImageJson.js +20 -12
- package/src/ol/geom/circle.js +1 -1
- package/src/overrideClassRegistry.js +204 -0
- package/src/{vcs/vcm/util/style → style}/declarativeStyleItem.js +43 -19
- package/src/{vcs/vcm/util/style → style}/shapesCategory.js +0 -0
- package/src/style/styleFactory.js +29 -0
- package/src/{vcs/vcm/util/style → style}/styleHelpers.js +3 -3
- package/src/{vcs/vcm/util/style → style}/styleItem.js +23 -85
- package/src/{vcs/vcm/util/style → style}/vectorStyleItem.js +73 -81
- package/src/{vcs/vcm/util/style → style}/writeStyle.js +4 -7
- package/src/{vcs/vcm/util → util}/clipping/clippingObject.js +10 -10
- package/src/{vcs/vcm/util → util}/clipping/clippingObjectManager.js +2 -2
- package/src/{vcs/vcm/util → util}/clipping/clippingPlaneHelper.js +4 -4
- package/src/{vcs/vcm/util → util}/collection.js +1 -1
- package/src/{vcs/vcm/util → util}/dateTime.js +0 -0
- package/src/{vcs/vcm/util → util}/exclusiveManager.js +0 -0
- package/src/{vcs/vcm/util → util}/extent.js +18 -11
- package/src/{vcs/vcm/util → util}/featureconverter/circleToCesium.js +0 -0
- package/src/{vcs/vcm/util → util}/featureconverter/convert.js +0 -0
- package/src/util/featureconverter/extent3D.js +181 -0
- package/src/{vcs/vcm/util → util}/featureconverter/featureconverterHelper.js +1 -1
- package/src/{vcs/vcm/util → util}/featureconverter/lineStringToCesium.js +0 -0
- package/src/{vcs/vcm/util → util}/featureconverter/pointToCesium.js +3 -3
- package/src/{vcs/vcm/util → util}/featureconverter/polygonToCesium.js +1 -1
- package/src/util/fetch.js +32 -0
- package/src/{vcs/vcm/util → util}/geometryHelpers.js +0 -0
- package/src/{vcs/vcm/util → util}/indexedCollection.js +24 -1
- package/src/{vcs/vcm/util → util}/isMobile.js +0 -0
- package/src/{vcs/vcm/util → util}/layerCollection.js +11 -6
- package/src/{vcs/vcm/util → util}/locale.js +1 -1
- package/src/{vcs/vcm/util → util}/mapCollection.js +63 -21
- package/src/{vcs/vcm/util → util}/math.js +0 -0
- package/src/util/overrideCollection.js +224 -0
- package/src/{vcs/vcm/util → util}/projection.js +39 -24
- package/src/{vcs/vcm/util → util}/splitScreen.js +10 -10
- package/src/{vcs/vcm/util → util}/urlHelpers.js +0 -0
- package/src/{vcs/vcm/util → util}/viewpoint.js +5 -5
- package/src/vcsApp.js +471 -0
- package/src/vcsAppContextHelpers.js +121 -0
- package/src/{vcs/vcm/event/vcsEvent.js → vcsEvent.js} +2 -2
- package/src/{vcs/vcm/object.js → vcsObject.js} +2 -10
- package/src/vcs/vcm/classRegistry.js +0 -106
- package/src/vcs/vcm/globalCollections.js +0 -11
- package/src/vcs/vcm/layer/buildings.js +0 -17
- package/src/vcs/vcm/layer/cesium/pointCloudCesium.js +0 -58
- package/src/vcs/vcm/layer/openlayers/openStreetMapOpenlayers.js +0 -27
- package/src/vcs/vcm/layer/tileProvider/staticGeojsonTileProvider.js +0 -67
- package/src/vcs/vcm/layer/tileProvider/tileProviderFactory.js +0 -28
- package/src/vcs/vcm/util/featureconverter/extent3d.js +0 -154
- package/src/vcs/vcm/util/style/styleFactory.js +0 -48
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { check } from '@vcsuite/check';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { Feature } from 'ol';
|
|
4
|
+
import { contextIdSymbol, destroyCollection } from '../vcsAppContextHelpers.js';
|
|
5
|
+
import makeOverrideCollection, { isOverrideCollection } from '../util/overrideCollection.js';
|
|
6
|
+
import VcsObject from '../vcsObject.js';
|
|
7
|
+
import VectorLayer from '../layer/vectorLayer.js';
|
|
8
|
+
import IndexedCollection from '../util/indexedCollection.js';
|
|
9
|
+
import { parseGeoJSON, writeGeoJSONFeature } from '../layer/geojsonHelpers.js';
|
|
10
|
+
import Collection from '../util/collection.js';
|
|
11
|
+
import { getStyleOrDefaultStyle } from '../style/styleFactory.js';
|
|
12
|
+
import { categoryClassRegistry, getObjectFromClassRegistry } from '../classRegistry.js';
|
|
13
|
+
import OverrideClassRegistry from '../overrideClassRegistry.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {VcsObjectOptions} CategoryOptions
|
|
17
|
+
* @property {string|Object<string, string>} [title]
|
|
18
|
+
* @property {string} [classRegistryName=''] - the class registry name on the current app to provide classes for this category. if provided, parseItems will deserialize using this class registry. See: {@link getObjectFromClassRegistry}.
|
|
19
|
+
* @property {string|undefined} [featureProperty]
|
|
20
|
+
* @property {VectorOptions} [layerOptions={}]
|
|
21
|
+
* @property {Array<Object>} [items] - items are not evaluated by the constructor but passed to parseItem during deserialization.
|
|
22
|
+
* @property {string} [keyProperty=name]
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @type {*|string}
|
|
27
|
+
*/
|
|
28
|
+
const categoryContextId = uuidv4();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {import("@vcmap/core").VectorLayer} layer
|
|
32
|
+
* @param {VectorOptions} options
|
|
33
|
+
* @private
|
|
34
|
+
*/
|
|
35
|
+
function assignLayerOptions(layer, options) {
|
|
36
|
+
if (options.style) {
|
|
37
|
+
layer.setStyle(getStyleOrDefaultStyle(options.style, layer.defaultStyle));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.highlightStyle) {
|
|
41
|
+
const highlightStyle = getStyleOrDefaultStyle(options.highlightStyle, layer.highlightStyle);
|
|
42
|
+
layer.setHighlightStyle(/** @type {import("@vcmap/core").VectorStyleItem} */ (highlightStyle));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (options.vectorProperties) {
|
|
46
|
+
layer.vectorProperties.setValues(options.vectorProperties);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (options.zIndex != null) {
|
|
50
|
+
layer.zIndex = options.zIndex;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} key
|
|
56
|
+
* @param {T} value
|
|
57
|
+
* @param {T} defaultOption
|
|
58
|
+
* @param {T=} option
|
|
59
|
+
* @template {number|boolean|string} T
|
|
60
|
+
* @returns {void}
|
|
61
|
+
*/
|
|
62
|
+
function checkMergeOptionOverride(key, value, defaultOption, option) {
|
|
63
|
+
const isOverride = option == null ? value !== defaultOption : option !== value;
|
|
64
|
+
if (isOverride) {
|
|
65
|
+
throw new Error(`Cannot merge options, values of ${key} do not match`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A category contains user based items and is a special container. The container should not be created directly, but via
|
|
71
|
+
* the requestCategory API on the categories collection. Do not use toJSON to retrieve the state of a category, since
|
|
72
|
+
* categories outlive contexts and may be changed with mergeOptions to no longer reflect your initial state. Requestors
|
|
73
|
+
* should keep track of the requested options themselves.
|
|
74
|
+
* @class
|
|
75
|
+
* @extends {VcsObject}
|
|
76
|
+
* @template {Object|VcsObject} T
|
|
77
|
+
*/
|
|
78
|
+
class Category extends VcsObject {
|
|
79
|
+
static get className() { return 'Category'; }
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @returns {CategoryOptions}
|
|
83
|
+
*/
|
|
84
|
+
static getDefaultConfig() {
|
|
85
|
+
return {
|
|
86
|
+
title: '',
|
|
87
|
+
featureProperty: undefined,
|
|
88
|
+
classRegistryName: undefined,
|
|
89
|
+
layerOptions: {},
|
|
90
|
+
keyProperty: 'name',
|
|
91
|
+
items: [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {CategoryOptions} options
|
|
97
|
+
*/
|
|
98
|
+
constructor(options) {
|
|
99
|
+
super(options);
|
|
100
|
+
const defaultOptions = Category.getDefaultConfig();
|
|
101
|
+
/**
|
|
102
|
+
* @type {string|Object<string, string>}
|
|
103
|
+
*/
|
|
104
|
+
this.title = options.title || this.name;
|
|
105
|
+
/**
|
|
106
|
+
* @type {import("@vcmap/core").VcsApp}
|
|
107
|
+
* @protected
|
|
108
|
+
*/
|
|
109
|
+
this._app = null;
|
|
110
|
+
/**
|
|
111
|
+
* @type {string}
|
|
112
|
+
* @private
|
|
113
|
+
*/
|
|
114
|
+
this._featureProperty = options.featureProperty || defaultOptions.featureProperty;
|
|
115
|
+
/**
|
|
116
|
+
* @type {string}
|
|
117
|
+
* @private
|
|
118
|
+
*/
|
|
119
|
+
this._classRegistryName = options.classRegistryName;
|
|
120
|
+
/**
|
|
121
|
+
* @type {VectorOptions}
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
this._layerOptions = options.layerOptions || defaultOptions.layerOptions;
|
|
125
|
+
/**
|
|
126
|
+
* @type {import("@vcmap/core").VectorLayer}
|
|
127
|
+
* @protected
|
|
128
|
+
*/
|
|
129
|
+
this._layer = null;
|
|
130
|
+
if (this._featureProperty) {
|
|
131
|
+
this._layer = new VectorLayer(this._layerOptions);
|
|
132
|
+
this._layer[contextIdSymbol] = categoryContextId;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* @type {string}
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
this._keyProperty = options.keyProperty || defaultOptions.keyProperty;
|
|
139
|
+
/**
|
|
140
|
+
* @type {Array<function():void>}
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
this._collectionListeners = [];
|
|
144
|
+
/**
|
|
145
|
+
* @type {OverrideCollection<T>}
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
this._collection = null;
|
|
149
|
+
this.setCollection(new IndexedCollection(this._keyProperty));
|
|
150
|
+
/**
|
|
151
|
+
* @type {function():void}
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
this._contextRemovedListener = () => {};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @type {string}
|
|
159
|
+
* @readonly
|
|
160
|
+
*/
|
|
161
|
+
get classRegistryName() { return this._classRegistryName; }
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The collection of this category.
|
|
165
|
+
* @type {OverrideCollection<T>}
|
|
166
|
+
* @readonly
|
|
167
|
+
*/
|
|
168
|
+
get collection() {
|
|
169
|
+
return this._collection;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Returns the layer of this collection. Caution, do not use the layer API to add or remove items.
|
|
174
|
+
* When adding items to the collection, the features are added to the layer async (timeout of 0), since there is weird behavior
|
|
175
|
+
* when removing and adding a feature with the same id in the same sync call.
|
|
176
|
+
* @type {import("@vcmap/core").VectorLayer|null}
|
|
177
|
+
*/
|
|
178
|
+
get layer() {
|
|
179
|
+
return this._layer;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {T} item
|
|
184
|
+
* @protected
|
|
185
|
+
*/
|
|
186
|
+
_itemAdded(item) {
|
|
187
|
+
if (this._featureProperty) {
|
|
188
|
+
const id = item[this._keyProperty];
|
|
189
|
+
this._layer.removeFeaturesById([id]); // this may be a replacement.
|
|
190
|
+
|
|
191
|
+
const geoJsonFeature = item[this._featureProperty];
|
|
192
|
+
let feature;
|
|
193
|
+
if (geoJsonFeature instanceof Feature) {
|
|
194
|
+
feature = geoJsonFeature;
|
|
195
|
+
} else if (typeof geoJsonFeature === 'object') {
|
|
196
|
+
const { features } = parseGeoJSON(geoJsonFeature);
|
|
197
|
+
if (features[0]) { // XXX do we warn on feature collection?
|
|
198
|
+
feature = features[0];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (feature) {
|
|
203
|
+
feature.setId(id);
|
|
204
|
+
setTimeout(() => { this._layer.addFeatures([feature]); }, 0); // We need to set a timeout, since removing and adding the feature in the same sync call leads to undefined behavior in OL TODO recheck in ol 6.11
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @param {T} item
|
|
211
|
+
* @protected
|
|
212
|
+
*/
|
|
213
|
+
_itemRemoved(item) {
|
|
214
|
+
if (this._featureProperty) {
|
|
215
|
+
this._layer.removeFeaturesById([item[this._keyProperty]]);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {T} item
|
|
221
|
+
* @protected
|
|
222
|
+
*/
|
|
223
|
+
// eslint-disable-next-line class-methods-use-this,no-unused-vars
|
|
224
|
+
_itemReplaced(item) {}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @param {T} item
|
|
228
|
+
* @protected
|
|
229
|
+
*/
|
|
230
|
+
// eslint-disable-next-line class-methods-use-this,no-unused-vars
|
|
231
|
+
_itemMoved(item) {}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @returns {string}
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
_getDynamicContextId() {
|
|
238
|
+
if (!this._app) {
|
|
239
|
+
throw new Error('Cannot get dynamic context id, before setting the vcApp');
|
|
240
|
+
}
|
|
241
|
+
return this._app.dynamicContextId;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Throws if typed, featureProperty and keyProperty do not match. Merges other options.
|
|
246
|
+
* Only merges: style, highlightStyle, zIndex & vectorProperties from layerOptions.
|
|
247
|
+
* @param {CategoryOptions} options
|
|
248
|
+
*/
|
|
249
|
+
mergeOptions(options) {
|
|
250
|
+
const defaultOptions = Category.getDefaultConfig();
|
|
251
|
+
checkMergeOptionOverride(
|
|
252
|
+
'classRegistryName',
|
|
253
|
+
this._classRegistryName,
|
|
254
|
+
defaultOptions.classRegistryName,
|
|
255
|
+
options.classRegistryName,
|
|
256
|
+
);
|
|
257
|
+
checkMergeOptionOverride(
|
|
258
|
+
'featureProperty',
|
|
259
|
+
this._featureProperty,
|
|
260
|
+
defaultOptions.featureProperty,
|
|
261
|
+
options.featureProperty,
|
|
262
|
+
);
|
|
263
|
+
checkMergeOptionOverride('keyProperty', this._keyProperty, defaultOptions.keyProperty, options.keyProperty);
|
|
264
|
+
this.title = options.title || this.title;
|
|
265
|
+
if (options.layerOptions && this._layer) {
|
|
266
|
+
assignLayerOptions(this._layer, options.layerOptions);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* When setting the category, it MUST use the same unqiueKey as the previous collection (default is "name").
|
|
272
|
+
* All items in the current collection _will be destroyed_ and the current collection will be destroyed. The category will take
|
|
273
|
+
* complete ownership of the collection and destroy it once the category is destroyed. The collection will
|
|
274
|
+
* be turned into an {@see OverrideCollection}.
|
|
275
|
+
* @param {import("@vcmap/core").Collection<T>} collection
|
|
276
|
+
*/
|
|
277
|
+
setCollection(collection) {
|
|
278
|
+
check(collection, Collection);
|
|
279
|
+
|
|
280
|
+
if (this._keyProperty !== collection.uniqueKey) {
|
|
281
|
+
throw new Error('The collections key property does not match the categories key property');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this._collectionListeners.forEach((cb) => { cb(); });
|
|
285
|
+
if (this._collection) {
|
|
286
|
+
destroyCollection(this._collection);
|
|
287
|
+
}
|
|
288
|
+
if (this._layer) {
|
|
289
|
+
this._layer.removeAllFeatures(); // XXX should we call `itemRemoved` instead?
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this._collection = collection[isOverrideCollection] ?
|
|
293
|
+
/** @type {OverrideCollection} */ (collection) :
|
|
294
|
+
makeOverrideCollection(
|
|
295
|
+
collection,
|
|
296
|
+
this._getDynamicContextId.bind(this),
|
|
297
|
+
this._serializeItem.bind(this),
|
|
298
|
+
this._deserializeItem.bind(this),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
[...this.collection].forEach((item) => { this._itemAdded(item); });
|
|
302
|
+
/**
|
|
303
|
+
* @type {Array<function():void>}
|
|
304
|
+
* @private
|
|
305
|
+
*/
|
|
306
|
+
this._collectionListeners = [
|
|
307
|
+
this._collection.added.addEventListener(this._itemAdded.bind(this)),
|
|
308
|
+
this._collection.removed.addEventListener(this._itemRemoved.bind(this)),
|
|
309
|
+
this._collection.replaced.addEventListener(this._itemReplaced.bind(this)),
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
// @ts-ignore
|
|
313
|
+
if (this._collection.moved) {
|
|
314
|
+
// @ts-ignore
|
|
315
|
+
this._collectionListeners.push(this._collection.moved.addEventListener(this._itemMoved.bind(this)));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @param {import("@vcmap/core").VcsApp} app
|
|
321
|
+
*/
|
|
322
|
+
setApp(app) {
|
|
323
|
+
if (this._app) {
|
|
324
|
+
throw new Error('Cannot switch apps');
|
|
325
|
+
}
|
|
326
|
+
this._app = app;
|
|
327
|
+
this._contextRemovedListener = this._app.contextRemoved.addEventListener((context) => {
|
|
328
|
+
this._collection.removeContext(context.id);
|
|
329
|
+
});
|
|
330
|
+
if (this._layer) {
|
|
331
|
+
this._app.layers.add(this._layer);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* @protected
|
|
337
|
+
* @param {*} config
|
|
338
|
+
* @returns {Promise<T>} // XXX should this still be async?
|
|
339
|
+
*/
|
|
340
|
+
async _deserializeItem(config) {
|
|
341
|
+
if (!this._app) {
|
|
342
|
+
throw new Error('Cannot deserialize item before setting the vcApp');
|
|
343
|
+
}
|
|
344
|
+
const classRegistry = this._classRegistryName ? this._app[this._classRegistryName] : null;
|
|
345
|
+
if (classRegistry && classRegistry instanceof OverrideClassRegistry) {
|
|
346
|
+
return getObjectFromClassRegistry(classRegistry, config);
|
|
347
|
+
}
|
|
348
|
+
return config;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @protected
|
|
353
|
+
* @param {T} item
|
|
354
|
+
* @returns {Array<Object>}
|
|
355
|
+
*/
|
|
356
|
+
_serializeItem(item) {
|
|
357
|
+
const config = JSON.parse(JSON.stringify(item));
|
|
358
|
+
if (this._featureProperty) {
|
|
359
|
+
const feature = this._layer.getFeatureById(item[this._keyProperty]);
|
|
360
|
+
if (feature) {
|
|
361
|
+
config[this._featureProperty] = writeGeoJSONFeature(feature);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return config;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @param {string} contextId
|
|
369
|
+
* @returns {CategoryOptions|null}
|
|
370
|
+
*/
|
|
371
|
+
serializeForContext(contextId) {
|
|
372
|
+
if (this._collection.size === 0) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
name: this.name,
|
|
378
|
+
items: this.collection.serializeContext(contextId),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
destroy() {
|
|
383
|
+
super.destroy();
|
|
384
|
+
if (this._app && this._layer) {
|
|
385
|
+
this._app.layers.remove(this._layer);
|
|
386
|
+
}
|
|
387
|
+
if (this._layer) {
|
|
388
|
+
this._layer.destroy();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
this._collectionListeners.forEach((cb) => { cb(); });
|
|
392
|
+
this._collectionListeners.splice(0);
|
|
393
|
+
this._contextRemovedListener();
|
|
394
|
+
this._contextRemovedListener = () => {};
|
|
395
|
+
destroyCollection(this._collection);
|
|
396
|
+
this._app = null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export default Category;
|
|
401
|
+
categoryClassRegistry.registerClass(Category.className, Category);
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { getLogger as getLoggerByName } from '@vcsuite/logger';
|
|
2
|
+
import Category from './category.js';
|
|
3
|
+
import './appBackedCategory.js';
|
|
4
|
+
import IndexedCollection from '../util/indexedCollection.js';
|
|
5
|
+
import { getObjectFromClassRegistry } from '../classRegistry.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @returns {import("@vcsuite/logger").Logger}
|
|
9
|
+
*/
|
|
10
|
+
function getLogger() {
|
|
11
|
+
return getLoggerByName('CategoryCollection');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @class
|
|
16
|
+
* @extends {IndexedCollection<Category<Object|import("@vcmap/core").VcsObject>>}
|
|
17
|
+
*/
|
|
18
|
+
class CategoryCollection extends IndexedCollection {
|
|
19
|
+
/**
|
|
20
|
+
* @param {import("@vcmap/core").VcsApp} app
|
|
21
|
+
*/
|
|
22
|
+
constructor(app) {
|
|
23
|
+
super();
|
|
24
|
+
/**
|
|
25
|
+
* @type {import("@vcmap/core").VcsApp}
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
this._app = app;
|
|
29
|
+
/**
|
|
30
|
+
* Map of category names, where the value is a map of contextId and items.
|
|
31
|
+
* @type {Map<string, Map<string, Array<Object>>>}
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
this._cache = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* @type {Function}
|
|
37
|
+
* @private
|
|
38
|
+
*/
|
|
39
|
+
this._contextRemovedListener = this._app.contextRemoved.addEventListener((context) => {
|
|
40
|
+
this._cache.forEach((contextMap, name) => {
|
|
41
|
+
contextMap.delete(context.id);
|
|
42
|
+
if (contextMap.size === 0) {
|
|
43
|
+
this._cache.delete(name);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Do not call add directly. Use request category for adding categories.
|
|
51
|
+
* @param {Category<Object|import("@vcmap/core").VcsObject>} category
|
|
52
|
+
* @returns {number|null}
|
|
53
|
+
*/
|
|
54
|
+
add(category) { // XXX use a symbol to enforce using request over add?
|
|
55
|
+
if (this.hasKey(category.name)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
category.setApp(this._app);
|
|
60
|
+
const added = super.add(category);
|
|
61
|
+
if (added != null && this._cache.has(category.name)) {
|
|
62
|
+
this._cache
|
|
63
|
+
.get(category.name)
|
|
64
|
+
.forEach((items, contextId) => {
|
|
65
|
+
this.parseCategoryItems(category.name, items, contextId);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
this._cache.delete(category.name);
|
|
69
|
+
}
|
|
70
|
+
return added;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Categories should be static. Removing them can lead to undefined behavior.
|
|
75
|
+
* @param {Category<Object|import("@vcmap/core").VcsObject>} category
|
|
76
|
+
*/
|
|
77
|
+
remove(category) { // XXX add logger warning?
|
|
78
|
+
super.remove(category);
|
|
79
|
+
this._cache.delete(category.name);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Parses the category items. Items will only be parsed, if a category with said name exists. Otherwise,
|
|
84
|
+
* they will be cached, until such a category is requested.
|
|
85
|
+
* @param {string} name
|
|
86
|
+
* @param {Array<Object>} items
|
|
87
|
+
* @param {string} contextId
|
|
88
|
+
* @returns {Promise<void>}
|
|
89
|
+
*/
|
|
90
|
+
async parseCategoryItems(name, items, contextId) {
|
|
91
|
+
const category = this.getByKey(name);
|
|
92
|
+
|
|
93
|
+
if (category) {
|
|
94
|
+
await category.collection.parseItems(items, contextId);
|
|
95
|
+
} else if (this._cache.has(name)) {
|
|
96
|
+
this._cache.get(name).set(contextId, items);
|
|
97
|
+
} else {
|
|
98
|
+
this._cache.set(name, new Map([[contextId, items]]));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Add categories with this API.
|
|
104
|
+
* @param {CategoryOptions} options
|
|
105
|
+
* @returns {Promise<Category<Object|import("@vcmap/core").VcsObject>>}
|
|
106
|
+
*/
|
|
107
|
+
async requestCategory(options) {
|
|
108
|
+
if (!options.name) {
|
|
109
|
+
getLogger().error('Cannot request a category without a name');
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!options.type) {
|
|
114
|
+
getLogger().warning(`Implicitly typing category ${options.name} as ${Category.className}`);
|
|
115
|
+
options.type = Category.className;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let category;
|
|
119
|
+
if (this.hasKey(options.name)) {
|
|
120
|
+
category = this.getByKey(options.name);
|
|
121
|
+
category.mergeOptions(options);
|
|
122
|
+
} else {
|
|
123
|
+
category = await getObjectFromClassRegistry(this._app.categoryClassRegistry, options);
|
|
124
|
+
if (category) {
|
|
125
|
+
if (this.add(category) == null) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!category) {
|
|
132
|
+
throw new Error(`Category ${options.name} with type ${options.type} could not be created`);
|
|
133
|
+
}
|
|
134
|
+
return category;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
destroy() {
|
|
138
|
+
super.destroy();
|
|
139
|
+
this._contextRemovedListener();
|
|
140
|
+
this._cache.clear();
|
|
141
|
+
this._app = null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export default CategoryCollection;
|
|
@@ -5,5 +5,5 @@ import { Cesium3DTileFeature } from '@vcmap/cesium';
|
|
|
5
5
|
* @returns {string|number}
|
|
6
6
|
*/
|
|
7
7
|
Cesium3DTileFeature.prototype.getId = function getId() {
|
|
8
|
-
return this.getProperty('id') || `${this.content.url}${this._batchId}`;
|
|
8
|
+
return this.getProperty('id') || `${this.content.url}${this._batchId}`; // XXX there is a new property `featureId` on the Cesium3DTileset. this may cause issues when picking b3dm.
|
|
9
9
|
};
|