@vcmap/core 5.0.0-rc.4 → 5.0.0-rc.7

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 (42) hide show
  1. package/index.d.ts +274 -72
  2. package/index.js +11 -2
  3. package/package.json +9 -16
  4. package/src/vcs/vcm/category/appBackedCategory.js +41 -0
  5. package/src/vcs/vcm/category/category.js +374 -0
  6. package/src/vcs/vcm/category/categoryCollection.js +145 -0
  7. package/src/vcs/vcm/context.js +73 -0
  8. package/src/vcs/vcm/event/vcsEvent.js +2 -2
  9. package/src/vcs/vcm/globalCollections.js +1 -5
  10. package/src/vcs/vcm/layer/featureStore.js +2 -2
  11. package/src/vcs/vcm/layer/featureStoreChanges.js +89 -73
  12. package/src/vcs/vcm/layer/featureVisibility.js +3 -2
  13. package/src/vcs/vcm/layer/geojson.js +3 -5
  14. package/src/vcs/vcm/layer/geojsonHelpers.js +3 -3
  15. package/src/vcs/vcm/layer/openlayers/rasterLayerOpenlayers.js +3 -2
  16. package/src/vcs/vcm/layer/tileProvider/mvtTileProvider.js +3 -3
  17. package/src/vcs/vcm/layer/tileProvider/staticGeojsonTileProvider.js +3 -3
  18. package/src/vcs/vcm/layer/tileProvider/urlTemplateTileProvider.js +3 -3
  19. package/src/vcs/vcm/layer/vectorHelpers.js +4 -4
  20. package/src/vcs/vcm/layer/wfs.js +5 -5
  21. package/src/vcs/vcm/maps/oblique.js +19 -23
  22. package/src/vcs/vcm/maps/openlayers.js +6 -3
  23. package/src/vcs/vcm/oblique/ObliqueCollection.js +33 -5
  24. package/src/vcs/vcm/oblique/ObliqueDataSet.js +7 -7
  25. package/src/vcs/vcm/oblique/ObliqueImageMeta.js +2 -2
  26. package/src/vcs/vcm/oblique/ObliqueProvider.js +6 -3
  27. package/src/vcs/vcm/oblique/ObliqueView.js +31 -1
  28. package/src/vcs/vcm/oblique/defaultObliqueCollection.js +62 -0
  29. package/src/vcs/vcm/util/clipping/clippingPlaneHelper.js +4 -4
  30. package/src/vcs/vcm/util/featureProvider/featureProviderHelpers.js +3 -4
  31. package/src/vcs/vcm/util/featureProvider/wmsFeatureProvider.js +13 -7
  32. package/src/vcs/vcm/util/featureconverter/extent3D.js +181 -0
  33. package/src/vcs/vcm/util/fetch.js +32 -0
  34. package/src/vcs/vcm/util/overrideCollection.js +224 -0
  35. package/src/vcs/vcm/util/projection.js +5 -2
  36. package/src/vcs/vcm/util/style/declarativeStyleItem.js +2 -0
  37. package/src/vcs/vcm/util/style/styleFactory.js +1 -1
  38. package/src/vcs/vcm/util/style/styleItem.js +2 -0
  39. package/src/vcs/vcm/util/style/vectorStyleItem.js +2 -0
  40. package/src/vcs/vcm/vcsApp.js +373 -0
  41. package/src/vcs/vcm/vcsAppContextHelpers.js +108 -0
  42. package/src/vcs/vcm/util/featureconverter/extent3d.js +0 -154
@@ -0,0 +1,224 @@
1
+ /* eslint no-underscore-dangle: ["error", { "allow": ["_array"] }] */
2
+ // eslint-disable-next-line max-classes-per-file
3
+ import { check } from '@vcsuite/check';
4
+ import { getLogger as getLoggerByName } from '@vcsuite/logger';
5
+ import { contextIdSymbol } from '../vcsAppContextHelpers.js';
6
+ import Collection from './collection.js';
7
+ import VcsEvent from '../event/vcsEvent.js';
8
+
9
+ /**
10
+ * @returns {import("@vcsuite/logger").Logger}
11
+ */
12
+ function getLogger() {
13
+ return getLoggerByName('OverrideCollection');
14
+ }
15
+
16
+ /**
17
+ * The override collection adds the ability to override a unique item and re-creating it, should the override
18
+ * be removed. This does change some flow of called events. 1) if you override an item, removed is not called for the
19
+ * removed current item. 2) added can be called more the once for the same unique id. 3) replaced is called for items
20
+ * which where replaced. replaced is called after added has been called for the item.
21
+ * @interface OverrideCollectionInterface
22
+ * @property {import("@vcmap/core").VcsEvent<T>} replaced - replaced is called after added
23
+ * @property {function(T):T|null} override - returns the overriden item or null if the item could not be inserted (this would be the result of a race condition)
24
+ * @property {Map<string, Array<Object>>} shadowMap
25
+ * @property {function(Array<Object>, string):Promise<void>} parseItems
26
+ * @property {function(string):Promise<void>} removeContext
27
+ * @property {function(string):Array<Object>} serializeContext
28
+ * @template {*} T
29
+ */
30
+
31
+ /**
32
+ * A symbol added to override collections.
33
+ * @type {symbol}
34
+ */
35
+ export const isOverrideCollection = Symbol('OverrideCollection');
36
+
37
+ /**
38
+ * @param {Collection<T>} collection
39
+ * @param {function():string} getDynamicContextId - function to get the current dynamic context id
40
+ * @param {(function(T):Object)=} serializeItem - optional function to serialize an item, defaults to returning item.toJSON or item: i => (i.toJSON || i)
41
+ * @param {(function(Object):(T|Promise<T>))=} deserializeItem - optional desirialization function. defaults to returning the passed object: i => i
42
+ * @param {*=} ctor - optional constructor to validate deserialized items against. if passed, deserializeItem must be an instance of ctor.
43
+ * @param {(function(T, (T|null|undefined), (number|undefined)):number|null)=} determineShadowIndex - return the index where a shadow should be inserted. only has relevance, if the collection is indexed. previous and current index may be null.
44
+ * @template {*} T
45
+ * @returns {OverrideCollection<T>}
46
+ */
47
+ function makeOverrideCollection(
48
+ collection,
49
+ getDynamicContextId,
50
+ serializeItem,
51
+ deserializeItem,
52
+ ctor,
53
+ determineShadowIndex,
54
+ ) {
55
+ check(collection, Collection);
56
+
57
+ const overrideCollection = /** @type {OverrideCollection<T>} */ (collection);
58
+ if (overrideCollection[isOverrideCollection]) {
59
+ throw new Error('Cannot transform collection, collection already is an OverrideCollection');
60
+ }
61
+ overrideCollection[isOverrideCollection] = true;
62
+
63
+ const deserialize = deserializeItem || (i => i);
64
+ // @ts-ignore
65
+ const serialize = serializeItem || (i => (i.toJSON ? i.toJSON() : i));
66
+ const getShadowIndex = determineShadowIndex || ((item, shadow, currentIndex) => currentIndex);
67
+
68
+ /**
69
+ * @type {Map<string, Array<Object>>}
70
+ */
71
+ overrideCollection.shadowMap = new Map();
72
+
73
+ /**
74
+ * @param {T} item
75
+ * @returns {T|null}
76
+ */
77
+ overrideCollection.override = function override(item) {
78
+ let shadow;
79
+ let index;
80
+ const itemId = item[overrideCollection.uniqueKey];
81
+
82
+ if (overrideCollection.hasKey(itemId)) {
83
+ shadow = overrideCollection.getByKey(itemId);
84
+ // @ts-ignore
85
+ index = overrideCollection._array.indexOf(shadow); // faking remove to not call removed
86
+ if (index > -1) {
87
+ // @ts-ignore
88
+ overrideCollection._array.splice(index, 1);
89
+ }
90
+ if (!overrideCollection.shadowMap.has(itemId)) {
91
+ overrideCollection.shadowMap.set(itemId, []);
92
+ }
93
+ const shadowsArray = overrideCollection.shadowMap.get(itemId);
94
+ const serializedShadow = serialize(shadow);
95
+ // @ts-ignore
96
+ if (shadow.destroy) {
97
+ // @ts-ignore
98
+ shadow.destroy();
99
+ }
100
+ serializedShadow[contextIdSymbol] = shadow[contextIdSymbol];
101
+ shadowsArray.push(serializedShadow);
102
+ }
103
+
104
+ const usedIndex = shadow ? getShadowIndex(shadow, item, index) : null;
105
+ // @ts-ignore
106
+ if (overrideCollection.add(item, usedIndex) >= 0) {
107
+ if (shadow) {
108
+ overrideCollection.replaced.raiseEvent(item);
109
+ }
110
+ return item;
111
+ }
112
+ return null;
113
+ };
114
+
115
+ /**
116
+ * @param {Array<Object>} configArray
117
+ * @param {string} contextId
118
+ * @returns {Promise<void>}
119
+ */
120
+ overrideCollection.parseItems = async function parseItems(configArray, contextId) {
121
+ if (Array.isArray(configArray)) {
122
+ const instanceArray = await Promise.all(configArray.map(async (config) => {
123
+ const item = await deserialize(config);
124
+ if (!item || (ctor && !(item instanceof ctor))) {
125
+ getLogger().warning(`Could not load item ${config[overrideCollection.uniqueKey]} of type ${config.type}`);
126
+ return null;
127
+ }
128
+ item[contextIdSymbol] = contextId;
129
+ return item;
130
+ }));
131
+ instanceArray
132
+ .filter(i => i)
133
+ .forEach((i) => { overrideCollection.override(i); });
134
+ }
135
+ };
136
+
137
+ overrideCollection.removed.addEventListener(async (item) => {
138
+ const itemId = item[overrideCollection.uniqueKey];
139
+
140
+ if (overrideCollection.shadowMap.has(itemId)) {
141
+ const serializedShadow = overrideCollection.shadowMap.get(itemId).pop();
142
+ if (serializedShadow) {
143
+ const reincarnation = await deserialize(serializedShadow);
144
+ reincarnation[contextIdSymbol] = serializedShadow[contextIdSymbol];
145
+ // @ts-ignore
146
+ const index = getShadowIndex(reincarnation, item, item[overrideCollection.previousIndexSymbol]);
147
+ // @ts-ignore
148
+ overrideCollection.add(reincarnation, index);
149
+ }
150
+
151
+ if (overrideCollection.shadowMap.get(itemId).length === 0) {
152
+ overrideCollection.shadowMap.delete(itemId);
153
+ }
154
+ }
155
+ });
156
+
157
+ overrideCollection.added.addEventListener((item) => {
158
+ if (!item[contextIdSymbol]) {
159
+ item[contextIdSymbol] = getDynamicContextId();
160
+ }
161
+ });
162
+
163
+ /**
164
+ * @param {string} contextId
165
+ * @returns {Promise<void>}
166
+ */
167
+ overrideCollection.removeContext = async function removeContext(contextId) {
168
+ overrideCollection.shadowMap.forEach((shadowsArray, name) => {
169
+ const newShadowsArray = shadowsArray.filter(c => c[contextIdSymbol] !== contextId);
170
+ if (newShadowsArray.length === 0) {
171
+ overrideCollection.shadowMap.delete(name);
172
+ } else if (newShadowsArray.length !== shadowsArray.length) {
173
+ overrideCollection.shadowMap.set(name, newShadowsArray);
174
+ }
175
+ });
176
+
177
+ await Promise.all([...overrideCollection]
178
+ .filter(item => item[contextIdSymbol] === contextId)
179
+ .map(async (item) => {
180
+ overrideCollection.remove(item);
181
+ // @ts-ignore
182
+ if (item.destroy) {
183
+ // @ts-ignore
184
+ item.destroy();
185
+ }
186
+ }));
187
+ };
188
+
189
+ /**
190
+ * @type {VcsEvent<T>}
191
+ */
192
+ overrideCollection.replaced = new VcsEvent();
193
+
194
+ /**
195
+ * @param {string} contextId
196
+ * @returns {Array<Object>}
197
+ */
198
+ overrideCollection.serializeContext = function serializeContext(contextId) {
199
+ return [...overrideCollection]
200
+ .map((item) => {
201
+ if (item[contextIdSymbol] === contextId) {
202
+ return serialize(item);
203
+ }
204
+ if (overrideCollection.shadowMap.has(item[overrideCollection.uniqueKey])) {
205
+ return overrideCollection.shadowMap.get(item[overrideCollection.uniqueKey])
206
+ .find(i => i[contextIdSymbol] === contextId);
207
+ }
208
+ return null;
209
+ })
210
+ .filter(i => i);
211
+ };
212
+
213
+ const originalDestroy = overrideCollection.destroy.bind(overrideCollection);
214
+
215
+ overrideCollection.destroy = function destroy() {
216
+ originalDestroy();
217
+ overrideCollection.shadowMap.clear();
218
+ overrideCollection.replaced.destroy();
219
+ };
220
+
221
+ return overrideCollection;
222
+ }
223
+
224
+ export default makeOverrideCollection;
@@ -14,9 +14,12 @@ import { VcsClassRegistry } from '../classRegistry.js';
14
14
  * @property {string|undefined} [prefix='EPSG:'] - an alternate prefix to use for custom projection
15
15
  * @api stable
16
16
  */
17
+ /**
18
+ * @typedef {function(Array<number>, Array<number>=, number=): Array<number>} CorrectTransformFunction
19
+ */
17
20
 
18
- export const wgs84ToMercatorTransformer = getTransform('EPSG:4326', 'EPSG:3857');
19
- export const mercatorToWgs84Transformer = getTransform('EPSG:3857', 'EPSG:4326');
21
+ export const wgs84ToMercatorTransformer = /** @type {CorrectTransformFunction} */ (getTransform('EPSG:4326', 'EPSG:3857'));
22
+ export const mercatorToWgs84Transformer = /** @type {CorrectTransformFunction} */ (getTransform('EPSG:3857', 'EPSG:4326'));
20
23
 
21
24
 
22
25
  /**
@@ -14,6 +14,7 @@ import {
14
14
  whiteColor,
15
15
  } from './styleHelpers.js';
16
16
  import { originalFeatureSymbol } from '../../layer/vectorSymbols.js';
17
+ import { VcsClassRegistry } from '../../classRegistry.js';
17
18
 
18
19
  /**
19
20
  * @typedef {Object} DeclarativeStyleItemConditions
@@ -558,6 +559,7 @@ class DeclarativeStyleItem extends StyleItem {
558
559
  }
559
560
 
560
561
  export default DeclarativeStyleItem;
562
+ VcsClassRegistry.registerClass(DeclarativeStyleItem.className, DeclarativeStyleItem);
561
563
 
562
564
  /**
563
565
  * @type {DeclarativeStyleItem}
@@ -13,7 +13,7 @@ function getLogger() {
13
13
 
14
14
  /**
15
15
  * @param {(Reference|DeclarativeStyleItemOptions|VectorStyleItemOptions|StyleItem|string)=} styleOptions
16
- * @param {(VectorStyleItem|DeclarativeStyleItem)=} defaultStyle
16
+ * @param {StyleItem=} defaultStyle
17
17
  * @returns {StyleItem}
18
18
  */
19
19
  // eslint-disable-next-line import/prefer-default-export
@@ -4,6 +4,7 @@ import deepEqual from 'fast-deep-equal';
4
4
  import { parseEnumValue } from '@vcsuite/parsers';
5
5
  import VcsObject from '../../object.js';
6
6
  import VcsEvent from '../../event/vcsEvent.js';
7
+ import { VcsClassRegistry } from '../../classRegistry.js';
7
8
 
8
9
  /**
9
10
  * @namespace style
@@ -241,3 +242,4 @@ class StyleItem extends VcsObject {
241
242
  }
242
243
 
243
244
  export default StyleItem;
245
+ VcsClassRegistry.registerClass(StyleItem.className, StyleItem);
@@ -24,6 +24,7 @@ import {
24
24
  getDefaultVectorStyleItemOptions,
25
25
  } from './styleHelpers.js';
26
26
  import { getShapeFromOptions } from './shapesCategory.js';
27
+ import { VcsClassRegistry } from '../../classRegistry.js';
27
28
 
28
29
  /**
29
30
  * @typedef {Object} VectorStyleItemPattern
@@ -907,6 +908,7 @@ export default VectorStyleItem;
907
908
  * @export
908
909
  */
909
910
  export const defaultVectorStyle = new VectorStyleItem(getDefaultVectorStyleItemOptions());
911
+ VcsClassRegistry.registerClass(VectorStyleItem.className, VectorStyleItem);
910
912
 
911
913
  /**
912
914
  * @param {import("@vcmap/cesium").Color} cesiumColor
@@ -0,0 +1,373 @@
1
+ import { getLogger as getLoggerByName } from '@vcsuite/logger';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { check } from '@vcsuite/check';
4
+ import Context from './context.js';
5
+ import {
6
+ contextIdSymbol,
7
+ destroyCollection,
8
+ getObjectFromOptions,
9
+ deserializeViewPoint,
10
+ deserializeMap,
11
+ getLayerIndex,
12
+ serializeLayer,
13
+ } from './vcsAppContextHelpers.js';
14
+ import makeOverrideCollection from './util/overrideCollection.js';
15
+ import CategoryCollection from './category/categoryCollection.js';
16
+ import MapCollection from './util/mapCollection.js';
17
+ import VcsMap from './maps/map.js';
18
+ import Layer from './layer/layer.js';
19
+ import Collection from './util/collection.js';
20
+ import ObliqueCollection from './oblique/ObliqueCollection.js';
21
+ import ViewPoint from './util/viewpoint.js';
22
+ import StyleItem from './util/style/styleItem.js';
23
+ import IndexedCollection from './util/indexedCollection.js';
24
+ import VcsEvent from './event/vcsEvent.js';
25
+ import { setDefaultProjectionOptions } from './util/projection.js';
26
+ import Oblique from './maps/oblique.js';
27
+
28
+ /**
29
+ * @returns {import("@vcsuite/logger").Logger}
30
+ */
31
+ function getLogger() {
32
+ return getLoggerByName('init');
33
+ }
34
+
35
+ /**
36
+ * @type {Map<string, VcsApp>}
37
+ */
38
+ const vcsApps = new Map();
39
+
40
+ /**
41
+ * @class
42
+ */
43
+ class VcsApp {
44
+ constructor() {
45
+ /**
46
+ * @type {string}
47
+ * @private
48
+ */
49
+ this._id = uuidv4();
50
+ /**
51
+ * @type {Context}
52
+ * @private
53
+ */
54
+ this._defaultDynamicContext = new Context({ id: '_defaultDynamicContext' });
55
+ /**
56
+ * @type {Context}
57
+ * @private
58
+ */
59
+ this._dynamicContext = this._defaultDynamicContext;
60
+
61
+ const getDynamicContextId = () => this._dynamicContext.id;
62
+ /**
63
+ * @type {OverrideMapCollection}
64
+ * @private
65
+ */
66
+ // @ts-ignore
67
+ this._maps = makeOverrideCollection(
68
+ new MapCollection(),
69
+ getDynamicContextId,
70
+ null,
71
+ deserializeMap.bind(null, this),
72
+ VcsMap,
73
+ );
74
+ /**
75
+ * @type {OverrideLayerCollection}
76
+ * @private
77
+ */
78
+ // @ts-ignore
79
+ this._layers = makeOverrideCollection(
80
+ this._maps.layerCollection,
81
+ getDynamicContextId,
82
+ serializeLayer.bind(null, this),
83
+ getObjectFromOptions,
84
+ Layer,
85
+ getLayerIndex,
86
+ );
87
+ /**
88
+ * @type {OverrideCollection<import("@vcmap/core").ObliqueCollection>}
89
+ * @private
90
+ */
91
+ this._obliqueCollections = makeOverrideCollection(
92
+ new Collection(),
93
+ getDynamicContextId,
94
+ null,
95
+ config => new ObliqueCollection(config),
96
+ ObliqueCollection,
97
+ ); // XXX there is a global for this collection in core.
98
+ /**
99
+ * @type {OverrideCollection<import("@vcmap/core").ViewPoint>}
100
+ * @private
101
+ */
102
+ this._viewPoints = makeOverrideCollection(
103
+ new Collection(),
104
+ getDynamicContextId,
105
+ null,
106
+ deserializeViewPoint,
107
+ ViewPoint,
108
+ );
109
+ /**
110
+ * @type {OverrideCollection<import("@vcmap/core").StyleItem>}
111
+ * @private
112
+ */
113
+ this._styles = makeOverrideCollection(
114
+ new Collection(),
115
+ getDynamicContextId,
116
+ null,
117
+ getObjectFromOptions,
118
+ StyleItem,
119
+ ); // XXX there is a global for this collection in core.
120
+
121
+ /**
122
+ * @type {IndexedCollection<Context>}
123
+ * @private
124
+ */
125
+ this._contexts = new IndexedCollection('id');
126
+ this._contexts.add(this._dynamicContext);
127
+ /**
128
+ * @type {CategoryCollection}
129
+ * @private
130
+ */
131
+ this._categories = new CategoryCollection(this);
132
+ /**
133
+ * @type {import("@vcmap/core").VcsEvent<void>}
134
+ * @private
135
+ */
136
+ this._destroyed = new VcsEvent();
137
+ /**
138
+ * @type {Promise<void>}
139
+ * @private
140
+ */
141
+ this._contextMutationPromise = Promise.resolve();
142
+ vcsApps.set(this._id, this);
143
+ }
144
+
145
+ /**
146
+ * @type {string}
147
+ * @readonly
148
+ */
149
+ get id() { return this._id; }
150
+
151
+ /**
152
+ * @type {OverrideMapCollection}
153
+ * @readonly
154
+ */
155
+ get maps() { return this._maps; }
156
+
157
+ /**
158
+ * @type {OverrideLayerCollection}
159
+ * @readonly
160
+ */
161
+ get layers() { return this._layers; }
162
+
163
+ /**
164
+ * @type {OverrideCollection<import("@vcmap/core").ObliqueCollection>}
165
+ * @readonly
166
+ */
167
+ get obliqueCollections() { return this._obliqueCollections; }
168
+
169
+ /**
170
+ * @type {OverrideCollection<import("@vcmap/core").ViewPoint>}
171
+ * @readonly
172
+ */
173
+ get viewPoints() { return this._viewPoints; }
174
+
175
+ /**
176
+ * @type {OverrideCollection<import("@vcmap/core").StyleItem>}
177
+ * @readonly
178
+ */
179
+ get styles() { return this._styles; }
180
+
181
+ /**
182
+ * @type {CategoryCollection}
183
+ * @readonly
184
+ */
185
+ get categories() { return this._categories; }
186
+
187
+ /**
188
+ * @type {VcsEvent<void>}
189
+ * @readonly
190
+ */
191
+ get destroyed() { return this._destroyed; }
192
+
193
+ /**
194
+ * @returns {VcsEvent<Context>}
195
+ * @readonly
196
+ */
197
+ get contextAdded() { return this._contexts.added; }
198
+
199
+ /**
200
+ * @returns {VcsEvent<Context>}
201
+ * @readonly
202
+ */
203
+ get contextRemoved() { return this._contexts.removed; }
204
+
205
+ get dynamicContextId() { return this._dynamicContext.id; }
206
+
207
+ /**
208
+ * @param {string} id
209
+ * @returns {Context}
210
+ */
211
+ getContextById(id) {
212
+ return this._contexts.getByKey(id);
213
+ }
214
+
215
+ /**
216
+ * @param {Context} context
217
+ * @returns {Promise<void>}
218
+ * @protected
219
+ */
220
+ async _parseContext(context) {
221
+ const { config } = context;
222
+ if (config.projection) { // XXX this needs fixing. this should be _projections_ and there should be a `defaultProjection`
223
+ setDefaultProjectionOptions(config.projection);
224
+ }
225
+
226
+ await this._styles.parseItems(config.styles, context.id);
227
+ await this._layers.parseItems(config.layers, context.id);
228
+ // TODO add flights & ade here
229
+
230
+ await this._obliqueCollections.parseItems(config.obliqueCollections, context.id);
231
+ await this._viewPoints.parseItems(config.viewpoints, context.id);
232
+ await this._maps.parseItems(config.maps, context.id);
233
+
234
+ if (Array.isArray(config.categories)) {
235
+ await Promise.all((config.categories).map(async ({ name, items }) => {
236
+ await this._categories.parseCategoryItems(name, items, context.id);
237
+ }));
238
+ }
239
+ }
240
+
241
+ /**
242
+ * @param {Context} context
243
+ * @returns {Promise<void>}
244
+ * @protected
245
+ */
246
+ async _setContextState(context) {
247
+ const { config } = context;
248
+ [...this._layers]
249
+ .filter(l => l[contextIdSymbol] === context.id)
250
+ .forEach((l) => {
251
+ if (l.activeOnStartup) {
252
+ l.activate()
253
+ .catch((err) => {
254
+ getLogger().error(`Failed to activate active on startup layer ${l.name}`);
255
+ getLogger().error(err);
256
+ this._layers.remove(l);
257
+ l.destroy();
258
+ });
259
+ }
260
+ });
261
+
262
+ const activeObliqueCollection = [...this._obliqueCollections]
263
+ .find(c => c[contextIdSymbol] === context.id && c.activeOnStartup);
264
+
265
+ if (activeObliqueCollection) {
266
+ [...this._maps]
267
+ .filter(m => m instanceof Oblique)
268
+ .forEach((m) => { /** @type {Oblique} */ (m).setCollection(activeObliqueCollection); });
269
+ }
270
+
271
+ if (config.startingMapName) {
272
+ await this._maps.setActiveMap(config.startingMapName);
273
+ } else if (!this._maps.activeMap && this._maps.size > 0) {
274
+ await this._maps.setActiveMap([...this._maps][0].name);
275
+ }
276
+
277
+ if (config.startingViewPointName && this._maps.activeMap) {
278
+ const startViewPoint = this._viewPoints.getByKey(config.startingViewPointName);
279
+ if (startViewPoint) {
280
+ await this._maps.activeMap.gotoViewPoint(startViewPoint);
281
+ }
282
+ }
283
+ }
284
+
285
+ /**
286
+ * @param {Context} context
287
+ * @returns {Promise<void>}
288
+ */
289
+ addContext(context) {
290
+ check(context, Context);
291
+
292
+ this._contextMutationPromise = this._contextMutationPromise
293
+ .then(async () => {
294
+ if (this._contexts.has(context)) {
295
+ getLogger().info(`context with id ${context.id} already loaded`);
296
+ return;
297
+ }
298
+
299
+ await this._parseContext(context);
300
+ await this._setContextState(context);
301
+ this._contexts.add(context);
302
+ });
303
+ return this._contextMutationPromise;
304
+ }
305
+
306
+ /**
307
+ * @param {string} contextId
308
+ * @returns {Promise<void>}
309
+ * @protected
310
+ */
311
+ async _removeContext(contextId) {
312
+ await Promise.all([
313
+ this._maps.removeContext(contextId),
314
+ this._layers.removeContext(contextId),
315
+ this._viewPoints.removeContext(contextId),
316
+ this._styles.removeContext(contextId),
317
+ this._obliqueCollections.removeContext(contextId),
318
+ ]);
319
+ }
320
+
321
+ /**
322
+ * @param {string} contextId
323
+ * @returns {Promise<void>}
324
+ */
325
+ removeContext(contextId) {
326
+ this._contextMutationPromise = this._contextMutationPromise
327
+ .then(async () => {
328
+ const context = this._contexts.getByKey(contextId);
329
+ if (!context) {
330
+ getLogger().info(`context with id ${contextId} has alread been removed`);
331
+ return;
332
+ }
333
+ await this._removeContext(contextId);
334
+ this._contexts.remove(context);
335
+ });
336
+
337
+ return this._contextMutationPromise;
338
+ }
339
+
340
+ /**
341
+ * Destroys the app and all its collections, their content and ui managers.
342
+ */
343
+ destroy() {
344
+ Object.defineProperty(this, '_contextMutationPromise', {
345
+ get() {
346
+ throw new Error('VcsApp was destroyed');
347
+ },
348
+ });
349
+ vcsApps.delete(this._id);
350
+ destroyCollection(this._maps);
351
+ destroyCollection(this._layers);
352
+ destroyCollection(this._obliqueCollections);
353
+ destroyCollection(this._viewPoints);
354
+ destroyCollection(this._styles);
355
+ destroyCollection(this._contexts);
356
+ destroyCollection(this._categories);
357
+ this.destroyed.raiseEvent();
358
+ this.destroyed.destroy();
359
+ }
360
+ }
361
+
362
+ /**
363
+ * @param {string} id
364
+ * @returns {VcsApp}
365
+ */
366
+ export function getVcsAppById(id) {
367
+ return vcsApps.get(id);
368
+ }
369
+
370
+ window.vcs = window.vcs || {};
371
+ window.vcs.apps = vcsApps;
372
+
373
+ export default VcsApp;