@open-pioneer/ogc-features 1.3.0-dev.20260225133423 → 1.3.0-dev.20260310142705

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 (45) hide show
  1. package/CHANGELOG.md +1 -36
  2. package/_virtual/source-info.js +2 -2
  3. package/index.d.ts +1 -1
  4. package/package.json +1 -1
  5. package/{OgcFeatureSearchSource.d.ts → search-source/OgcFeatureSearchSource.d.ts} +2 -2
  6. package/{OgcFeatureSearchSource.js → search-source/OgcFeatureSearchSource.js} +4 -2
  7. package/search-source/OgcFeatureSearchSource.js.map +1 -0
  8. package/search-source/OgcFeatureSearchSourceFactory.d.ts +13 -0
  9. package/search-source/OgcFeatureSearchSourceFactory.js +14 -0
  10. package/search-source/OgcFeatureSearchSourceFactory.js.map +1 -0
  11. package/services.d.ts +4 -21
  12. package/services.js +2 -23
  13. package/services.js.map +1 -1
  14. package/{Metadata.d.ts → vector-source/Metadata.d.ts} +8 -10
  15. package/vector-source/Metadata.js +28 -0
  16. package/vector-source/Metadata.js.map +1 -0
  17. package/vector-source/NextStrategy.d.ts +24 -0
  18. package/vector-source/NextStrategy.js +33 -0
  19. package/vector-source/NextStrategy.js.map +1 -0
  20. package/vector-source/OffsetStrategy.d.ts +29 -0
  21. package/vector-source/OffsetStrategy.js +111 -0
  22. package/vector-source/OffsetStrategy.js.map +1 -0
  23. package/vector-source/OgcFeaturesVectorSource.d.ts +7 -0
  24. package/vector-source/OgcFeaturesVectorSource.js +180 -0
  25. package/vector-source/OgcFeaturesVectorSource.js.map +1 -0
  26. package/vector-source/OgcFeaturesVectorSourceFactory.d.ts +15 -0
  27. package/vector-source/OgcFeaturesVectorSourceFactory.js +14 -0
  28. package/vector-source/OgcFeaturesVectorSourceFactory.js.map +1 -0
  29. package/vector-source/requestUtils.d.ts +21 -0
  30. package/vector-source/requestUtils.js +41 -0
  31. package/vector-source/requestUtils.js.map +1 -0
  32. package/Metadata.js +0 -47
  33. package/Metadata.js.map +0 -1
  34. package/OffsetStrategy.d.ts +0 -11
  35. package/OffsetStrategy.js +0 -68
  36. package/OffsetStrategy.js.map +0 -1
  37. package/OgcFeatureSearchSource.js.map +0 -1
  38. package/api.js +0 -2
  39. package/api.js.map +0 -1
  40. package/createVectorSource.d.ts +0 -69
  41. package/createVectorSource.js +0 -176
  42. package/createVectorSource.js.map +0 -1
  43. package/requestUtils.d.ts +0 -25
  44. package/requestUtils.js +0 -52
  45. package/requestUtils.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @open-pioneer/ogc-features
2
2
 
3
- ## 1.3.0-dev.20260225133423
3
+ ## 1.3.0-dev.20260310142705
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -98,17 +98,12 @@
98
98
  ### Patch Changes
99
99
 
100
100
  - b152428: Update trails dependencies
101
- - Updated dependencies [b152428]
102
- - @open-pioneer/search@0.4.4
103
101
 
104
102
  ## 0.4.3
105
103
 
106
104
  ### Patch Changes
107
105
 
108
106
  - 28e092a: Update dependencies
109
- - Updated dependencies [7f5e58a]
110
- - Updated dependencies [28e092a]
111
- - @open-pioneer/search@0.4.3
112
107
 
113
108
  ## 0.4.2
114
109
 
@@ -119,11 +114,6 @@
119
114
  - 6977f0b: Added `strategy` and `rewriteUrl` options to the vector source factory.
120
115
  - 81bc7da: Update trails dependencies
121
116
  - 2c092dc: Update dependencies
122
- - Updated dependencies [4140646]
123
- - Updated dependencies [4140646]
124
- - Updated dependencies [81bc7da]
125
- - Updated dependencies [2c092dc]
126
- - @open-pioneer/search@0.4.2
127
117
 
128
118
  ## 0.4.1
129
119
 
@@ -141,51 +131,26 @@
141
131
 
142
132
  - 1a8ad89: Update package.json metadata
143
133
  - 6162979: Update versions of core packages
144
- - Updated dependencies [1a8ad89]
145
- - Updated dependencies [a0d8882]
146
- - Updated dependencies [6162979]
147
- - Updated dependencies [9334e81]
148
- - @open-pioneer/search@0.4.0
149
134
 
150
135
  ## 0.3.1
151
136
 
152
- ### Patch Changes
153
-
154
- - Updated dependencies [611ddb9]
155
- - @open-pioneer/search@0.3.1
156
-
157
137
  ## 0.3.0
158
138
 
159
139
  ### Minor Changes
160
140
 
161
141
  - ee7c2d4: Update runtime version.
162
142
 
163
- ### Patch Changes
164
-
165
- - Updated dependencies [0883bbd]
166
- - Updated dependencies [ee7c2d4]
167
- - Updated dependencies [0456500]
168
- - @open-pioneer/search@0.3.0
169
-
170
143
  ## 0.2.0
171
144
 
172
145
  ### Minor Changes
173
146
 
174
147
  - 70349a8: Update to new core packages major versions
175
148
 
176
- ### Patch Changes
177
-
178
- - Updated dependencies [70349a8]
179
- - @open-pioneer/search@0.2.0
180
-
181
149
  ## 0.1.1
182
150
 
183
151
  ### Patch Changes
184
152
 
185
153
  - dfc0896: Add OGC API Features search source
186
- - Updated dependencies [6209d6c]
187
- - Updated dependencies [565bd8b]
188
- - @open-pioneer/search@0.1.0
189
154
 
190
155
  ## 0.1.0
191
156
 
@@ -1,6 +1,6 @@
1
- const sourceId$1 = "@open-pioneer/ogc-features/createVectorSource";
1
+ const sourceId$1 = "@open-pioneer/ogc-features/vector-source/OgcFeaturesVectorSource";
2
2
 
3
- const sourceId = "@open-pioneer/ogc-features/Metadata";
3
+ const sourceId = "@open-pioneer/ogc-features/vector-source/OffsetStrategy";
4
4
 
5
5
  export { sourceId, sourceId$1 };
6
6
  //# sourceMappingURL=source-info.js.map
package/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * from "./api";
1
+ export { type FeatureResponse, type OgcFeatureSearchSourceOptions, type OgcFeaturesSearchSourceFactory, type OgcFeaturesVectorSourceFactory, type OgcFeatureVectorSourceOptions, type OgcFetchStrategy } from "./api";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@open-pioneer/ogc-features",
4
- "version": "1.3.0-dev.20260225133423",
4
+ "version": "1.3.0-dev.20260310142705",
5
5
  "description": "This package provides utilities to work with OGC API Features services.",
6
6
  "keywords": [
7
7
  "open-pioneer-trails"
@@ -1,6 +1,6 @@
1
- import { SearchSource, SearchResult, SearchOptions } from "@open-pioneer/search";
2
- import { OgcFeatureSearchSourceOptions } from "./api";
3
1
  import { HttpService } from "@open-pioneer/http";
2
+ import { SearchOptions, SearchResult, SearchSource } from "@open-pioneer/search";
3
+ import { OgcFeatureSearchSourceOptions } from "../api";
4
4
  /** The general shape of features returned by an OGC API Features service. */
5
5
  export interface FeatureResponse {
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import { isAbortError } from '@open-pioneer/core';
2
- import { v4 } from 'uuid';
3
2
  import GeoJSON from 'ol/format/GeoJSON.js';
3
+ import { v4 } from 'uuid';
4
4
 
5
5
  class OgcFeatureSearchSource {
6
6
  label;
@@ -48,7 +48,9 @@ class OgcFeatureSearchSource {
48
48
  };
49
49
  }
50
50
  #getUrl(inputValue, limit) {
51
- const url = new URL(`${this.#baseUrl}/collections/${this.#options.collectionId}/items`);
51
+ const url = new URL(
52
+ `${this.#baseUrl.replace(/\/+$/, "")}/collections/${this.#options.collectionId}/items`
53
+ );
52
54
  for (const [k, v] of this.#params) {
53
55
  url.searchParams.append(k, v);
54
56
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OgcFeatureSearchSource.js","sources":["OgcFeatureSearchSource.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { isAbortError } from \"@open-pioneer/core\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport { SearchOptions, SearchResult, SearchSource } from \"@open-pioneer/search\";\nimport GeoJSON from \"ol/format/GeoJSON\";\nimport { v4 as uuid4v } from \"uuid\";\nimport { OgcFeatureSearchSourceOptions } from \"../api\";\n\n/** The general shape of features returned by an OGC API Features service. */\nexport interface FeatureResponse {\n /**\n * The type of the feature (e.g. `Feature`).\n */\n type: string;\n\n /**\n * The id of the feature.\n */\n id: string | number;\n\n /**\n * The geometry of the feature.\n */\n geometry: unknown;\n\n /**\n * The properties of the feature.\n */\n properties: Readonly<Record<string, unknown>>;\n}\n\n/**\n * An implementation of {@link SearchSource} that searches on an OGC Features API service.\n */\nexport class OgcFeatureSearchSource implements SearchSource {\n readonly label: string;\n #options: OgcFeatureSearchSourceOptions;\n #httpService: HttpService;\n #baseUrl: string;\n #params: URLSearchParams;\n\n constructor(options: OgcFeatureSearchSourceOptions, httpService: HttpService) {\n this.label = options.label;\n this.#options = options;\n this.#httpService = httpService;\n\n const { baseUrl, params } = getBaseUrl(options.baseUrl);\n this.#baseUrl = baseUrl;\n this.#params = params;\n }\n\n async search(\n inputValue: string,\n { mapProjection, maxResults, signal }: SearchOptions\n ): Promise<SearchResult[]> {\n const url = this.#getUrl(inputValue, maxResults);\n const geojson = new GeoJSON({\n dataProjection: \"EPSG:4326\",\n featureProjection: mapProjection\n });\n\n const responses = await fetchJson(this.#httpService, url, signal);\n return responses.features.map((feature) => this.#createResult(feature, geojson));\n }\n\n #createResult(feature: FeatureResponse, geojson: GeoJSON): SearchResult {\n const customLabel = this.#options.renderLabel?.(feature);\n\n const singleLabelProperty =\n feature.properties[this.#options.labelProperty as keyof typeof feature.properties];\n\n const singleSearchProperty =\n feature.properties[this.#options.searchProperty as keyof typeof feature.properties];\n\n const label = (() => {\n if (customLabel) {\n return customLabel;\n } else if (singleLabelProperty !== undefined) {\n return String(singleLabelProperty);\n } else if (singleSearchProperty !== undefined) {\n return String(singleSearchProperty);\n } else {\n return \"\";\n }\n })();\n\n return {\n id: feature.id ?? uuid4v(),\n label: label,\n geometry: geojson.readGeometry(feature.geometry),\n properties: feature.properties\n };\n }\n\n #getUrl(inputValue: string, limit: number): URL {\n const url = new URL(\n `${this.#baseUrl.replace(/\\/+$/, \"\")}/collections/${this.#options.collectionId}/items`\n );\n\n for (const [k, v] of this.#params) {\n url.searchParams.append(k, v);\n }\n url.searchParams.set(this.#options.searchProperty, `*${inputValue}*`);\n url.searchParams.set(\"limit\", String(limit));\n url.searchParams.set(\"f\", \"json\");\n\n // Passing a copy of the original URL to prevent accidental modifications.\n // Users should return a new URL instead.\n return this.#options.rewriteUrl?.(new URL(url)) ?? url;\n }\n}\n\n// Exported for test\nexport interface SearchResponse {\n features: FeatureResponse[];\n links?: Record<string, unknown>[];\n numberMatched?: number;\n numberReturned?: number;\n timeStamp?: string;\n type?: string;\n}\n\nasync function fetchJson(\n httpService: HttpService,\n url: URL,\n signal?: AbortSignal | undefined\n): Promise<SearchResponse> {\n try {\n const response = await httpService.fetch(url, {\n signal,\n headers: {\n Accept: \"application/json\"\n }\n });\n if (!response.ok) {\n throw new Error(\"Request failed with status \" + response.status);\n }\n\n const result = await response.json();\n return result;\n } catch (error) {\n if (isAbortError(error)) {\n throw error;\n }\n throw new Error(\"Failed to search on OGC API Features service\", { cause: error });\n }\n}\n\n/**\n * Splits the base url into a \"clean\" base URL (no query params) and the original query params.\n */\nfunction getBaseUrl(baseUrl: string) {\n const url = new URL(baseUrl);\n const params = new URLSearchParams(url.searchParams);\n url.search = \"\";\n\n const cleanBaseUrl = url.href.replace(/\\/+$/, \"\"); // prevent double slash\n return { baseUrl: cleanBaseUrl, params };\n}\n"],"names":["uuid4v"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { HttpService } from "@open-pioneer/http";
2
+ import { ServiceOptions } from "@open-pioneer/runtime";
3
+ import { SearchSource } from "@open-pioneer/search";
4
+ import { OgcFeatureSearchSourceOptions, OgcFeaturesSearchSourceFactory as ServiceInterface } from "../api";
5
+ interface References {
6
+ httpService: HttpService;
7
+ }
8
+ export declare class OgcFeatureSearchSourceFactory implements ServiceInterface {
9
+ #private;
10
+ constructor({ references }: ServiceOptions<References>);
11
+ createSearchSource(options: OgcFeatureSearchSourceOptions): SearchSource;
12
+ }
13
+ export {};
@@ -0,0 +1,14 @@
1
+ import { OgcFeatureSearchSource } from './OgcFeatureSearchSource.js';
2
+
3
+ class OgcFeatureSearchSourceFactory {
4
+ #httpService;
5
+ constructor({ references }) {
6
+ this.#httpService = references.httpService;
7
+ }
8
+ createSearchSource(options) {
9
+ return new OgcFeatureSearchSource(options, this.#httpService);
10
+ }
11
+ }
12
+
13
+ export { OgcFeatureSearchSourceFactory };
14
+ //# sourceMappingURL=OgcFeatureSearchSourceFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OgcFeatureSearchSourceFactory.js","sources":["OgcFeatureSearchSourceFactory.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 { ServiceOptions } from \"@open-pioneer/runtime\";\nimport { SearchSource } from \"@open-pioneer/search\";\nimport {\n OgcFeatureSearchSourceOptions,\n OgcFeaturesSearchSourceFactory as ServiceInterface\n} from \"../api\";\nimport { OgcFeatureSearchSource } from \"./OgcFeatureSearchSource\";\n\ninterface References {\n httpService: HttpService;\n}\n\nexport class OgcFeatureSearchSourceFactory implements ServiceInterface {\n #httpService: HttpService;\n\n constructor({ references }: ServiceOptions<References>) {\n this.#httpService = references.httpService;\n }\n\n createSearchSource(options: OgcFeatureSearchSourceOptions): SearchSource {\n return new OgcFeatureSearchSource(options, this.#httpService);\n }\n}\n"],"names":[],"mappings":";;AAeO,MAAM,6BAAA,CAA0D;AAAA,EACnE,YAAA;AAAA,EAEA,WAAA,CAAY,EAAE,UAAA,EAAW,EAA+B;AACpD,IAAA,IAAA,CAAK,eAAe,UAAA,CAAW,WAAA;AAAA,EACnC;AAAA,EAEA,mBAAmB,OAAA,EAAsD;AACrE,IAAA,OAAO,IAAI,sBAAA,CAAuB,OAAA,EAAS,IAAA,CAAK,YAAY,CAAA;AAAA,EAChE;AACJ;;;;"}
package/services.d.ts CHANGED
@@ -1,21 +1,4 @@
1
- import { Feature } from "ol";
2
- import { Geometry } from "ol/geom";
3
- import VectorSource from "ol/source/Vector";
4
- import { OgcFeatureSearchSourceOptions, OgcFeatureVectorSourceOptions, OgcFeaturesSearchSourceFactory, OgcFeaturesVectorSourceFactory } from "./api";
5
- import { SearchSource } from "@open-pioneer/search";
6
- import { HttpService } from "@open-pioneer/http";
7
- import { ServiceOptions } from "@open-pioneer/runtime";
8
- interface References {
9
- httpService: HttpService;
10
- }
11
- export declare class VectorSourceFactory implements OgcFeaturesVectorSourceFactory {
12
- #private;
13
- constructor({ references }: ServiceOptions<References>);
14
- createVectorSource(options: OgcFeatureVectorSourceOptions): VectorSource<Feature<Geometry>>;
15
- }
16
- export declare class SearchSourceFactory implements OgcFeaturesSearchSourceFactory {
17
- #private;
18
- constructor({ references }: ServiceOptions<References>);
19
- createSearchSource(options: OgcFeatureSearchSourceOptions): SearchSource;
20
- }
21
- export {};
1
+ import { OgcFeaturesVectorSourceFactory } from "./vector-source/OgcFeaturesVectorSourceFactory";
2
+ import { OgcFeatureSearchSourceFactory } from "./search-source/OgcFeatureSearchSourceFactory";
3
+ export { OgcFeaturesVectorSourceFactory as VectorSourceFactory };
4
+ export { OgcFeatureSearchSourceFactory as SearchSourceFactory };
package/services.js CHANGED
@@ -1,24 +1,3 @@
1
- import { createVectorSource } from './createVectorSource.js';
2
- import { OgcFeatureSearchSource } from './OgcFeatureSearchSource.js';
3
-
4
- class VectorSourceFactory {
5
- #httpService;
6
- constructor({ references }) {
7
- this.#httpService = references.httpService;
8
- }
9
- createVectorSource(options) {
10
- return createVectorSource(options, this.#httpService);
11
- }
12
- }
13
- class SearchSourceFactory {
14
- #httpService;
15
- constructor({ references }) {
16
- this.#httpService = references.httpService;
17
- }
18
- createSearchSource(options) {
19
- return new OgcFeatureSearchSource(options, this.#httpService);
20
- }
21
- }
22
-
23
- export { SearchSourceFactory, VectorSourceFactory };
1
+ export { OgcFeaturesVectorSourceFactory as VectorSourceFactory } from './vector-source/OgcFeaturesVectorSourceFactory.js';
2
+ export { OgcFeatureSearchSourceFactory as SearchSourceFactory } from './search-source/OgcFeatureSearchSourceFactory.js';
24
3
  //# sourceMappingURL=services.js.map
package/services.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"services.js","sources":["services.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { Feature } from \"ol\";\nimport { Geometry } from \"ol/geom\";\nimport VectorSource from \"ol/source/Vector\";\nimport {\n OgcFeatureSearchSourceOptions,\n OgcFeatureVectorSourceOptions,\n OgcFeaturesSearchSourceFactory,\n OgcFeaturesVectorSourceFactory\n} from \"./api\";\nimport { SearchSource } from \"@open-pioneer/search\";\nimport { createVectorSource } from \"./createVectorSource\";\nimport { OgcFeatureSearchSource } from \"./OgcFeatureSearchSource\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport { ServiceOptions } from \"@open-pioneer/runtime\";\n\n// Both services share the same dependencies for now\ninterface References {\n httpService: HttpService;\n}\n\nexport class VectorSourceFactory implements OgcFeaturesVectorSourceFactory {\n #httpService: HttpService;\n\n constructor({ references }: ServiceOptions<References>) {\n this.#httpService = references.httpService;\n }\n\n createVectorSource(options: OgcFeatureVectorSourceOptions): VectorSource<Feature<Geometry>> {\n return createVectorSource(options, this.#httpService);\n }\n}\n\nexport class SearchSourceFactory implements OgcFeaturesSearchSourceFactory {\n #httpService: HttpService;\n\n constructor({ references }: ServiceOptions<References>) {\n this.#httpService = references.httpService;\n }\n\n createSearchSource(options: OgcFeatureSearchSourceOptions): SearchSource {\n return new OgcFeatureSearchSource(options, this.#httpService);\n }\n}\n"],"names":[],"mappings":";;;AAsBO,MAAM,mBAAA,CAA8D;AAAA,EACvE,YAAA;AAAA,EAEA,WAAA,CAAY,EAAE,UAAA,EAAW,EAA+B;AACpD,IAAA,IAAA,CAAK,eAAe,UAAA,CAAW,WAAA;AAAA,EACnC;AAAA,EAEA,mBAAmB,OAAA,EAAyE;AACxF,IAAA,OAAO,kBAAA,CAAmB,OAAA,EAAS,IAAA,CAAK,YAAY,CAAA;AAAA,EACxD;AACJ;AAEO,MAAM,mBAAA,CAA8D;AAAA,EACvE,YAAA;AAAA,EAEA,WAAA,CAAY,EAAE,UAAA,EAAW,EAA+B;AACpD,IAAA,IAAA,CAAK,eAAe,UAAA,CAAW,WAAA;AAAA,EACnC;AAAA,EAEA,mBAAmB,OAAA,EAAsD;AACrE,IAAA,OAAO,IAAI,sBAAA,CAAuB,OAAA,EAAS,IAAA,CAAK,YAAY,CAAA;AAAA,EAChE;AACJ;;;;"}
1
+ {"version":3,"file":"services.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,4 +1,11 @@
1
1
  import { HttpService } from "@open-pioneer/http";
2
+ /**
3
+ * @module
4
+ *
5
+ * Provides metadata-related utilities for working with OGC API Features services, such as fetching collection metadata.
6
+ *
7
+ *
8
+ */
2
9
  /**
3
10
  * Requests metadata for an OGC API Features service collection.
4
11
  *
@@ -7,7 +14,7 @@ import { HttpService } from "@open-pioneer/http";
7
14
  * @param httpService Instance to perform the HTTP request.
8
15
  * @returns
9
16
  */
10
- export declare function getCollectionMetadata(baseUrl: string, collectionId: string, httpService: HttpService): Promise<CollectionMetadata>;
17
+ export declare function getCollectionMetadata(collectionUrl: string, httpService: HttpService): Promise<CollectionMetadata>;
11
18
  /**
12
19
  * Metadata of a collection retrieved from the OGC API Features service as provided by the `/collections/{collectionId}` endpoint.
13
20
  */
@@ -16,15 +23,6 @@ export interface CollectionMetadata {
16
23
  crs: string[] | undefined;
17
24
  attribution?: string;
18
25
  }
19
- /**
20
- * Determines the appropriate coordinate reference system (CRS) identifier to use for requests to the OGC API Features service, based on the map's current CRS, the collection's supported CRSs, and any explicitly configured CRS in the options.
21
- *
22
- * @param mapCrs the CRS used by the map that is requesting to load the features.
23
- * @param supportedCrses list of supported CRS identifiers.
24
- * @param configuredCrs an explicitly configured CRS identifier to use for requests, if any.
25
- * @returns the CRS to use for feature requests based on the given information.
26
- */
27
- export declare function getRequestCrs(mapCrs: string, supportedCrses: string[] | undefined, configuredCrs: string | undefined): string;
28
26
  /**
29
27
  * Checks if a given coordinate reference system (CRS) identifier matches any of the available CRS URIs.
30
28
  * The function especially supports matching a simple EPSG code like "EPSG:4326" with its corresponding CRS URI (e.g. "http://www.opengis.net/def/crs/EPSG/0/4326").
@@ -0,0 +1,28 @@
1
+ async function getCollectionMetadata(collectionUrl, httpService) {
2
+ const response = await httpService.fetch(collectionUrl, {
3
+ headers: {
4
+ Accept: "application/json"
5
+ }
6
+ });
7
+ if (!response.ok) {
8
+ throw new Error(
9
+ `Failed to fetch collection metadata for collection '${collectionUrl}' (status code ${response.status})`
10
+ );
11
+ }
12
+ return await response.json();
13
+ }
14
+ function findMatchingCrs(testCrs, availableCrsUris) {
15
+ if (!availableCrsUris) {
16
+ return void 0;
17
+ }
18
+ if (testCrs.startsWith("EPSG:")) {
19
+ const testCode = testCrs.split(":")[1];
20
+ const testCrsUri = `http://www.opengis.net/def/crs/EPSG/0/${testCode}`;
21
+ return availableCrsUris.find((crsUri) => crsUri === testCrsUri);
22
+ } else {
23
+ return availableCrsUris.find((crsUri) => crsUri === testCrs);
24
+ }
25
+ }
26
+
27
+ export { findMatchingCrs, getCollectionMetadata };
28
+ //# sourceMappingURL=Metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Metadata.js","sources":["Metadata.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\";\n\n/**\n * @module\n *\n * Provides metadata-related utilities for working with OGC API Features services, such as fetching collection metadata.\n *\n *\n */\n\n/**\n * Requests metadata for an OGC API Features service collection.\n *\n * @param baseUrl Base URL of the OGC API Features service (e.g. `https://example.com/ogcapi/v1`).\n * @param collectionId ID of the collection to retrieve the metadata for.\n * @param httpService Instance to perform the HTTP request.\n * @returns\n */\nexport async function getCollectionMetadata(\n collectionUrl: string,\n httpService: HttpService\n): Promise<CollectionMetadata> {\n const response = await httpService.fetch(collectionUrl, {\n headers: {\n Accept: \"application/json\"\n }\n });\n if (!response.ok) {\n throw new Error(\n `Failed to fetch collection metadata for collection '${collectionUrl}' (status code ${response.status})`\n );\n }\n // Note: Currently no validation\n return await response.json();\n}\n\n/**\n * Metadata of a collection retrieved from the OGC API Features service as provided by the `/collections/{collectionId}` endpoint.\n */\nexport interface CollectionMetadata {\n id: string;\n crs: string[] | undefined;\n attribution?: string;\n}\n\n/**\n * Checks if a given coordinate reference system (CRS) identifier matches any of the available CRS URIs.\n * The function especially supports matching a simple EPSG code like \"EPSG:4326\" with its corresponding CRS URI (e.g. \"http://www.opengis.net/def/crs/EPSG/0/4326\").\n *\n *\n * @param testCrs a CRS identifier to test, e.g. \"EPSG:4326\" or \"http://www.opengis.net/def/crs/EPSG/0/4326\"\n * @param availableCrsUris list of CRS URIs to check against, expected to be in the form of \"http://www.opengis.net/def/crs/{authority}/{version}/{code}\".\n *\n * @returns the matching CRS URI if a match is found, otherwise `undefined`.\n */\n\nexport function findMatchingCrs(\n testCrs: string,\n availableCrsUris: string[] | undefined\n): string | undefined {\n if (!availableCrsUris) {\n return undefined;\n }\n\n if (testCrs.startsWith(\"EPSG:\")) {\n const testCode = testCrs.split(\":\")[1];\n const testCrsUri = `http://www.opengis.net/def/crs/EPSG/0/${testCode}`;\n return availableCrsUris.find((crsUri) => crsUri === testCrsUri);\n } else {\n return availableCrsUris.find((crsUri) => crsUri === testCrs);\n }\n}\n"],"names":[],"mappings":"AAoBA,eAAsB,qBAAA,CAClB,eACA,WAAA,EAC2B;AAC3B,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,KAAA,CAAM,aAAA,EAAe;AAAA,IACpD,OAAA,EAAS;AAAA,MACL,MAAA,EAAQ;AAAA;AACZ,GACH,CAAA;AACD,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,oDAAA,EAAuD,aAAa,CAAA,eAAA,EAAkB,QAAA,CAAS,MAAM,CAAA,CAAA;AAAA,KACzG;AAAA,EACJ;AAEA,EAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAC/B;AAsBO,SAAS,eAAA,CACZ,SACA,gBAAA,EACkB;AAClB,EAAA,IAAI,CAAC,gBAAA,EAAkB;AACnB,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA,EAAG;AAC7B,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,KAAA,CAAM,GAAG,EAAE,CAAC,CAAA;AACrC,IAAA,MAAM,UAAA,GAAa,yCAAyC,QAAQ,CAAA,CAAA;AACpE,IAAA,OAAO,gBAAA,CAAiB,IAAA,CAAK,CAAC,MAAA,KAAW,WAAW,UAAU,CAAA;AAAA,EAClE,CAAA,MAAO;AACH,IAAA,OAAO,gBAAA,CAAiB,IAAA,CAAK,CAAC,MAAA,KAAW,WAAW,OAAO,CAAA;AAAA,EAC/D;AACJ;;;;"}
@@ -0,0 +1,24 @@
1
+ import { HttpService } from "@open-pioneer/http";
2
+ import Feature from "ol/Feature";
3
+ import FeatureFormat from "ol/format/Feature";
4
+ interface NextStrategyOptions {
5
+ fullUrl: URL;
6
+ limit: number | undefined;
7
+ featureFormat: FeatureFormat;
8
+ httpService: HttpService;
9
+ signal: AbortSignal;
10
+ onFeaturesLoaded: (features: Feature[]) => void;
11
+ }
12
+ /**
13
+ * Loads features from the OGC API Features collection by following the "next" links.
14
+ *
15
+ * This is the standards compliant way of iterating through a result set.
16
+ *
17
+ * This implementation may be slow for large data sets because it does not allow for any parallelism.
18
+ */
19
+ export declare class NextStrategy {
20
+ private options;
21
+ constructor(options: NextStrategyOptions);
22
+ load(): Promise<Feature[]>;
23
+ }
24
+ export {};
@@ -0,0 +1,33 @@
1
+ import { queryFeatures } from './requestUtils.js';
2
+
3
+ const DEFAULT_LIMIT = 5e3;
4
+ class NextStrategy {
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ async load() {
9
+ const options = this.options;
10
+ const limit = options.limit ?? DEFAULT_LIMIT;
11
+ let url = new URL(options.fullUrl);
12
+ url.searchParams.set("limit", limit.toString());
13
+ const featureChunks = [];
14
+ do {
15
+ const { features, nextLink } = await queryFeatures(
16
+ url,
17
+ options.featureFormat,
18
+ options.httpService,
19
+ options.signal
20
+ );
21
+ options.onFeaturesLoaded(features);
22
+ featureChunks.push(features);
23
+ if (!nextLink) {
24
+ break;
25
+ }
26
+ url = new URL(nextLink);
27
+ } while (1);
28
+ return featureChunks.flat(1);
29
+ }
30
+ }
31
+
32
+ export { NextStrategy };
33
+ //# sourceMappingURL=NextStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NextStrategy.js","sources":["NextStrategy.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 Feature from \"ol/Feature\";\nimport FeatureFormat from \"ol/format/Feature\";\nimport { queryFeatures } from \"./requestUtils\";\n\ninterface NextStrategyOptions {\n fullUrl: URL;\n limit: number | undefined;\n featureFormat: FeatureFormat;\n httpService: HttpService;\n signal: AbortSignal;\n\n // Called for partial results as they appear\n onFeaturesLoaded: (features: Feature[]) => void;\n}\n\nconst DEFAULT_LIMIT = 5000;\n\n/**\n * Loads features from the OGC API Features collection by following the \"next\" links.\n *\n * This is the standards compliant way of iterating through a result set.\n *\n * This implementation may be slow for large data sets because it does not allow for any parallelism.\n */\nexport class NextStrategy {\n constructor(private options: NextStrategyOptions) {}\n\n async load(): Promise<Feature[]> {\n const options = this.options;\n const limit = options.limit ?? DEFAULT_LIMIT;\n\n let url = new URL(options.fullUrl);\n url.searchParams.set(\"limit\", limit.toString());\n\n const featureChunks: Feature[][] = [];\n do {\n const { features, nextLink } = await queryFeatures(\n url,\n options.featureFormat,\n options.httpService,\n options.signal\n );\n\n options.onFeaturesLoaded(features);\n featureChunks.push(features);\n\n if (!nextLink) {\n break;\n }\n url = new URL(nextLink);\n // eslint-disable-next-line no-constant-condition\n } while (1);\n return featureChunks.flat(1);\n }\n}\n"],"names":[],"mappings":";;AAkBA,MAAM,aAAA,GAAgB,GAAA;AASf,MAAM,YAAA,CAAa;AAAA,EACtB,YAAoB,OAAA,EAA8B;AAA9B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAA+B;AAAA,EAEnD,MAAM,IAAA,GAA2B;AAC7B,IAAA,MAAM,UAAU,IAAA,CAAK,OAAA;AACrB,IAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,aAAA;AAE/B,IAAA,IAAI,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,OAAO,CAAA;AACjC,IAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,KAAA,CAAM,UAAU,CAAA;AAE9C,IAAA,MAAM,gBAA6B,EAAC;AACpC,IAAA,GAAG;AACC,MAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAS,GAAI,MAAM,aAAA;AAAA,QACjC,GAAA;AAAA,QACA,OAAA,CAAQ,aAAA;AAAA,QACR,OAAA,CAAQ,WAAA;AAAA,QACR,OAAA,CAAQ;AAAA,OACZ;AAEA,MAAA,OAAA,CAAQ,iBAAiB,QAAQ,CAAA;AACjC,MAAA,aAAA,CAAc,KAAK,QAAQ,CAAA;AAE3B,MAAA,IAAI,CAAC,QAAA,EAAU;AACX,QAAA;AAAA,MACJ;AACA,MAAA,GAAA,GAAM,IAAI,IAAI,QAAQ,CAAA;AAAA,IAE1B,CAAA,QAAS,CAAA;AACT,IAAA,OAAO,aAAA,CAAc,KAAK,CAAC,CAAA;AAAA,EAC/B;AACJ;;;;"}
@@ -0,0 +1,29 @@
1
+ import { HttpService } from "@open-pioneer/http";
2
+ import Feature from "ol/Feature";
3
+ import FeatureFormat from "ol/format/Feature";
4
+ export interface OffsetStrategyOptions {
5
+ fullUrl: URL;
6
+ limit: number | undefined;
7
+ featureFormat: FeatureFormat;
8
+ httpService: HttpService;
9
+ signal: AbortSignal;
10
+ concurrency?: number;
11
+ onFeaturesLoaded: (features: Feature[]) => void;
12
+ }
13
+ /**
14
+ * Loads features from the OGC API Features collection by using the non-standard `offset` parameter
15
+ *
16
+ * This can be faster than the standards compliant {@link NextStrategy} because we can issue
17
+ * parallel requests for large datasets.
18
+ */
19
+ export declare class OffsetStrategy {
20
+ #private;
21
+ private options;
22
+ private concurrency;
23
+ constructor(options: OffsetStrategyOptions);
24
+ load(): Promise<Feature[]>;
25
+ }
26
+ /**
27
+ * Returns true if the service supports paging via `offset` parameter.
28
+ */
29
+ export declare function supportsOffsetStrategy(collectionsItemsUrl: string, httpService: HttpService): Promise<boolean>;
@@ -0,0 +1,111 @@
1
+ import { createLogger } from '@open-pioneer/core';
2
+ import { sourceId } from '../_virtual/source-info.js';
3
+ import { queryFeatures, getNextLink } from './requestUtils.js';
4
+
5
+ const LOG = createLogger(sourceId);
6
+ const DEFAULT_CONCURRENCY = 6;
7
+ const DEFAULT_LIMIT = 2500;
8
+ class OffsetStrategy {
9
+ constructor(options) {
10
+ this.options = options;
11
+ this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
12
+ if (this.concurrency < 1) {
13
+ throw new Error("Invalid concurrency: " + this.concurrency);
14
+ }
15
+ }
16
+ concurrency;
17
+ async load() {
18
+ const options = this.options;
19
+ const fullUrl = options.fullUrl;
20
+ const pageSize = options.limit ?? DEFAULT_LIMIT;
21
+ const concurrency = this.concurrency;
22
+ let startOffset = 0;
23
+ let currentUrl = fullUrl;
24
+ const featureChunks = [];
25
+ let totalFeatures;
26
+ while (currentUrl) {
27
+ let pagesInIteration;
28
+ if (totalFeatures == void 0) {
29
+ pagesInIteration = concurrency;
30
+ } else {
31
+ pagesInIteration = Math.ceil((totalFeatures - startOffset) / pageSize);
32
+ }
33
+ pagesInIteration = Math.max(1, Math.min(pagesInIteration, concurrency));
34
+ const urls = [];
35
+ for (let page = 0; page < pagesInIteration; ++page) {
36
+ urls.push(createOffsetUrl(fullUrl, startOffset, pageSize));
37
+ startOffset += pageSize;
38
+ }
39
+ const { features, numberMatched, nextLink } = await this.#loadPages(urls);
40
+ featureChunks.push(features);
41
+ currentUrl = nextLink ? new URL(nextLink) : void 0;
42
+ if (numberMatched != null) {
43
+ totalFeatures = numberMatched;
44
+ }
45
+ }
46
+ return featureChunks.flat(1);
47
+ }
48
+ /**
49
+ * Loads features from multiple urls in parallel.
50
+ * The URLs should represent pages of the same result set.
51
+ * The `nextURL` of the last page (if any) is returned from this function.
52
+ */
53
+ async #loadPages(allUrls) {
54
+ const { featureFormat, httpService, signal, onFeaturesLoaded } = this.options;
55
+ const allFeatureResponse = {
56
+ nextLink: void 0,
57
+ numberMatched: void 0,
58
+ features: []
59
+ };
60
+ const allRequestPromises = allUrls.map(async (singleUrl, index) => {
61
+ const isLast = index === allUrls.length - 1;
62
+ const {
63
+ features,
64
+ numberMatched,
65
+ nextLink: nextUrl
66
+ } = await queryFeatures(singleUrl, featureFormat, httpService, signal);
67
+ onFeaturesLoaded(features);
68
+ LOG.debug(
69
+ `NextURL for index = ${index} (isLast = ${isLast}): ${nextUrl || "No Next URL"}`
70
+ );
71
+ allFeatureResponse.features.push(...features);
72
+ if (isLast) {
73
+ allFeatureResponse.numberMatched = numberMatched;
74
+ allFeatureResponse.nextLink = nextUrl;
75
+ }
76
+ });
77
+ await Promise.all(allRequestPromises);
78
+ return allFeatureResponse;
79
+ }
80
+ }
81
+ async function supportsOffsetStrategy(collectionsItemsUrl, httpService) {
82
+ const url = new URL(collectionsItemsUrl);
83
+ url.searchParams.set("limit", "1");
84
+ url.searchParams.set("f", "json");
85
+ const response = await httpService.fetch(url.toString(), {
86
+ headers: {
87
+ Accept: "application/geo+json"
88
+ }
89
+ });
90
+ if (response.status !== 200) {
91
+ throw new Error(`Failed to probe collection information (status code ${response.status})`);
92
+ }
93
+ const jsonResp = await response.json();
94
+ const nextUrl = getNextLink(jsonResp.links);
95
+ if (!nextUrl) {
96
+ return false;
97
+ }
98
+ const parsedURL = new URL(nextUrl);
99
+ const hasOffset = parsedURL.searchParams.has("offset");
100
+ return hasOffset;
101
+ }
102
+ function createOffsetUrl(fullUrl, offset, pageSize) {
103
+ const url = new URL(fullUrl);
104
+ const searchParams = url.searchParams;
105
+ searchParams.set("offset", offset.toString());
106
+ searchParams.set("limit", pageSize.toString());
107
+ return url;
108
+ }
109
+
110
+ export { OffsetStrategy, supportsOffsetStrategy };
111
+ //# sourceMappingURL=OffsetStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OffsetStrategy.js","sources":["OffsetStrategy.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger } from \"@open-pioneer/core\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport Feature from \"ol/Feature\";\nimport FeatureFormat from \"ol/format/Feature\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { type NextStrategy } from \"./NextStrategy\";\nimport { FeatureResponse, getNextLink, queryFeatures } from \"./requestUtils\";\n\nconst LOG = createLogger(sourceId);\n\nconst DEFAULT_CONCURRENCY = 6;\nconst DEFAULT_LIMIT = 2500;\n\nexport interface OffsetStrategyOptions {\n fullUrl: URL;\n limit: number | undefined;\n featureFormat: FeatureFormat;\n httpService: HttpService;\n signal: AbortSignal;\n concurrency?: number;\n\n // Called for partial results as they appear\n onFeaturesLoaded: (features: Feature[]) => void;\n}\n\n/**\n * Loads features from the OGC API Features collection by using the non-standard `offset` parameter\n *\n * This can be faster than the standards compliant {@link NextStrategy} because we can issue\n * parallel requests for large datasets.\n */\nexport class OffsetStrategy {\n private concurrency: number;\n\n constructor(private options: OffsetStrategyOptions) {\n this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;\n if (this.concurrency < 1) {\n throw new Error(\"Invalid concurrency: \" + this.concurrency);\n }\n }\n\n async load(): Promise<Feature[]> {\n const options = this.options;\n const fullUrl = options.fullUrl;\n const pageSize = options.limit ?? DEFAULT_LIMIT;\n const concurrency = this.concurrency;\n\n let startOffset = 0;\n let currentUrl: URL | undefined = fullUrl;\n\n const featureChunks: Feature[][] = [];\n let totalFeatures: number | undefined;\n while (currentUrl) {\n let pagesInIteration: number;\n if (totalFeatures == undefined) {\n // We don't know the actual size of the result set yet.\n pagesInIteration = concurrency;\n } else {\n pagesInIteration = Math.ceil((totalFeatures - startOffset) / pageSize);\n }\n pagesInIteration = Math.max(1, Math.min(pagesInIteration, concurrency));\n\n const urls: URL[] = [];\n for (let page = 0; page < pagesInIteration; ++page) {\n urls.push(createOffsetUrl(fullUrl, startOffset, pageSize));\n startOffset += pageSize;\n }\n\n const { features, numberMatched, nextLink } = await this.#loadPages(urls);\n featureChunks.push(features);\n currentUrl = nextLink ? new URL(nextLink) : undefined;\n if (numberMatched != null) {\n totalFeatures = numberMatched;\n }\n }\n return featureChunks.flat(1);\n }\n\n /**\n * Loads features from multiple urls in parallel.\n * The URLs should represent pages of the same result set.\n * The `nextURL` of the last page (if any) is returned from this function.\n */\n async #loadPages(allUrls: URL[]): Promise<FeatureResponse> {\n const { featureFormat, httpService, signal, onFeaturesLoaded } = this.options;\n const allFeatureResponse: FeatureResponse = {\n nextLink: undefined,\n numberMatched: undefined,\n features: []\n };\n const allRequestPromises = allUrls.map(async (singleUrl, index): Promise<void> => {\n const isLast = index === allUrls.length - 1;\n\n const {\n features,\n numberMatched,\n nextLink: nextUrl\n } = await queryFeatures(singleUrl, featureFormat, httpService, signal);\n onFeaturesLoaded(features);\n\n LOG.debug(\n `NextURL for index = ${index} (isLast = ${isLast}): ${nextUrl || \"No Next URL\"}`\n );\n allFeatureResponse.features.push(...features);\n if (isLast) {\n allFeatureResponse.numberMatched = numberMatched;\n allFeatureResponse.nextLink = nextUrl;\n }\n });\n await Promise.all(allRequestPromises);\n return allFeatureResponse;\n }\n}\n\n/**\n * Returns true if the service supports paging via `offset` parameter.\n */\nexport async function supportsOffsetStrategy(\n collectionsItemsUrl: string,\n httpService: HttpService\n): Promise<boolean> {\n const url = new URL(collectionsItemsUrl);\n url.searchParams.set(\"limit\", \"1\");\n url.searchParams.set(\"f\", \"json\");\n const response = await httpService.fetch(url.toString(), {\n headers: {\n Accept: \"application/geo+json\"\n }\n });\n if (response.status !== 200) {\n throw new Error(`Failed to probe collection information (status code ${response.status})`);\n }\n\n const jsonResp = await response.json();\n const nextUrl = getNextLink(jsonResp.links);\n if (!nextUrl) {\n return false;\n }\n\n const parsedURL = new URL(nextUrl);\n const hasOffset = parsedURL.searchParams.has(\"offset\");\n return hasOffset;\n}\n\n/**\n * Adds (or replaces) offset/limit params on the given url.\n */\nfunction createOffsetUrl(fullUrl: URL, offset: number, pageSize: number): URL {\n const url = new URL(fullUrl);\n const searchParams = url.searchParams;\n searchParams.set(\"offset\", offset.toString());\n searchParams.set(\"limit\", pageSize.toString());\n return url;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { HttpService } from "@open-pioneer/http";
2
+ import VectorSource from "ol/source/Vector";
3
+ import { OgcFeatureVectorSourceOptions } from "../api";
4
+ export declare class OgcFeaturesVectorSource extends VectorSource {
5
+ #private;
6
+ constructor(options: OgcFeatureVectorSourceOptions, httpService: HttpService);
7
+ }