@open-pioneer/map 1.4.0-dev.20260727093741 → 1.4.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/CHANGELOG.md +39 -21
- package/LayerFactory.d.ts +1 -1
- package/LayerFactory.js.map +1 -1
- package/MapRegistry.js.map +1 -1
- package/layers/AbstractLayer.d.ts +36 -3
- package/layers/AbstractLayer.js +123 -79
- package/layers/AbstractLayer.js.map +1 -1
- package/layers/AbstractLayerBase.d.ts +2 -2
- package/layers/AbstractLayerBase.js.map +1 -1
- package/layers/GroupLayer.d.ts +2 -1
- package/layers/GroupLayer.js +4 -1
- package/layers/GroupLayer.js.map +1 -1
- package/layers/SimpleLayer.d.ts +1 -1
- package/layers/SimpleLayer.js +1 -1
- package/layers/SimpleLayer.js.map +1 -1
- package/layers/WMSLayer.js +58 -10
- package/layers/WMSLayer.js.map +1 -1
- package/layers/WMTSLayer.d.ts +1 -1
- package/layers/WMTSLayer.js +10 -4
- package/layers/WMTSLayer.js.map +1 -1
- package/layers/group/GroupLayerCollection.d.ts +2 -2
- package/layers/group/GroupLayerCollection.js +1 -1
- package/layers/group/GroupLayerCollection.js.map +1 -1
- package/layers/shared/SublayerBaseType.d.ts +9 -0
- package/layers/shared/SublayersCollection.js.map +1 -1
- package/layers/shared/getRecursiveLayers.d.ts +1 -1
- package/layers/shared/getRecursiveLayers.js +12 -5
- package/layers/shared/getRecursiveLayers.js.map +1 -1
- package/layers/shared/internals.d.ts +3 -2
- package/layers/shared/internals.js +2 -1
- package/layers/shared/internals.js.map +1 -1
- package/layers/unions.js.map +1 -1
- package/layers/wms/WMSSublayer.d.ts +14 -1
- package/layers/wms/WMSSublayer.js +29 -2
- package/layers/wms/WMSSublayer.js.map +1 -1
- package/layers/wms/getAttributions.js.map +1 -1
- package/layers/wms/getLegendUrl.js.map +1 -1
- package/layers/wmts/getAttributions.js.map +1 -1
- package/layers/wmts/getLegendUrl.js.map +1 -1
- package/model/Highlights.js.map +1 -1
- package/model/LayerCollection.js.map +1 -1
- package/model/MapAttributions.js.map +1 -1
- package/model/MapModel.d.ts +5 -5
- package/model/MapModel.js +4 -4
- package/model/MapModel.js.map +1 -1
- package/model/Overlays.d.ts +2 -2
- package/model/Overlays.js +2 -2
- package/model/Overlays.js.map +1 -1
- package/model/createMapModel.js +2 -2
- package/model/createMapModel.js.map +1 -1
- package/model/getGeometries.js.map +1 -1
- package/package.json +7 -7
- package/ui/DefaultMapProvider.js.map +1 -1
- package/ui/MapAnchor.js.map +1 -1
- package/ui/MapContainer.js +1 -1
- package/ui/MapContainer.js.map +1 -1
- package/ui/MapContainerContext.js.map +1 -1
- package/ui/OverlaysRenderer.js.map +1 -1
- package/ui/computeMapAnchorStyles.js.map +1 -1
- package/ui/hooks/useMapModel.js +2 -2
- package/ui/hooks/useMapModel.js.map +1 -1
- package/utils/fetch.js.map +1 -1
- package/utils/geometry-utils.js.map +1 -1
- package/utils/ol-test-support.js.map +1 -1
- package/utils/projections.js.map +1 -1
- package/utils/sanitize.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
# @open-pioneer/map
|
|
2
2
|
|
|
3
|
-
## 1.4.0
|
|
3
|
+
## 1.4.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
7
|
- c30396d: Update Chakra to 3.36.1
|
|
8
|
+
- 690a892: A layer's loadState is now a derived from three different channels:
|
|
9
|
+
|
|
10
|
+
- source — the OpenLayers source state (undefined/loading/ready/error).
|
|
11
|
+
- health — the result of the optional healthCheck (run once on attach).
|
|
12
|
+
- metadata — the result of a layer's own capabilities request (if any).
|
|
13
|
+
|
|
14
|
+
Additionally, a new property `loadError` has been added to the layer class.
|
|
15
|
+
|
|
8
16
|
- e4b47f1: `useMapModel` now prints an error message if the map model could not be created.
|
|
9
17
|
This error was previously returned and was easy to silently ignore.
|
|
10
18
|
|
|
11
19
|
Use the option `quiet: true` to suppress this error message.
|
|
12
20
|
|
|
13
21
|
- c9b3ced: Provide reactive rotation in MapModel
|
|
22
|
+
- 690a892: WMS Layer: add validation that configured sublayers actually exist in the service's capabilities.
|
|
23
|
+
- d862003: Update to trails core-packages 4.7.0
|
|
14
24
|
|
|
15
25
|
### Patch Changes
|
|
16
26
|
|
|
17
27
|
- 078bef5: Use private JavaScript properties (#) instead of TypeScript keyword.
|
|
18
28
|
- b58a50f: Use new `shallowEqual` function to compare attribution arrays.
|
|
29
|
+
- c16a401: Migrated from eslint to oxlint and from prettier to oxfmt.
|
|
19
30
|
|
|
20
31
|
## 1.3.0
|
|
21
32
|
|
|
@@ -33,6 +44,7 @@
|
|
|
33
44
|
- 73453af: Update OpenLayers to 10.9.0
|
|
34
45
|
- fcbd505: Sanitize HTML used for layer attributions.
|
|
35
46
|
- 33ab02f: Move highlight methods to `mapModel.highlights`.
|
|
47
|
+
|
|
36
48
|
- `mapModel.highlight()` -> `mapModel.highlights.add()`
|
|
37
49
|
- `mapModel.highlightAndZoom()` -> `mapModel.highlights.addAndZoom()`
|
|
38
50
|
- `mapModel.removeHighlights()` -> `mapModel.highlights.clear()`
|
|
@@ -353,6 +365,7 @@
|
|
|
353
365
|
- 138d85b: Update core packages to 4.2.0
|
|
354
366
|
- 4f1e7bd: The internal constant `TOPMOST_LAYER_Z` has been removed.
|
|
355
367
|
To configure a layer that is always on top:
|
|
368
|
+
|
|
356
369
|
- Create a layer using the `LayerFactory`
|
|
357
370
|
- Add it to the map model and specify the `at: "topmost"` option
|
|
358
371
|
|
|
@@ -368,6 +381,7 @@
|
|
|
368
381
|
|
|
369
382
|
- 66179bc: Update to core-packages v4.0.0
|
|
370
383
|
- acd5115: **Breaking:** Remove the following hooks, which were deprecated since version 0.8.0:
|
|
384
|
+
|
|
371
385
|
- useView
|
|
372
386
|
- useProjection
|
|
373
387
|
- useResolution
|
|
@@ -620,6 +634,7 @@
|
|
|
620
634
|
`layer.children` is either an alias of `layer.sublayers` (if the layer has sublayers), `layer.layers` (if it's a `GroupLayer`) or undefined, if the layer does not have any children.
|
|
621
635
|
|
|
622
636
|
- d8337a6: The following hooks are deprecated and will be removed in a future release:
|
|
637
|
+
|
|
623
638
|
- `useView`
|
|
624
639
|
- `useProjection`
|
|
625
640
|
- `useResolution`
|
|
@@ -637,6 +652,7 @@
|
|
|
637
652
|
```
|
|
638
653
|
|
|
639
654
|
- 2fa8020: Update trails core package dependencies.
|
|
655
|
+
|
|
640
656
|
- Also updates Chakra UI to the latest 2.x version and Chakra React Select to version 5.
|
|
641
657
|
- Removes any obsolete references to `@chakra-ui/system`.
|
|
642
658
|
This dependency seems to be no longer required and may lead to duplicate packages in your dependency tree.
|
|
@@ -649,34 +665,34 @@
|
|
|
649
665
|
|
|
650
666
|
```js
|
|
651
667
|
const olLayer1 = new TileLayer({
|
|
652
|
-
|
|
668
|
+
source: new OSM(),
|
|
653
669
|
});
|
|
654
670
|
const olLayer2 = new TileLayer({
|
|
655
|
-
|
|
671
|
+
source: new BkgTopPlusOpen(),
|
|
656
672
|
});
|
|
657
673
|
|
|
658
674
|
// Create group layer with nested sub group
|
|
659
675
|
const group = new GroupLayer({
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
676
|
+
id: "group",
|
|
677
|
+
title: "a group layer",
|
|
678
|
+
layers: [
|
|
679
|
+
new SimpleLayer({
|
|
680
|
+
id: "member",
|
|
681
|
+
title: "group member",
|
|
682
|
+
olLayer: olLayer1,
|
|
683
|
+
}),
|
|
684
|
+
new GroupLayer({
|
|
685
|
+
id: "subgroup",
|
|
686
|
+
title: "a nested group layer",
|
|
687
|
+
layers: [
|
|
663
688
|
new SimpleLayer({
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
689
|
+
id: "submember",
|
|
690
|
+
title: "subgroup member",
|
|
691
|
+
olLayer: olLayer2,
|
|
667
692
|
}),
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
layers: [
|
|
672
|
-
new SimpleLayer({
|
|
673
|
-
id: "submember",
|
|
674
|
-
title: "subgroup member",
|
|
675
|
-
olLayer: olLayer2
|
|
676
|
-
})
|
|
677
|
-
]
|
|
678
|
-
})
|
|
679
|
-
]
|
|
693
|
+
],
|
|
694
|
+
}),
|
|
695
|
+
],
|
|
680
696
|
});
|
|
681
697
|
|
|
682
698
|
const childLayers: GroupLayerCollection = group.layers; // Access child layers
|
|
@@ -686,6 +702,7 @@
|
|
|
686
702
|
Sublayers (e.g. `WMSSublayer`) cannot be added to a group directly.
|
|
687
703
|
|
|
688
704
|
- d8337a6: Provide new reactive properties on the `MapModel` type.
|
|
705
|
+
|
|
689
706
|
- `olView` (-> `olMap.getView()`)
|
|
690
707
|
- `projection` (-> `olMap.getView().getProjection()`)
|
|
691
708
|
- `resolution` (-> `olMap.getView().getResolution()`)
|
|
@@ -717,6 +734,7 @@
|
|
|
717
734
|
Two type guards have been implemented that allow to check if a layer instance is a `Layer` or `Sublayer`: `isLayer()`and `isSublayer()` (see example below).
|
|
718
735
|
|
|
719
736
|
The following `type` attribute values have been implemented at the layers:
|
|
737
|
+
|
|
720
738
|
- SimpleLayer: `simple`
|
|
721
739
|
- WMSLayer: `wms`
|
|
722
740
|
- WMSSubLayer: `wms-sublayer`
|
package/LayerFactory.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HttpService } from "@open-pioneer/http";
|
|
2
2
|
import { DECLARE_SERVICE_INTERFACE, ServiceOptions } from "@open-pioneer/runtime";
|
|
3
|
-
import { LayerConfig } from "./layers/shared/LayerConfig";
|
|
4
3
|
import { LayerConstructor } from "./layers/shared/internals";
|
|
4
|
+
import { LayerConfig } from "./layers/shared/LayerConfig";
|
|
5
5
|
import { Layer } from "./layers/unions";
|
|
6
6
|
interface References {
|
|
7
7
|
httpService: HttpService;
|
package/LayerFactory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayerFactory.js","sources":["LayerFactory.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { HttpService } from \"@open-pioneer/http\";\nimport { DECLARE_SERVICE_INTERFACE, ServiceOptions } from \"@open-pioneer/runtime\";\nimport { AbstractLayer } from \"./layers/AbstractLayer\";\nimport {
|
|
1
|
+
{"version":3,"file":"LayerFactory.js","sources":["LayerFactory.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { HttpService } from \"@open-pioneer/http\";\nimport { DECLARE_SERVICE_INTERFACE, ServiceOptions } from \"@open-pioneer/runtime\";\nimport { AbstractLayer } from \"./layers/AbstractLayer\";\nimport { LayerConstructor } from \"./layers/shared/internals\";\nimport { LayerConfig } from \"./layers/shared/LayerConfig\";\nimport { Layer } from \"./layers/unions\";\nimport { INTERNAL_CONSTRUCTOR_TAG } from \"./utils/InternalConstructorTag\";\n\ninterface References {\n httpService: HttpService;\n}\n\n/**\n * Options that can be passed to {@link LayerFactory.create}.\n *\n * The `type` option is mandatory and indicates the type of the layer (e.g. `WMSLayer`).\n * The other options depend on the specific layer type.\n *\n * @group Services\n */\nexport type LayerCreateOptions<LayerType extends Layer, Config extends LayerConfig> = {\n /** The layer type to construct. */\n type: LayerConstructor<Config, LayerType>;\n} & Config;\n\n/**\n * Creates instances of layer classes.\n *\n * Use the interface `\"map.LayerFactory\"` to obtain an instance of this service.\n *\n * @group Services\n */\nexport class LayerFactory {\n declare [DECLARE_SERVICE_INTERFACE]: \"map.LayerFactory\";\n\n #httpService: HttpService;\n\n constructor(options: ServiceOptions<References>) {\n this.#httpService = options.references.httpService;\n }\n\n /**\n * Creates a new instance of the given layer type.\n * The `\"type\"` option is mandatory and specifies which layer type to create.\n *\n * The other configuration options may be dependent on the specific layer type.\n *\n * Example:\n *\n * ```ts\n * import { SimpleLayer } from \"@open-pioneer/map\";\n *\n * const layerFactory = ...; // injected\n * const layer = layerFactory.create({\n * type: SimpleLayer,\n * title: \"Layer title\",\n * olLayer: new TileLayer({}),\n * // ... other options\n * });\n * ```\n */\n create<LayerType extends Layer, Config extends LayerConfig>(\n config: LayerCreateOptions<LayerType, Config>\n ): LayerType {\n const { type, ...rest } = config;\n if (!type || !(type.prototype instanceof AbstractLayer)) {\n throw new Error(\n `Invalid layer type option. Use one of the layer classes exported by this package.`\n );\n }\n return new type(\n rest as unknown as Config,\n { httpService: this.#httpService },\n INTERNAL_CONSTRUCTOR_TAG\n );\n }\n}\n"],"names":[],"mappings":";;;AAmCO,MAAM,YAAA,CAAa;AAAA,EAGtB,YAAA;AAAA,EAEA,YAAY,OAAA,EAAqC;AAC7C,IAAA,IAAA,CAAK,YAAA,GAAe,QAAQ,UAAA,CAAW,WAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,OACI,MAAA,EACS;AACT,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,IAAA,EAAK,GAAI,MAAA;AAC1B,IAAA,IAAI,CAAC,IAAA,IAAQ,EAAE,IAAA,CAAK,qBAAqB,aAAA,CAAA,EAAgB;AACrD,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,CAAA,iFAAA;AAAA,OACJ;AAAA,IACJ;AACA,IAAA,OAAO,IAAI,IAAA;AAAA,MACP,IAAA;AAAA,MACA,EAAE,WAAA,EAAa,IAAA,CAAK,YAAA,EAAa;AAAA,MACjC;AAAA,KACJ;AAAA,EACJ;AACJ;;;;"}
|
package/MapRegistry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapRegistry.js","sources":["MapRegistry.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { batch, ReadonlyReactive } from \"@conterra/reactivity-core\";\nimport { on } from \"@conterra/reactivity-events\";\nimport { createLogger, Resource } from \"@open-pioneer/core\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport {\n DECLARE_SERVICE_INTERFACE,\n PackageIntl,\n Service,\n ServiceOptions\n} from \"@open-pioneer/runtime\";\nimport OlMap from \"ol/Map\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { LayerFactory } from \"./LayerFactory\";\nimport { createMapModel } from \"./model/createMapModel\";\nimport { MapConfig } from \"./model/MapConfig\";\nimport { MapModel } from \"./model/MapModel\";\n\nconst LOG = createLogger(sourceId);\n\n/**\n * Options passed to the {@link MapConfigProvider.getMapConfig} method.\n *\n * @group Services\n */\nexport interface MapConfigProviderOptions {\n /**\n * A reference to the layer factory, for the construction of new layer instances.\n */\n layerFactory: LayerFactory;\n}\n\n/**\n * Provides an OpenLayers map configuration with a given map id.\n *\n * The implementor must also provide the interface name `\"map.MapConfigProvider\"`.\n *\n * @group Services\n */\nexport interface MapConfigProvider {\n /**\n * Unique identifier of the map.\n */\n readonly mapId: string;\n\n /**\n * Returns the map configuration for this map.\n *\n * Called by the {@link MapRegistry} when the map is requested for the first time.\n *\n * See {@link MapConfig} for supported options during map creation.\n * Use {@link MapConfigProviderOptions.layerFactory} to construct new layers.\n */\n getMapConfig(options: MapConfigProviderOptions): Promise<MapConfig>;\n}\n\ninterface References {\n providers: MapConfigProvider[];\n httpService: HttpService;\n layerFactory: LayerFactory;\n}\n\ntype ModelJobResult =\n { kind: \"model\"; model: MapModel; listener: Resource } | { kind: \"error\"; error: Error };\n\n/**\n * Provides access to registered map instances.\n *\n * Maps are identified by a unique id.\n *\n * Inject an instance of this service by referencing the interface name `\"map.MapRegistry\"`.\n *\n * @group Services\n */\nexport class MapRegistry implements Service {\n declare [DECLARE_SERVICE_INTERFACE]: \"map.MapRegistry\";\n\n #currentIntl: ReadonlyReactive<PackageIntl>;\n #httpService: HttpService;\n #layerFactory: LayerFactory;\n\n #configProviders = new Map<string, MapConfigProvider>();\n #entries = new Map<string, ModelJobResult>();\n #modelCreationJobs = new Map<string, Promise<ModelJobResult>>();\n #modelsByOlMap = new WeakMap<OlMap, MapModel>();\n #destroyed = false;\n\n constructor({ references, currentIntl }: ServiceOptions<References>) {\n this.#currentIntl = currentIntl;\n this.#httpService = references.httpService;\n this.#layerFactory = references.layerFactory;\n\n const providers = references.providers;\n for (const provider of providers) {\n this.#configProviders.set(provider.mapId, provider);\n }\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n batch(() => {\n LOG.debug(`Destroy map registry and all maps`);\n this.#destroyed = true;\n this.#entries.forEach((entry) => {\n if (entry.kind === \"model\") {\n entry.listener.destroy();\n entry.model.destroy();\n }\n });\n this.#entries.clear();\n this.#modelCreationJobs.clear();\n });\n }\n\n /**\n * Returns the map model associated with the given id.\n * Returns undefined if there is no such model.\n */\n async getMapModel(mapId: string): Promise<MapModel | undefined> {\n if (this.#destroyed) {\n throw new Error(\"MapRegistry has already been destroyed.\");\n }\n\n const entry = this.#entries.get(mapId);\n if (entry) {\n return unbox(entry);\n }\n\n const creationJob = this.#modelCreationJobs.get(mapId);\n if (creationJob) {\n return await waitForResult(creationJob);\n }\n\n const provider = this.#configProviders.get(mapId);\n if (!provider) {\n LOG.debug(`Failed to find a config provider for map id '${mapId}'.`);\n return undefined;\n }\n\n return await this.#createMapModel(mapId, () =>\n provider.getMapConfig({\n layerFactory: this.#layerFactory\n })\n );\n }\n\n /**\n * Like {@link getMapModel}, but throws if no model exists for the given `mapId`.\n */\n async expectMapModel(mapId: string): Promise<MapModel> {\n const model = await this.getMapModel(mapId);\n if (!model) {\n throw new Error(`No configuration available for map with id '${mapId}'.`);\n }\n return model;\n }\n\n /**\n * Creates a MapModel without the need to provide a {@link MapConfigProvider}.\n * Throws an error if a map with the given id already exists or if the map config is invalid.\n */\n async createMapModel(mapId: string, options?: MapConfig): Promise<MapModel> {\n if (this.#destroyed) {\n throw new Error(\"MapRegistry has already been destroyed.\");\n }\n if (this.#entries.has(mapId)) {\n throw new Error(`Map id '${mapId}' is not unique.`);\n }\n if (this.#modelCreationJobs.has(mapId)) {\n throw new Error(`A map with the id '${mapId}' is already under construction.`);\n }\n return await this.#createMapModel(mapId, () => Promise.resolve(options ?? {}));\n }\n\n /**\n * Given a raw OpenLayers map instance, returns the associated {@link MapModel} - or undefined\n * if the map is unknown to this registry.\n *\n * All OpenLayers maps created by this registry (e.g. via {@link MapConfigProvider} or {@link createMapModel}) have an associated map model.\n */\n getMapModelByRawInstance(olMap: OlMap): MapModel | undefined {\n return this.#modelsByOlMap.get(olMap);\n }\n\n // Wrapper method to ensure that in-progress construction of maps can be observed.\n #createMapModel(mapId: string, configProvider: () => Promise<MapConfig>): Promise<MapModel> {\n const creationJob = this.#modelCreationJobs.get(mapId);\n if (creationJob) {\n throw new Error(\"Internal error: a map model is already being created for this mapId\");\n }\n\n const modelPromise = this.#createMapModelImpl(mapId, configProvider)\n .then((mapModel) => {\n if (this.#destroyed) {\n mapModel.destroy();\n throw new Error(`MapRegistry has been destroyed.`);\n }\n\n const listener = on(\n mapModel.destroyed,\n () => {\n // Allow id reuse for dynamically created maps\n if (this.#isDynamic(mapId)) {\n const currentEntry = this.#entries.get(mapId);\n if (currentEntry === entry) {\n this.#entries.delete(mapId);\n }\n }\n },\n { dispatch: \"sync\" }\n );\n\n const entry: ModelJobResult = { kind: \"model\", model: mapModel, listener };\n this.#entries.set(mapId, entry);\n this.#modelCreationJobs.delete(mapId);\n this.#modelsByOlMap.set(mapModel.olMap, mapModel);\n return entry;\n })\n .catch((cause) => {\n const error = new Error(`Failed to construct map '${mapId}'`, { cause });\n const entry: ModelJobResult = { kind: \"error\", error };\n this.#modelCreationJobs.delete(mapId);\n if (!this.#isDynamic(mapId)) {\n // Don't store errors for dynamically created maps (the caller already got the error via promise).\n this.#entries.set(mapId, entry);\n }\n return entry;\n });\n this.#modelCreationJobs.set(mapId, modelPromise);\n return waitForResult(modelPromise);\n }\n\n async #createMapModelImpl(\n mapId: string,\n configProvider: () => Promise<MapConfig>\n ): Promise<MapModel> {\n LOG.info(`Creating map with id '${mapId}'`);\n const mapConfig = await configProvider();\n const mapModel = await createMapModel(\n mapId,\n mapConfig,\n this.#currentIntl,\n this.#httpService\n );\n return mapModel;\n }\n\n #isDynamic(mapId: string): boolean {\n return !this.#configProviders.has(mapId);\n }\n}\n\nasync function waitForResult(job: Promise<ModelJobResult>): Promise<MapModel> {\n return unbox(await job);\n}\n\nfunction unbox(entry: ModelJobResult): MapModel {\n if (entry.kind === \"error\") {\n throw entry.error;\n }\n return entry.model;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MapRegistry.js","sources":["MapRegistry.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { batch, ReadonlyReactive } from \"@conterra/reactivity-core\";\nimport { on } from \"@conterra/reactivity-events\";\nimport { createLogger, Resource } from \"@open-pioneer/core\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport {\n DECLARE_SERVICE_INTERFACE,\n PackageIntl,\n Service,\n ServiceOptions\n} from \"@open-pioneer/runtime\";\nimport OlMap from \"ol/Map\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { LayerFactory } from \"./LayerFactory\";\nimport { createMapModel } from \"./model/createMapModel\";\nimport { MapConfig } from \"./model/MapConfig\";\nimport { MapModel } from \"./model/MapModel\";\n\nconst LOG = createLogger(sourceId);\n\n/**\n * Options passed to the {@link MapConfigProvider.getMapConfig} method.\n *\n * @group Services\n */\nexport interface MapConfigProviderOptions {\n /**\n * A reference to the layer factory, for the construction of new layer instances.\n */\n layerFactory: LayerFactory;\n}\n\n/**\n * Provides an OpenLayers map configuration with a given map id.\n *\n * The implementor must also provide the interface name `\"map.MapConfigProvider\"`.\n *\n * @group Services\n */\nexport interface MapConfigProvider {\n /**\n * Unique identifier of the map.\n */\n readonly mapId: string;\n\n /**\n * Returns the map configuration for this map.\n *\n * Called by the {@link MapRegistry} when the map is requested for the first time.\n *\n * See {@link MapConfig} for supported options during map creation.\n * Use {@link MapConfigProviderOptions.layerFactory} to construct new layers.\n */\n getMapConfig(options: MapConfigProviderOptions): Promise<MapConfig>;\n}\n\ninterface References {\n providers: MapConfigProvider[];\n httpService: HttpService;\n layerFactory: LayerFactory;\n}\n\ntype ModelJobResult =\n | { kind: \"model\"; model: MapModel; listener: Resource }\n | { kind: \"error\"; error: Error };\n\n/**\n * Provides access to registered map instances.\n *\n * Maps are identified by a unique id.\n *\n * Inject an instance of this service by referencing the interface name `\"map.MapRegistry\"`.\n *\n * @group Services\n */\nexport class MapRegistry implements Service {\n declare [DECLARE_SERVICE_INTERFACE]: \"map.MapRegistry\";\n\n #currentIntl: ReadonlyReactive<PackageIntl>;\n #httpService: HttpService;\n #layerFactory: LayerFactory;\n\n #configProviders = new Map<string, MapConfigProvider>();\n #entries = new Map<string, ModelJobResult>();\n #modelCreationJobs = new Map<string, Promise<ModelJobResult>>();\n #modelsByOlMap = new WeakMap<OlMap, MapModel>();\n #destroyed = false;\n\n constructor({ references, currentIntl }: ServiceOptions<References>) {\n this.#currentIntl = currentIntl;\n this.#httpService = references.httpService;\n this.#layerFactory = references.layerFactory;\n\n const providers = references.providers;\n for (const provider of providers) {\n this.#configProviders.set(provider.mapId, provider);\n }\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n batch(() => {\n LOG.debug(`Destroy map registry and all maps`);\n this.#destroyed = true;\n this.#entries.forEach((entry) => {\n if (entry.kind === \"model\") {\n entry.listener.destroy();\n entry.model.destroy();\n }\n });\n this.#entries.clear();\n this.#modelCreationJobs.clear();\n });\n }\n\n /**\n * Returns the map model associated with the given id.\n * Returns undefined if there is no such model.\n */\n async getMapModel(mapId: string): Promise<MapModel | undefined> {\n if (this.#destroyed) {\n throw new Error(\"MapRegistry has already been destroyed.\");\n }\n\n const entry = this.#entries.get(mapId);\n if (entry) {\n return unbox(entry);\n }\n\n const creationJob = this.#modelCreationJobs.get(mapId);\n if (creationJob) {\n return await waitForResult(creationJob);\n }\n\n const provider = this.#configProviders.get(mapId);\n if (!provider) {\n LOG.debug(`Failed to find a config provider for map id '${mapId}'.`);\n return undefined;\n }\n\n return await this.#createMapModel(mapId, () =>\n provider.getMapConfig({\n layerFactory: this.#layerFactory\n })\n );\n }\n\n /**\n * Like {@link getMapModel}, but throws if no model exists for the given `mapId`.\n */\n async expectMapModel(mapId: string): Promise<MapModel> {\n const model = await this.getMapModel(mapId);\n if (!model) {\n throw new Error(`No configuration available for map with id '${mapId}'.`);\n }\n return model;\n }\n\n /**\n * Creates a MapModel without the need to provide a {@link MapConfigProvider}.\n * Throws an error if a map with the given id already exists or if the map config is invalid.\n */\n async createMapModel(mapId: string, options?: MapConfig): Promise<MapModel> {\n if (this.#destroyed) {\n throw new Error(\"MapRegistry has already been destroyed.\");\n }\n if (this.#entries.has(mapId)) {\n throw new Error(`Map id '${mapId}' is not unique.`);\n }\n if (this.#modelCreationJobs.has(mapId)) {\n throw new Error(`A map with the id '${mapId}' is already under construction.`);\n }\n return await this.#createMapModel(mapId, () => Promise.resolve(options ?? {}));\n }\n\n /**\n * Given a raw OpenLayers map instance, returns the associated {@link MapModel} - or undefined\n * if the map is unknown to this registry.\n *\n * All OpenLayers maps created by this registry (e.g. via {@link MapConfigProvider} or {@link createMapModel}) have an associated map model.\n */\n getMapModelByRawInstance(olMap: OlMap): MapModel | undefined {\n return this.#modelsByOlMap.get(olMap);\n }\n\n // Wrapper method to ensure that in-progress construction of maps can be observed.\n #createMapModel(mapId: string, configProvider: () => Promise<MapConfig>): Promise<MapModel> {\n const creationJob = this.#modelCreationJobs.get(mapId);\n if (creationJob) {\n throw new Error(\"Internal error: a map model is already being created for this mapId\");\n }\n\n const modelPromise = this.#createMapModelImpl(mapId, configProvider)\n .then((mapModel) => {\n if (this.#destroyed) {\n mapModel.destroy();\n throw new Error(`MapRegistry has been destroyed.`);\n }\n\n const listener = on(\n mapModel.destroyed,\n () => {\n // Allow id reuse for dynamically created maps\n if (this.#isDynamic(mapId)) {\n const currentEntry = this.#entries.get(mapId);\n if (currentEntry === entry) {\n this.#entries.delete(mapId);\n }\n }\n },\n { dispatch: \"sync\" }\n );\n\n const entry: ModelJobResult = { kind: \"model\", model: mapModel, listener };\n this.#entries.set(mapId, entry);\n this.#modelCreationJobs.delete(mapId);\n this.#modelsByOlMap.set(mapModel.olMap, mapModel);\n return entry;\n })\n .catch((cause) => {\n const error = new Error(`Failed to construct map '${mapId}'`, { cause });\n const entry: ModelJobResult = { kind: \"error\", error };\n this.#modelCreationJobs.delete(mapId);\n if (!this.#isDynamic(mapId)) {\n // Don't store errors for dynamically created maps (the caller already got the error via promise).\n this.#entries.set(mapId, entry);\n }\n return entry;\n });\n this.#modelCreationJobs.set(mapId, modelPromise);\n return waitForResult(modelPromise);\n }\n\n async #createMapModelImpl(\n mapId: string,\n configProvider: () => Promise<MapConfig>\n ): Promise<MapModel> {\n LOG.info(`Creating map with id '${mapId}'`);\n const mapConfig = await configProvider();\n const mapModel = await createMapModel(\n mapId,\n mapConfig,\n this.#currentIntl,\n this.#httpService\n );\n return mapModel;\n }\n\n #isDynamic(mapId: string): boolean {\n return !this.#configProviders.has(mapId);\n }\n}\n\nasync function waitForResult(job: Promise<ModelJobResult>): Promise<MapModel> {\n return unbox(await job);\n}\n\nfunction unbox(entry: ModelJobResult): MapModel {\n if (entry.kind === \"error\") {\n throw entry.error;\n }\n return entry.model;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import OlBaseLayer from "ol/layer/Base";
|
|
2
|
+
import OlSource from "ol/source/Source";
|
|
2
3
|
import { MapModel } from "../model/MapModel";
|
|
3
4
|
import { InternalConstructorTag } from "../utils/InternalConstructorTag";
|
|
4
5
|
import { AbstractLayerBase } from "./AbstractLayerBase";
|
|
5
|
-
import { ATTACH_TO_MAP, GET_DEPS, LayerDependencies, SET_VISIBLE } from "./shared/internals";
|
|
6
|
+
import { ATTACH_TO_MAP, GET_DEPS, LayerDependencies, SET_METADATA_LOAD_INFO, SET_VISIBLE } from "./shared/internals";
|
|
6
7
|
import { SimpleLayerConfig } from "./SimpleLayer";
|
|
7
8
|
import { LayerTypes } from "./unions";
|
|
8
9
|
/**
|
|
@@ -11,6 +12,15 @@ import { LayerTypes } from "./unions";
|
|
|
11
12
|
* @group Layer Utilities
|
|
12
13
|
**/
|
|
13
14
|
export type LayerLoadState = "not-loaded" | "loading" | "loaded" | "error";
|
|
15
|
+
/**
|
|
16
|
+
* Load state of a single layer, bundling the state with its error.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export type LayerLoadInfo = "not-loaded" | "loading" | "loaded" | {
|
|
21
|
+
kind: "error";
|
|
22
|
+
error: Error;
|
|
23
|
+
};
|
|
14
24
|
/**
|
|
15
25
|
* Represents an operational layer in the map.
|
|
16
26
|
*
|
|
@@ -23,7 +33,10 @@ export type LayerLoadState = "not-loaded" | "loading" | "loaded" | "error";
|
|
|
23
33
|
*/
|
|
24
34
|
export declare abstract class AbstractLayer extends AbstractLayerBase {
|
|
25
35
|
#private;
|
|
26
|
-
constructor(config: SimpleLayerConfig
|
|
36
|
+
constructor(config: SimpleLayerConfig & {
|
|
37
|
+
/** The initial load state (default: not-loaded). */
|
|
38
|
+
initialLoadInfo?: LayerLoadInfo;
|
|
39
|
+
}, deps?: LayerDependencies, internalTag?: InternalConstructorTag);
|
|
27
40
|
destroy(): void;
|
|
28
41
|
/**
|
|
29
42
|
* Identifies the type of this layer.
|
|
@@ -34,6 +47,10 @@ export declare abstract class AbstractLayer extends AbstractLayerBase {
|
|
|
34
47
|
* The raw OpenLayers layer.
|
|
35
48
|
*/
|
|
36
49
|
get olLayer(): OlBaseLayer;
|
|
50
|
+
/**
|
|
51
|
+
* The current source of the {@link olLayer}.
|
|
52
|
+
*/
|
|
53
|
+
get olSource(): OlSource | undefined;
|
|
37
54
|
/**
|
|
38
55
|
* True if this layer is a base layer.
|
|
39
56
|
*
|
|
@@ -41,9 +58,19 @@ export declare abstract class AbstractLayer extends AbstractLayerBase {
|
|
|
41
58
|
*/
|
|
42
59
|
get isBaseLayer(): boolean;
|
|
43
60
|
/**
|
|
44
|
-
* The load state of a layer.
|
|
61
|
+
* The combined load state of a layer.
|
|
62
|
+
*
|
|
63
|
+
* Aggregates the OpenLayers source state, health check and metadata state.
|
|
64
|
+
* Priority for the state: `error` > `loading` > `not-loaded` > `loaded`.
|
|
45
65
|
*/
|
|
46
66
|
get loadState(): LayerLoadState;
|
|
67
|
+
/**
|
|
68
|
+
* The error (if any) that prevented this layer from loading.
|
|
69
|
+
*
|
|
70
|
+
* Combines errors from the OpenLayers source, the health check and the
|
|
71
|
+
* metadata request (health > metadata > source).
|
|
72
|
+
*/
|
|
73
|
+
get loadError(): Error | undefined;
|
|
47
74
|
/**
|
|
48
75
|
* The minimum resolution (inclusive) at which this layer will be visible.
|
|
49
76
|
*/
|
|
@@ -86,6 +113,12 @@ export declare abstract class AbstractLayer extends AbstractLayerBase {
|
|
|
86
113
|
* @internal
|
|
87
114
|
*/
|
|
88
115
|
[ATTACH_TO_MAP](map: MapModel): void;
|
|
116
|
+
/**
|
|
117
|
+
* Updates the load state of the layer's metadata request.
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
[SET_METADATA_LOAD_INFO](loadInfo: LayerLoadInfo): void;
|
|
89
122
|
setVisible(newVisibility: boolean): void;
|
|
90
123
|
/** @internal */
|
|
91
124
|
[SET_VISIBLE](newVisibility: boolean): void;
|
package/layers/AbstractLayer.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { synchronized, reactive,
|
|
2
|
-
import { createLogger
|
|
1
|
+
import { computed, synchronized, reactive, constant } from '@conterra/reactivity-core';
|
|
2
|
+
import { createLogger } from '@open-pioneer/core';
|
|
3
3
|
import OlLayer from 'ol/layer/Layer.js';
|
|
4
4
|
import { unByKey } from 'ol/Observable.js';
|
|
5
5
|
import { sourceId } from '../_virtual/source-info.js';
|
|
6
6
|
import { AbstractLayerBase } from './AbstractLayerBase.js';
|
|
7
|
-
import { getLayerDependencies, SET_VISIBLE, ATTACH_TO_MAP, GET_DEPS, LAYER_DEPS } from './shared/internals.js';
|
|
7
|
+
import { getLayerDependencies, SET_VISIBLE, ATTACH_TO_MAP, SET_METADATA_LOAD_INFO, GET_DEPS, LAYER_DEPS } from './shared/internals.js';
|
|
8
8
|
|
|
9
9
|
const LOG = createLogger(sourceId);
|
|
10
10
|
class AbstractLayer extends AbstractLayerBase {
|
|
@@ -13,6 +13,7 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
13
13
|
// from the map, once connected).
|
|
14
14
|
#deps;
|
|
15
15
|
#olLayer;
|
|
16
|
+
#olSource;
|
|
16
17
|
#isBaseLayer;
|
|
17
18
|
#healthCheck;
|
|
18
19
|
#visible;
|
|
@@ -20,13 +21,20 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
20
21
|
#maxResolution;
|
|
21
22
|
#minZoom;
|
|
22
23
|
#maxZoom;
|
|
23
|
-
#
|
|
24
|
-
#
|
|
24
|
+
#sourceState;
|
|
25
|
+
#sourceInfo;
|
|
26
|
+
#healthInfo;
|
|
27
|
+
#healthCheckStarted = false;
|
|
28
|
+
#metadataInfo;
|
|
29
|
+
#loadInfo = computed(
|
|
30
|
+
() => combineLoadInfos(this.#sourceInfo.value, this.#healthInfo.value, this.#metadataInfo.value)
|
|
31
|
+
);
|
|
25
32
|
#visibleInScale;
|
|
26
33
|
constructor(config, deps, internalTag) {
|
|
27
34
|
super(config);
|
|
28
35
|
this.#deps = getLayerDependencies(deps, internalTag);
|
|
29
36
|
this.#olLayer = config.olLayer;
|
|
37
|
+
this.#olSource = synchronizedOlSource(this.#olLayer);
|
|
30
38
|
this.#isBaseLayer = config.isBaseLayer ?? false;
|
|
31
39
|
this.#healthCheck = config.healthCheck;
|
|
32
40
|
this.#visible = synchronized(
|
|
@@ -64,7 +72,22 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
64
72
|
return () => unByKey(key);
|
|
65
73
|
}
|
|
66
74
|
);
|
|
67
|
-
this.#
|
|
75
|
+
this.#sourceState = computed(() => {
|
|
76
|
+
const olSource = this.olSource;
|
|
77
|
+
if (!olSource) {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
return synchronized(
|
|
81
|
+
() => getSourceState(olSource),
|
|
82
|
+
(cb) => {
|
|
83
|
+
const key = olSource.on("change", cb);
|
|
84
|
+
return () => unByKey(key);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
this.#sourceInfo = computed(() => makeSourceInfo(this.id, this.#sourceState.value?.value));
|
|
89
|
+
this.#healthInfo = reactive("not-loaded");
|
|
90
|
+
this.#metadataInfo = reactive(config.initialLoadInfo ?? "not-loaded");
|
|
68
91
|
this[SET_VISIBLE](config.visible ?? true);
|
|
69
92
|
this.#visibleInScale = computed(() => {
|
|
70
93
|
const map = this.nullableMap;
|
|
@@ -102,7 +125,6 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
102
125
|
if (this.isDestroyed) {
|
|
103
126
|
return;
|
|
104
127
|
}
|
|
105
|
-
this.#stateWatchResource = destroyResource(this.#stateWatchResource);
|
|
106
128
|
this.olLayer.dispose();
|
|
107
129
|
super.destroy();
|
|
108
130
|
}
|
|
@@ -115,6 +137,12 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
115
137
|
get olLayer() {
|
|
116
138
|
return this.#olLayer;
|
|
117
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* The current source of the {@link olLayer}.
|
|
142
|
+
*/
|
|
143
|
+
get olSource() {
|
|
144
|
+
return this.#olSource.value;
|
|
145
|
+
}
|
|
118
146
|
/**
|
|
119
147
|
* True if this layer is a base layer.
|
|
120
148
|
*
|
|
@@ -124,10 +152,22 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
124
152
|
return this.#isBaseLayer;
|
|
125
153
|
}
|
|
126
154
|
/**
|
|
127
|
-
* The load state of a layer.
|
|
155
|
+
* The combined load state of a layer.
|
|
156
|
+
*
|
|
157
|
+
* Aggregates the OpenLayers source state, health check and metadata state.
|
|
158
|
+
* Priority for the state: `error` > `loading` > `not-loaded` > `loaded`.
|
|
128
159
|
*/
|
|
129
160
|
get loadState() {
|
|
130
|
-
return this.#
|
|
161
|
+
return loadInfoState(this.#loadInfo.value);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The error (if any) that prevented this layer from loading.
|
|
165
|
+
*
|
|
166
|
+
* Combines errors from the OpenLayers source, the health check and the
|
|
167
|
+
* metadata request (health > metadata > source).
|
|
168
|
+
*/
|
|
169
|
+
get loadError() {
|
|
170
|
+
return errorOf(this.#loadInfo.value);
|
|
131
171
|
}
|
|
132
172
|
/**
|
|
133
173
|
* The minimum resolution (inclusive) at which this layer will be visible.
|
|
@@ -190,18 +230,21 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
190
230
|
*/
|
|
191
231
|
[ATTACH_TO_MAP](map) {
|
|
192
232
|
super[ATTACH_TO_MAP](map);
|
|
193
|
-
if (!this.#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.#
|
|
197
|
-
|
|
198
|
-
this.#loadState.value = state;
|
|
199
|
-
}
|
|
200
|
-
);
|
|
201
|
-
this.#stateWatchResource = stateWatchResource;
|
|
202
|
-
this.#loadState.value = initialState;
|
|
233
|
+
if (!this.#healthCheckStarted && errorOf(this.#sourceInfo.value) == null) {
|
|
234
|
+
this.#healthCheckStarted = true;
|
|
235
|
+
doHealthCheck(this, this.#healthCheck).then((loadInfo) => {
|
|
236
|
+
this.#healthInfo.value = loadInfo;
|
|
237
|
+
});
|
|
203
238
|
}
|
|
204
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Updates the load state of the layer's metadata request.
|
|
242
|
+
*
|
|
243
|
+
* @internal
|
|
244
|
+
*/
|
|
245
|
+
[SET_METADATA_LOAD_INFO](loadInfo) {
|
|
246
|
+
this.#metadataInfo.value = loadInfo;
|
|
247
|
+
}
|
|
205
248
|
setVisible(newVisibility) {
|
|
206
249
|
if (this.isBaseLayer) {
|
|
207
250
|
LOG.warn(
|
|
@@ -233,58 +276,6 @@ class AbstractLayer extends AbstractLayerBase {
|
|
|
233
276
|
);
|
|
234
277
|
}
|
|
235
278
|
}
|
|
236
|
-
function watchLoadState(layer, healthCheck, onChange) {
|
|
237
|
-
const olLayer = layer.olLayer;
|
|
238
|
-
if (!(olLayer instanceof OlLayer)) {
|
|
239
|
-
return {
|
|
240
|
-
initial: "loaded",
|
|
241
|
-
resource: {
|
|
242
|
-
destroy() {
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
let currentSource = getSource(olLayer);
|
|
248
|
-
const currentOlLayerState = getSourceState(currentSource);
|
|
249
|
-
let currentLoadState = currentOlLayerState;
|
|
250
|
-
let currentHealthState = "loading";
|
|
251
|
-
if (currentOlLayerState !== "error") {
|
|
252
|
-
doHealthCheck(layer, healthCheck).then((state) => {
|
|
253
|
-
currentHealthState = state;
|
|
254
|
-
updateState();
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
const updateState = () => {
|
|
258
|
-
const olLayerState = getSourceState(currentSource);
|
|
259
|
-
const nextLoadState = currentHealthState === "error" ? "error" : olLayerState;
|
|
260
|
-
if (currentLoadState !== nextLoadState) {
|
|
261
|
-
currentLoadState = nextLoadState;
|
|
262
|
-
onChange(currentLoadState);
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
let stateHandle;
|
|
266
|
-
stateHandle = currentSource?.on("change", () => {
|
|
267
|
-
updateState();
|
|
268
|
-
});
|
|
269
|
-
const sourceHandle = olLayer.on("change:source", () => {
|
|
270
|
-
stateHandle && unByKey(stateHandle);
|
|
271
|
-
stateHandle = void 0;
|
|
272
|
-
currentSource = getSource(olLayer);
|
|
273
|
-
stateHandle = currentSource?.on("change", () => {
|
|
274
|
-
updateState();
|
|
275
|
-
});
|
|
276
|
-
updateState();
|
|
277
|
-
});
|
|
278
|
-
return {
|
|
279
|
-
initial: currentLoadState,
|
|
280
|
-
resource: {
|
|
281
|
-
destroy() {
|
|
282
|
-
stateHandle && unByKey(stateHandle);
|
|
283
|
-
unByKey(sourceHandle);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
279
|
async function doHealthCheck(layer, healthCheck) {
|
|
289
280
|
if (healthCheck == null) {
|
|
290
281
|
return "loaded";
|
|
@@ -309,23 +300,76 @@ async function doHealthCheck(layer, healthCheck) {
|
|
|
309
300
|
`Unexpected object for 'healthCheck' parameter of layer '${layer.id}'`,
|
|
310
301
|
healthCheck
|
|
311
302
|
);
|
|
312
|
-
return
|
|
303
|
+
return {
|
|
304
|
+
kind: "error",
|
|
305
|
+
error: new Error(`Invalid 'healthCheck' configuration for layer '${layer.id}'`)
|
|
306
|
+
};
|
|
313
307
|
}
|
|
314
308
|
try {
|
|
315
|
-
|
|
309
|
+
const state = await healthCheckFn(layer);
|
|
310
|
+
if (state === "error") {
|
|
311
|
+
return {
|
|
312
|
+
kind: "error",
|
|
313
|
+
error: new Error(`Health check failed for layer '${layer.id}'`)
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return state;
|
|
316
317
|
} catch (e) {
|
|
317
318
|
LOG.warn(`Health check failed for layer '${layer.id}'`, e);
|
|
318
|
-
return
|
|
319
|
+
return {
|
|
320
|
+
kind: "error",
|
|
321
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
322
|
+
};
|
|
319
323
|
}
|
|
320
324
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
325
|
+
const LOAD_STATE_SEVERITY = {
|
|
326
|
+
"not-loaded": 0,
|
|
327
|
+
loaded: 1,
|
|
328
|
+
loading: 2,
|
|
329
|
+
error: 3
|
|
330
|
+
};
|
|
331
|
+
function higherSeverity(a, b) {
|
|
332
|
+
return LOAD_STATE_SEVERITY[a] >= LOAD_STATE_SEVERITY[b] ? a : b;
|
|
333
|
+
}
|
|
334
|
+
function combineLoadInfos(source, health, metadata) {
|
|
335
|
+
const error = errorOf(health) ?? errorOf(metadata) ?? errorOf(source);
|
|
336
|
+
if (error) {
|
|
337
|
+
return { kind: "error", error };
|
|
338
|
+
}
|
|
339
|
+
return higherSeverity(loadInfoState(source), loadInfoState(metadata));
|
|
340
|
+
}
|
|
341
|
+
function errorOf(info) {
|
|
342
|
+
return typeof info === "object" ? info.error : void 0;
|
|
343
|
+
}
|
|
344
|
+
function loadInfoState(info) {
|
|
345
|
+
return typeof info === "object" ? "error" : info;
|
|
346
|
+
}
|
|
347
|
+
function makeSourceInfo(id, state) {
|
|
348
|
+
if (state == null) {
|
|
349
|
+
return "loaded";
|
|
324
350
|
}
|
|
325
|
-
|
|
351
|
+
if (state === "error") {
|
|
352
|
+
return {
|
|
353
|
+
kind: "error",
|
|
354
|
+
error: new Error(`Source of layer '${id}' is in error state`)
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
return state;
|
|
358
|
+
}
|
|
359
|
+
function synchronizedOlSource(layer) {
|
|
360
|
+
if (!(layer instanceof OlLayer)) {
|
|
361
|
+
return constant(void 0);
|
|
362
|
+
}
|
|
363
|
+
return synchronized(
|
|
364
|
+
() => layer.getSource() ?? void 0,
|
|
365
|
+
(cb) => {
|
|
366
|
+
const key = layer.on("change:source", cb);
|
|
367
|
+
return () => unByKey(key);
|
|
368
|
+
}
|
|
369
|
+
);
|
|
326
370
|
}
|
|
327
371
|
function getSourceState(olSource) {
|
|
328
|
-
const state = olSource
|
|
372
|
+
const state = olSource.getState();
|
|
329
373
|
switch (state) {
|
|
330
374
|
case void 0:
|
|
331
375
|
return "loaded";
|