@vcmap/core 6.3.0-rc.1 → 6.3.0-rc.2

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 (159) hide show
  1. package/dist/cesium.d.ts +12 -0
  2. package/dist/index.d.ts +10 -2
  3. package/dist/index.js +9 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/ol.d.ts +13 -2
  6. package/dist/src/cesium/cesium3DTileFeature.d.ts +1 -0
  7. package/dist/src/cesium/cesium3DTileFeature.js +16 -3
  8. package/dist/src/cesium/cesium3DTileFeature.js.map +1 -1
  9. package/dist/src/cesium/cesium3DTilePointFeature.js +2 -1
  10. package/dist/src/cesium/cesium3DTilePointFeature.js.map +1 -1
  11. package/dist/src/classRegistry.d.ts +7 -1
  12. package/dist/src/classRegistry.js.map +1 -1
  13. package/dist/src/featureProvider/abstractAttributeProvider.d.ts +62 -0
  14. package/dist/src/featureProvider/abstractAttributeProvider.js +129 -0
  15. package/dist/src/featureProvider/abstractAttributeProvider.js.map +1 -0
  16. package/dist/src/featureProvider/abstractFeatureProvider.d.ts +9 -18
  17. package/dist/src/featureProvider/abstractFeatureProvider.js +7 -38
  18. package/dist/src/featureProvider/abstractFeatureProvider.js.map +1 -1
  19. package/dist/src/featureProvider/compositeFeatureProvider.d.ts +27 -0
  20. package/dist/src/featureProvider/compositeFeatureProvider.js +53 -0
  21. package/dist/src/featureProvider/compositeFeatureProvider.js.map +1 -0
  22. package/dist/src/featureProvider/csvAttributeProvider.d.ts +41 -0
  23. package/dist/src/featureProvider/csvAttributeProvider.js +126 -0
  24. package/dist/src/featureProvider/csvAttributeProvider.js.map +1 -0
  25. package/dist/src/featureProvider/featureProviderFactory.d.ts +3 -0
  26. package/dist/src/featureProvider/featureProviderFactory.js +17 -0
  27. package/dist/src/featureProvider/featureProviderFactory.js.map +1 -0
  28. package/dist/src/featureProvider/i3sAttributeProvider.d.ts +7 -0
  29. package/dist/src/featureProvider/i3sAttributeProvider.js +43 -0
  30. package/dist/src/featureProvider/i3sAttributeProvider.js.map +1 -0
  31. package/dist/src/featureProvider/jsonAttributeProvider.d.ts +26 -0
  32. package/dist/src/featureProvider/jsonAttributeProvider.js +73 -0
  33. package/dist/src/featureProvider/jsonAttributeProvider.js.map +1 -0
  34. package/dist/src/featureProvider/tileProviderFeatureProvider.d.ts +4 -6
  35. package/dist/src/featureProvider/tileProviderFeatureProvider.js +9 -11
  36. package/dist/src/featureProvider/tileProviderFeatureProvider.js.map +1 -1
  37. package/dist/src/featureProvider/urlIdAttributeProvider.d.ts +28 -0
  38. package/dist/src/featureProvider/urlIdAttributeProvider.js +50 -0
  39. package/dist/src/featureProvider/urlIdAttributeProvider.js.map +1 -0
  40. package/dist/src/featureProvider/wmsFeatureProvider.d.ts +12 -3
  41. package/dist/src/featureProvider/wmsFeatureProvider.js +17 -6
  42. package/dist/src/featureProvider/wmsFeatureProvider.js.map +1 -1
  43. package/dist/src/interaction/featureAtPixelInteraction.d.ts +15 -3
  44. package/dist/src/interaction/featureAtPixelInteraction.js +41 -1
  45. package/dist/src/interaction/featureAtPixelInteraction.js.map +1 -1
  46. package/dist/src/interaction/featureProviderInteraction.js +42 -26
  47. package/dist/src/interaction/featureProviderInteraction.js.map +1 -1
  48. package/dist/src/layer/cesium/cesiumTilesetCesiumImpl.d.ts +14 -5
  49. package/dist/src/layer/cesium/cesiumTilesetCesiumImpl.js +243 -128
  50. package/dist/src/layer/cesium/cesiumTilesetCesiumImpl.js.map +1 -1
  51. package/dist/src/layer/cesium/i3sCesiumImpl.d.ts +33 -0
  52. package/dist/src/layer/cesium/i3sCesiumImpl.js +107 -0
  53. package/dist/src/layer/cesium/i3sCesiumImpl.js.map +1 -0
  54. package/dist/src/layer/cesium/vcsTile/vcsQuadtreeTileProvider.js +2 -1
  55. package/dist/src/layer/cesium/vcsTile/vcsQuadtreeTileProvider.js.map +1 -1
  56. package/dist/src/layer/cesium/vectorRasterTileCesiumImpl.js +7 -6
  57. package/dist/src/layer/cesium/vectorRasterTileCesiumImpl.js.map +1 -1
  58. package/dist/src/layer/cesium/vectorTileImageryProvider.js +2 -2
  59. package/dist/src/layer/cesium/vectorTileImageryProvider.js.map +1 -1
  60. package/dist/src/layer/cesiumTilesetLayer.d.ts +9 -0
  61. package/dist/src/layer/cesiumTilesetLayer.js +26 -1
  62. package/dist/src/layer/cesiumTilesetLayer.js.map +1 -1
  63. package/dist/src/layer/i3sLayer.d.ts +80 -0
  64. package/dist/src/layer/i3sLayer.js +242 -0
  65. package/dist/src/layer/i3sLayer.js.map +1 -0
  66. package/dist/src/layer/layer.d.ts +8 -3
  67. package/dist/src/layer/layer.js +7 -1
  68. package/dist/src/layer/layer.js.map +1 -1
  69. package/dist/src/layer/layerSymbols.d.ts +4 -0
  70. package/dist/src/layer/layerSymbols.js +4 -0
  71. package/dist/src/layer/layerSymbols.js.map +1 -1
  72. package/dist/src/layer/panoramaDatasetLayer.d.ts +2 -0
  73. package/dist/src/layer/panoramaDatasetLayer.js +33 -13
  74. package/dist/src/layer/panoramaDatasetLayer.js.map +1 -1
  75. package/dist/src/layer/tileProvider/flatGeobufTileProvider.js +3 -2
  76. package/dist/src/layer/tileProvider/flatGeobufTileProvider.js.map +1 -1
  77. package/dist/src/layer/tileProvider/mvtTileProvider.js +3 -2
  78. package/dist/src/layer/tileProvider/mvtTileProvider.js.map +1 -1
  79. package/dist/src/layer/tileProvider/tileProvider.d.ts +6 -0
  80. package/dist/src/layer/tileProvider/tileProvider.js +12 -1
  81. package/dist/src/layer/tileProvider/tileProvider.js.map +1 -1
  82. package/dist/src/layer/vectorLayer.js +2 -2
  83. package/dist/src/layer/vectorLayer.js.map +1 -1
  84. package/dist/src/layer/vectorProperties.js +10 -1
  85. package/dist/src/layer/vectorProperties.js.map +1 -1
  86. package/dist/src/layer/vectorTileLayer.d.ts +14 -5
  87. package/dist/src/layer/vectorTileLayer.js +78 -26
  88. package/dist/src/layer/vectorTileLayer.js.map +1 -1
  89. package/dist/src/layer/wmsLayer.d.ts +3 -0
  90. package/dist/src/layer/wmsLayer.js +62 -32
  91. package/dist/src/layer/wmsLayer.js.map +1 -1
  92. package/dist/src/map/baseCesiumMap.d.ts +4 -4
  93. package/dist/src/map/baseCesiumMap.js +12 -0
  94. package/dist/src/map/baseCesiumMap.js.map +1 -1
  95. package/dist/src/map/cesiumMap.js +0 -11
  96. package/dist/src/map/cesiumMap.js.map +1 -1
  97. package/dist/src/map/obliqueMap.js +11 -4
  98. package/dist/src/map/obliqueMap.js.map +1 -1
  99. package/dist/src/map/panoramaMap.js +1 -1
  100. package/dist/src/panorama/panoramaImage.js +6 -5
  101. package/dist/src/panorama/panoramaImage.js.map +1 -1
  102. package/dist/src/style/declarativeStyleItem.js +7 -8
  103. package/dist/src/style/declarativeStyleItem.js.map +1 -1
  104. package/dist/src/util/fetch.d.ts +7 -0
  105. package/dist/src/util/fetch.js +7 -0
  106. package/dist/src/util/fetch.js.map +1 -1
  107. package/dist/src/vcsApp.d.ts +2 -3
  108. package/dist/src/vcsApp.js.map +1 -1
  109. package/dist/src/vcsModuleHelpers.d.ts +5 -2
  110. package/dist/src/vcsModuleHelpers.js +27 -1
  111. package/dist/src/vcsModuleHelpers.js.map +1 -1
  112. package/dist/tests/unit/helpers/cesiumHelpers.js +7 -1
  113. package/dist/tests/unit/helpers/cesiumHelpers.js.map +1 -1
  114. package/index.ts +27 -0
  115. package/package.json +2 -2
  116. package/src/cesium/cesium.d.ts +12 -0
  117. package/src/cesium/cesium3DTileFeature.ts +25 -3
  118. package/src/cesium/cesium3DTilePointFeature.ts +3 -1
  119. package/src/classRegistry.ts +8 -3
  120. package/src/featureProvider/abstractAttributeProvider.ts +201 -0
  121. package/src/featureProvider/abstractFeatureProvider.ts +27 -47
  122. package/src/featureProvider/compositeFeatureProvider.ts +103 -0
  123. package/src/featureProvider/csvAttributeProvider.ts +186 -0
  124. package/src/featureProvider/featureProviderFactory.ts +31 -0
  125. package/src/featureProvider/i3sAttributeProvider.ts +60 -0
  126. package/src/featureProvider/jsonAttributeProvider.ts +109 -0
  127. package/src/featureProvider/tileProviderFeatureProvider.ts +13 -14
  128. package/src/featureProvider/urlIdAttributeProvider.ts +82 -0
  129. package/src/featureProvider/wmsFeatureProvider.ts +24 -7
  130. package/src/global.d.ts +2 -0
  131. package/src/interaction/featureAtPixelInteraction.ts +53 -3
  132. package/src/interaction/featureProviderInteraction.ts +59 -38
  133. package/src/layer/cesium/cesiumTilesetCesiumImpl.ts +296 -157
  134. package/src/layer/cesium/i3sCesiumImpl.ts +141 -0
  135. package/src/layer/cesium/vcsTile/vcsQuadtreeTileProvider.ts +4 -3
  136. package/src/layer/cesium/vectorRasterTileCesiumImpl.ts +7 -6
  137. package/src/layer/cesium/vectorTileImageryProvider.ts +2 -2
  138. package/src/layer/cesiumTilesetLayer.ts +51 -1
  139. package/src/layer/i3sLayer.ts +343 -0
  140. package/src/layer/layer.ts +30 -3
  141. package/src/layer/layerSymbols.ts +5 -0
  142. package/src/layer/panoramaDatasetLayer.ts +44 -13
  143. package/src/layer/tileProvider/flatGeobufTileProvider.ts +3 -2
  144. package/src/layer/tileProvider/mvtTileProvider.ts +3 -2
  145. package/src/layer/tileProvider/tileProvider.ts +13 -1
  146. package/src/layer/vectorLayer.ts +4 -2
  147. package/src/layer/vectorProperties.ts +10 -1
  148. package/src/layer/vectorTileLayer.ts +135 -47
  149. package/src/layer/wmsLayer.ts +77 -44
  150. package/src/map/baseCesiumMap.ts +29 -5
  151. package/src/map/cesiumMap.ts +0 -15
  152. package/src/map/obliqueMap.ts +13 -6
  153. package/src/map/panoramaMap.ts +1 -1
  154. package/src/ol/ol.d.ts +13 -2
  155. package/src/panorama/panoramaImage.ts +8 -5
  156. package/src/style/declarativeStyleItem.ts +7 -9
  157. package/src/util/fetch.ts +7 -0
  158. package/src/vcsApp.ts +7 -8
  159. package/src/vcsModuleHelpers.ts +62 -4
@@ -1,12 +1,14 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { parseBoolean } from '@vcsuite/parsers';
3
2
  import type { Feature } from 'ol/index.js';
4
3
  import type { Coordinate } from 'ol/coordinate.js';
5
4
 
6
5
  import { vcsLayerName } from '../layer/layerSymbols.js';
7
6
  import VcsObject, { type VcsObjectOptions } from '../vcsObject.js';
8
7
  import { getStyleOrDefaultStyle } from '../style/styleFactory.js';
9
- import { defaultVectorStyle } from '../style/vectorStyleItem.js';
8
+ import {
9
+ defaultVectorStyle,
10
+ type VectorStyleItemOptions,
11
+ } from '../style/vectorStyleItem.js';
10
12
  import VectorProperties, {
11
13
  type VectorPropertiesOptions,
12
14
  } from '../layer/vectorProperties.js';
@@ -14,20 +16,22 @@ import { isProvidedFeature } from './featureProviderSymbols.js';
14
16
  import type StyleItem from '../style/styleItem.js';
15
17
  import { type StyleItemOptions } from '../style/styleItem.js';
16
18
  import type VcsMap from '../map/vcsMap.js';
19
+ import type { DeclarativeStyleItemOptions } from '../style/declarativeStyleItem.js';
20
+ import type Layer from '../layer/layer.js';
17
21
 
18
22
  export type AbstractFeatureProviderOptions = VcsObjectOptions & {
19
23
  /**
20
24
  * the style to apply to features created by this feature provider
21
25
  */
22
- style?: StyleItemOptions | StyleItem;
26
+ style?:
27
+ | StyleItemOptions
28
+ | VectorStyleItemOptions
29
+ | DeclarativeStyleItemOptions
30
+ | StyleItem;
23
31
  /**
24
32
  * the vector properties of the features. Allow picking is false by default.
25
33
  */
26
34
  vectorProperties?: VectorProperties | VectorPropertiesOptions;
27
- /**
28
- * show the resulting geometry in the map
29
- */
30
- showGeometry?: boolean;
31
35
  /**
32
36
  * can be used to constrict the featureProvider to specific mapTypes empty array means no restriction
33
37
  */
@@ -38,7 +42,7 @@ export type AbstractFeatureProviderOptions = VcsObjectOptions & {
38
42
  * An abstract class providing features for {@link Layer}s which cannot provide features directly, but can provide features for
39
43
  * a given location, e.g. WmsLayer with a getFeatureInfo configuration. In this case, a feature provider can be created for this layer.
40
44
  */
41
- class AbstractFeatureProvider extends VcsObject {
45
+ abstract class AbstractFeatureProvider extends VcsObject {
42
46
  static get className(): string {
43
47
  return 'AbstractFeatureProvider';
44
48
  }
@@ -48,26 +52,15 @@ class AbstractFeatureProvider extends VcsObject {
48
52
  vectorProperties: {
49
53
  allowPicking: false,
50
54
  },
51
- showGeometry: false,
52
55
  mapTypes: [],
53
56
  };
54
57
  }
55
58
 
56
- /**
57
- * The layer name of the associated layer
58
- */
59
- layerName: string;
60
-
61
59
  /**
62
60
  * The style set on features created by this provider
63
61
  */
64
62
  style: StyleItem | undefined;
65
63
 
66
- /**
67
- * Whether to show the geometry on selection.
68
- */
69
- showGeometry: boolean;
70
-
71
64
  /**
72
65
  * The vector properties assigned to features created by this provider
73
66
  */
@@ -78,21 +71,14 @@ class AbstractFeatureProvider extends VcsObject {
78
71
  */
79
72
  mapTypes: string[];
80
73
 
81
- constructor(layerName: string, options: AbstractFeatureProviderOptions) {
74
+ constructor(options: AbstractFeatureProviderOptions) {
82
75
  const defaultOptions = AbstractFeatureProvider.getDefaultOptions();
83
76
  super({ ...defaultOptions, ...options });
84
77
 
85
- this.layerName = layerName;
86
-
87
78
  this.style = options.style
88
79
  ? getStyleOrDefaultStyle(options.style, defaultVectorStyle.clone())
89
80
  : undefined;
90
81
 
91
- this.showGeometry = parseBoolean(
92
- options.showGeometry,
93
- defaultOptions.showGeometry,
94
- );
95
-
96
82
  this.vectorProperties =
97
83
  options.vectorProperties instanceof VectorProperties
98
84
  ? options.vectorProperties
@@ -120,14 +106,14 @@ class AbstractFeatureProvider extends VcsObject {
120
106
  * Ensures the feature has an ID, applies all vectorProperties and adds style and the vcsLayerName
121
107
  * and isProvidedFeature symbols to the feature
122
108
  */
123
- getProviderFeature(feature: Feature): Feature {
109
+ getProviderFeature(feature: Feature, layer: Layer): Feature {
124
110
  if (!feature.getId()) {
125
111
  feature.setId(uuidv4());
126
112
  }
127
113
  if (this.style) {
128
114
  feature.setStyle(this.style.style);
129
115
  }
130
- feature[vcsLayerName] = this.layerName;
116
+ feature[vcsLayerName] = layer.name;
131
117
  feature[isProvidedFeature] = true;
132
118
  Object.entries(this.vectorProperties.getValues()).forEach(
133
119
  ([key, value]) => {
@@ -146,19 +132,13 @@ class AbstractFeatureProvider extends VcsObject {
146
132
  * to handle your feature is called: (e.g. <code>return features.map(f => this.getProviderFeature(f)</code>);
147
133
  * @param coordinate - in mercator
148
134
  * @param resolution - meters per pixel for the given location
149
- * @param headers - headers optional request headers to be sent with the server request
135
+ * @param layer - the layer to request the features for
150
136
  */
151
- // eslint-disable-next-line class-methods-use-this
152
- getFeaturesByCoordinate(
153
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
154
- _coordinate: Coordinate,
155
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
156
- _resolution: number,
157
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
158
- _headers?: Record<string, string>,
159
- ): Promise<Feature[]> {
160
- return Promise.resolve([]);
161
- }
137
+ abstract getFeaturesByCoordinate(
138
+ coordinate: Coordinate,
139
+ resolution: number,
140
+ layer: Layer,
141
+ ): Promise<Feature[]>;
162
142
 
163
143
  /**
164
144
  * Returns the object required to configure this feature provider.
@@ -168,12 +148,6 @@ class AbstractFeatureProvider extends VcsObject {
168
148
  ): AbstractFeatureProviderOptions {
169
149
  const config: AbstractFeatureProviderOptions = super.toJSON(defaultOptions);
170
150
 
171
- delete config.name; // the name is irrelevant, since its the layers name
172
-
173
- if (this.showGeometry !== defaultOptions.showGeometry) {
174
- config.showGeometry = this.showGeometry;
175
- }
176
-
177
151
  if (this.style) {
178
152
  config.style = this.style.toJSON();
179
153
  }
@@ -182,9 +156,15 @@ class AbstractFeatureProvider extends VcsObject {
182
156
  ...VectorProperties.getDefaultOptions(),
183
157
  ...(defaultOptions.vectorProperties as VectorPropertiesOptions),
184
158
  });
159
+
185
160
  if (Object.keys(vectorPropertiesConfig).length > 0) {
186
161
  config.vectorProperties = vectorPropertiesConfig;
187
162
  }
163
+
164
+ if (this.mapTypes.length > 0) {
165
+ config.mapTypes = this.mapTypes.slice();
166
+ }
167
+
188
168
  return config;
189
169
  }
190
170
 
@@ -0,0 +1,103 @@
1
+ import type Feature from 'ol/Feature.js';
2
+ import type { Extent } from 'ol/extent.js';
3
+ import type { AbstractFeatureProviderOptions } from './abstractFeatureProvider.js';
4
+ import AbstractFeatureProvider from './abstractFeatureProvider.js';
5
+ import AbstractAttributeProvider, {
6
+ type AbstractAttributeProviderOptions,
7
+ type AttributeProvider,
8
+ } from './abstractAttributeProvider.js';
9
+ import type { EventFeature } from '../interaction/abstractInteraction.js';
10
+ import type Layer from '../layer/layer.js';
11
+ import { getProviderForOption } from './featureProviderFactory.js';
12
+
13
+ export type CompositeFeatureProviderOptions = AbstractFeatureProviderOptions & {
14
+ featureProviders: (
15
+ | AbstractFeatureProviderOptions
16
+ | AbstractFeatureProvider
17
+ )[];
18
+ attributeProviders: (
19
+ | AbstractAttributeProviderOptions
20
+ | AbstractAttributeProvider
21
+ )[];
22
+ };
23
+
24
+ /**
25
+ * A feature provider that combines multiple feature providers and attribute providers.
26
+ * Used to combine multiple feature & attribute providers for a layer.
27
+ * The order of providers is important, as attributes are applied and
28
+ * features are returned in the order.
29
+ */
30
+ export default class CompositeFeatureProvider
31
+ extends AbstractFeatureProvider
32
+ implements AttributeProvider
33
+ {
34
+ static get className(): string {
35
+ return 'CompositeFeatureProvider';
36
+ }
37
+
38
+ private _attributeProviders: AttributeProvider[];
39
+
40
+ private _featureProviders: AbstractFeatureProvider[];
41
+
42
+ constructor(options: CompositeFeatureProviderOptions) {
43
+ super(options);
44
+ this._featureProviders = options.featureProviders
45
+ .map(getProviderForOption)
46
+ .filter((fp) => fp instanceof AbstractFeatureProvider);
47
+
48
+ this._featureProviders.forEach((fp) => {
49
+ fp.mapTypes = this.mapTypes;
50
+ });
51
+
52
+ this._attributeProviders = options.attributeProviders
53
+ .map(getProviderForOption)
54
+ .filter((ap) => ap instanceof AbstractAttributeProvider);
55
+ }
56
+
57
+ async augmentFeatures(
58
+ features: EventFeature[],
59
+ extent?: Extent,
60
+ ): Promise<void> {
61
+ for (const provider of this._attributeProviders) {
62
+ // order is relevant. second provider can overwrite or use attributes of first provider.
63
+ // eslint-disable-next-line no-await-in-loop
64
+ await provider.augmentFeatures(features, extent);
65
+ }
66
+ }
67
+
68
+ async getFeaturesByCoordinate(
69
+ coordinate: [number, number],
70
+ resolution: number,
71
+ layer: Layer,
72
+ ): Promise<Feature[]> {
73
+ const featureArray = await Promise.all(
74
+ this._featureProviders.map((fp) =>
75
+ fp.getFeaturesByCoordinate(coordinate, resolution, layer),
76
+ ),
77
+ );
78
+
79
+ return featureArray.flat();
80
+ }
81
+
82
+ async augmentFeature(feature: EventFeature): Promise<void> {
83
+ for (const provider of this._attributeProviders) {
84
+ // order is relevant. second provider can overwrite or use attributes of first provider.
85
+ // eslint-disable-next-line no-await-in-loop
86
+ await provider.augmentFeature(feature);
87
+ }
88
+ }
89
+
90
+ toJSON(
91
+ defaultOptions: AbstractFeatureProviderOptions = AbstractFeatureProvider.getDefaultOptions(),
92
+ ): CompositeFeatureProviderOptions {
93
+ const config: Partial<CompositeFeatureProviderOptions> = super.toJSON(
94
+ defaultOptions,
95
+ );
96
+
97
+ config.featureProviders = this._featureProviders.map((fp) => fp.toJSON());
98
+ config.attributeProviders = this._attributeProviders.map((ap) =>
99
+ ap.toJSON(),
100
+ );
101
+ return config as CompositeFeatureProviderOptions;
102
+ }
103
+ }
@@ -0,0 +1,186 @@
1
+ import AbstractAttributeProvider, {
2
+ type AbstractAttributeProviderOptions,
3
+ } from './abstractAttributeProvider.js';
4
+ import { featureProviderClassRegistry } from '../classRegistry.js';
5
+ import { getInitForUrl } from '../util/fetch.js';
6
+
7
+ export type CsvAttributeProviderOptions = AbstractAttributeProviderOptions & {
8
+ /**
9
+ * CSV data as a string or URL to fetch the CSV data from
10
+ */
11
+ data: string;
12
+ /**
13
+ * Delimiter used in the CSV data (default: ",")
14
+ */
15
+ delimiter?: string;
16
+ /**
17
+ * Column name to use as the feature ID (default: "id")
18
+ */
19
+ idColumn?: string;
20
+ /**
21
+ * Optional array of headers, if the data does not contain a header row. If not provided, the first row of the CSV data will be used as headers.
22
+ */
23
+ headers?: string[] | null;
24
+ httpHeaders?: Record<string, string>;
25
+ };
26
+
27
+ export default class CsvAttributeProvider extends AbstractAttributeProvider {
28
+ static get className(): string {
29
+ return 'CsvAttributeProvider';
30
+ }
31
+
32
+ static getDefaultOptions(): Required<CsvAttributeProviderOptions> {
33
+ return {
34
+ name: '',
35
+ keyProperty: '',
36
+ properties: {},
37
+ type: CsvAttributeProvider.className,
38
+ data: '',
39
+ delimiter: ',',
40
+ idColumn: 'id',
41
+ headers: null,
42
+ httpHeaders: {},
43
+ };
44
+ }
45
+
46
+ private _urlOrData: string;
47
+
48
+ private _data?: Map<string, Record<string, unknown>>;
49
+
50
+ private _delimiter: string;
51
+
52
+ private _idColumn: string;
53
+
54
+ private _headers?: string[];
55
+
56
+ private _httpHeaders?: Record<string, string>;
57
+
58
+ private _dataLoadingPromise?: Promise<void>;
59
+
60
+ constructor(options: CsvAttributeProviderOptions) {
61
+ const defaultOptions = CsvAttributeProvider.getDefaultOptions();
62
+ super({ ...defaultOptions, ...options });
63
+ this._urlOrData = options.data;
64
+ this._delimiter = options.delimiter ?? defaultOptions.delimiter;
65
+ this._idColumn = options.idColumn ?? defaultOptions.idColumn;
66
+
67
+ if (Array.isArray(options.headers)) {
68
+ this._headers = options.headers.slice();
69
+ }
70
+ if (options.httpHeaders) {
71
+ this._httpHeaders = structuredClone(options.httpHeaders);
72
+ }
73
+ }
74
+
75
+ private _readLine(line: string): string[] {
76
+ return line.split(this._delimiter).map((v) => v.trim());
77
+ }
78
+
79
+ private async _loadData(): Promise<void> {
80
+ if (this._data) {
81
+ return;
82
+ }
83
+
84
+ let csvData: string;
85
+ // Check if URL contains newline - if so, it's the data itself
86
+ if (this._urlOrData.includes('\n')) {
87
+ csvData = this._urlOrData;
88
+ } else {
89
+ const init = getInitForUrl(this._urlOrData, this._httpHeaders);
90
+ const response = await fetch(this._urlOrData, init);
91
+ if (!response.ok) {
92
+ throw new Error(`Failed to fetch CSV data from ${this._urlOrData}`);
93
+ }
94
+ csvData = await response.text();
95
+ }
96
+
97
+ // Split by both Windows (\r\n) and Unix (\n) line endings
98
+ const lines = csvData.split(/\r?\n/).filter((line) => line.trim() !== '');
99
+ if (lines.length === 0) {
100
+ this.getLogger().warning('No CSV data available to load.');
101
+ this._data = new Map<string, Record<string, unknown>>();
102
+ return;
103
+ }
104
+
105
+ const dataMap = new Map<string, Record<string, unknown>>();
106
+
107
+ let startIndex = 0;
108
+
109
+ if (!this._headers) {
110
+ this._headers = this._readLine(lines[0]);
111
+ startIndex = 1;
112
+ }
113
+
114
+ for (let i = startIndex; i < lines.length; i++) {
115
+ const values = this._readLine(lines[i]);
116
+
117
+ const attributes: Record<string, unknown> = {};
118
+
119
+ for (let j = 0; j < values.length; j++) {
120
+ const key = this._headers[j] ?? `column_${j}`;
121
+ attributes[key] = values[j];
122
+ }
123
+
124
+ const id = attributes[this._idColumn];
125
+ if (typeof id === 'string') {
126
+ dataMap.set(id, attributes);
127
+ }
128
+ }
129
+
130
+ this._data = dataMap;
131
+ }
132
+
133
+ private _ensureDataLoaded(): Promise<void> {
134
+ if (!this._dataLoadingPromise) {
135
+ this._dataLoadingPromise = this._loadData();
136
+ }
137
+ return this._dataLoadingPromise;
138
+ }
139
+
140
+ protected async _getAttributes(
141
+ key: string,
142
+ ): Promise<Record<string, unknown> | undefined> {
143
+ await this._ensureDataLoaded();
144
+ return this._data?.get(key);
145
+ }
146
+
147
+ protected async _getBulkAttributes(
148
+ bulk: {
149
+ key: string;
150
+ }[],
151
+ ): Promise<(Record<string, unknown> | undefined)[]> {
152
+ await this._ensureDataLoaded();
153
+ return bulk.map(({ key }) => this._data?.get(key));
154
+ }
155
+
156
+ toJSON(
157
+ defaultOptions = CsvAttributeProvider.getDefaultOptions(),
158
+ ): CsvAttributeProviderOptions {
159
+ const config = super.toJSON(defaultOptions) as CsvAttributeProviderOptions;
160
+ config.data = this._urlOrData;
161
+ if (this._delimiter !== defaultOptions.delimiter) {
162
+ config.delimiter = this._delimiter;
163
+ }
164
+ if (this._idColumn !== defaultOptions.idColumn) {
165
+ config.idColumn = this._idColumn;
166
+ }
167
+ if (this._headers && this._headers.length > 0) {
168
+ config.headers = this._headers.slice();
169
+ }
170
+ if (this._httpHeaders) {
171
+ config.httpHeaders = structuredClone(this._httpHeaders);
172
+ }
173
+ return config;
174
+ }
175
+
176
+ destroy(): void {
177
+ this._data?.clear();
178
+ this._data = undefined;
179
+ super.destroy();
180
+ }
181
+ }
182
+
183
+ featureProviderClassRegistry.registerClass(
184
+ CsvAttributeProvider.className,
185
+ CsvAttributeProvider,
186
+ );
@@ -0,0 +1,31 @@
1
+ import AbstractFeatureProvider, {
2
+ type AbstractFeatureProviderOptions,
3
+ } from './abstractFeatureProvider.js';
4
+ import AbstractAttributeProvider, {
5
+ type AbstractAttributeProviderOptions,
6
+ } from './abstractAttributeProvider.js';
7
+ import { featureProviderClassRegistry } from '../classRegistry.js';
8
+
9
+ export function getProviderForOption(
10
+ options?:
11
+ | AbstractFeatureProviderOptions
12
+ | AbstractAttributeProviderOptions
13
+ | AbstractAttributeProvider
14
+ | AbstractFeatureProvider,
15
+ ): AbstractFeatureProvider | AbstractAttributeProvider | undefined {
16
+ if (
17
+ options instanceof AbstractFeatureProvider ||
18
+ options instanceof AbstractAttributeProvider
19
+ ) {
20
+ return options;
21
+ }
22
+
23
+ if (options?.type) {
24
+ const CTOR = featureProviderClassRegistry.getClass(options.type);
25
+ if (CTOR) {
26
+ return new CTOR(options);
27
+ }
28
+ }
29
+
30
+ return undefined;
31
+ }
@@ -0,0 +1,60 @@
1
+ import { getLogger } from '@vcsuite/logger';
2
+ import type { I3SNode } from '@vcmap-cesium/engine';
3
+ import { Cesium3DTileFeature } from '@vcmap-cesium/engine';
4
+ import Feature from 'ol/Feature.js';
5
+ import { isI3SFeature } from '../interaction/featureAtPixelInteraction.js';
6
+ import AbstractAttributeProvider from './abstractAttributeProvider.js';
7
+ import { isProvidedFeature } from './featureProviderSymbols.js';
8
+ import { i3sData } from '../layer/layerSymbols.js';
9
+
10
+ export default class I3SAttributeProvider extends AbstractAttributeProvider {
11
+ static get className(): string {
12
+ return 'I3SAttributeProvider';
13
+ }
14
+
15
+ protected async _getAttributes(
16
+ _key: string,
17
+ feature: Cesium3DTileFeature | Feature,
18
+ ): Promise<Record<string, unknown> | undefined> {
19
+ if (feature instanceof Cesium3DTileFeature) {
20
+ const id = feature.featureId;
21
+
22
+ if (id && feature.content?.tile?.i3sNode) {
23
+ const node = feature.content.tile.i3sNode as unknown as I3SNode;
24
+ return node
25
+ .loadFields()
26
+ .then(() => node.getFieldsForFeature(id) as Record<string, unknown>)
27
+ .catch(() => {
28
+ getLogger(this.className).warning(
29
+ `Error getting I3S fields for feature with id ${id}`,
30
+ );
31
+ return undefined;
32
+ });
33
+ }
34
+ } else if (
35
+ feature instanceof Feature &&
36
+ feature[isProvidedFeature] &&
37
+ isI3SFeature(feature)
38
+ ) {
39
+ const { i3sNode, cartesianPosition } = feature[i3sData];
40
+ if (i3sNode && cartesianPosition) {
41
+ return i3sNode
42
+ .loadFields()
43
+ .then(
44
+ () =>
45
+ i3sNode.getFieldsForPickedPosition(cartesianPosition) as Record<
46
+ string,
47
+ unknown
48
+ >,
49
+ )
50
+ .catch(() => {
51
+ getLogger(this.className).warning(
52
+ 'Error getting I3S fields for picked position',
53
+ );
54
+ return undefined;
55
+ });
56
+ }
57
+ }
58
+ return undefined;
59
+ }
60
+ }
@@ -0,0 +1,109 @@
1
+ import { is, oneOf } from '@vcsuite/check';
2
+ import AbstractAttributeProvider, {
3
+ type AbstractAttributeProviderOptions,
4
+ } from './abstractAttributeProvider.js';
5
+ import { getInitForUrl, requestJson } from '../util/fetch.js';
6
+
7
+ export type JsonAttributeData = {
8
+ features: { id: string | number; properties: Record<string, unknown> }[];
9
+ };
10
+
11
+ export type JsonAttributeProviderOptions = AbstractAttributeProviderOptions & {
12
+ data: string | JsonAttributeData;
13
+ headers?: Record<string, string>;
14
+ };
15
+
16
+ export default class JsonAttributeProvider extends AbstractAttributeProvider {
17
+ static get className(): string {
18
+ return 'JsonAttributeProvider';
19
+ }
20
+
21
+ static getDefaultOptions(): JsonAttributeProviderOptions {
22
+ return {
23
+ ...AbstractAttributeProvider.getDefaultOptions(),
24
+ data: '',
25
+ };
26
+ }
27
+
28
+ private _data?: Map<string, Record<string, unknown>>;
29
+
30
+ private readonly _dataOrUrl: string | JsonAttributeData;
31
+
32
+ private readonly _headers?: Record<string, string>;
33
+
34
+ constructor(options: JsonAttributeProviderOptions) {
35
+ super(options);
36
+
37
+ this._dataOrUrl = is(options.data, Object)
38
+ ? structuredClone(options.data)
39
+ : options.data;
40
+ this._headers = options.headers;
41
+ }
42
+
43
+ private async _loadData(): Promise<void> {
44
+ if (this._data) {
45
+ return;
46
+ }
47
+
48
+ let serializedData;
49
+ if (is(this._dataOrUrl, String)) {
50
+ const init = getInitForUrl(this._dataOrUrl, this._headers);
51
+ serializedData = await requestJson<JsonAttributeData>(
52
+ this._dataOrUrl,
53
+ init,
54
+ );
55
+ } else {
56
+ serializedData = this._dataOrUrl;
57
+ }
58
+
59
+ const data = new Map<string, Record<string, unknown>>();
60
+ if (Array.isArray(serializedData.features)) {
61
+ serializedData?.features?.forEach(({ id, properties }) => {
62
+ if (is(id, oneOf(String, Number)) && is(properties, Object)) {
63
+ data.set(String(id), properties);
64
+ }
65
+ });
66
+ }
67
+
68
+ this._data = data;
69
+ }
70
+
71
+ protected async _getAttributes(
72
+ key: string,
73
+ ): Promise<Record<string, unknown> | undefined> {
74
+ await this._loadData();
75
+ return this._data?.get(key);
76
+ }
77
+
78
+ protected async _getBulkAttributes(
79
+ bulk: {
80
+ key: string;
81
+ }[],
82
+ ): Promise<(Record<string, unknown> | undefined)[]> {
83
+ await this._loadData();
84
+ return bulk.map(({ key }) => this._data?.get(key));
85
+ }
86
+
87
+ toJSON(
88
+ defaultOptions = JsonAttributeProvider.getDefaultOptions(),
89
+ ): JsonAttributeProviderOptions {
90
+ const config = super.toJSON(defaultOptions) as JsonAttributeProviderOptions;
91
+ if (is(this._dataOrUrl, Object)) {
92
+ config.data = structuredClone(this._dataOrUrl);
93
+ } else {
94
+ config.data = this._dataOrUrl;
95
+ }
96
+
97
+ if (this._headers) {
98
+ config.headers = structuredClone(this._headers);
99
+ }
100
+
101
+ return config;
102
+ }
103
+
104
+ destroy(): void {
105
+ this._data?.clear();
106
+ this._data = undefined;
107
+ super.destroy();
108
+ }
109
+ }