@open-pioneer/ogc-features 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 +3 -1
- package/package.json +3 -3
- package/search-source/OgcFeatureSearchSource.js.map +1 -1
- package/search-source/OgcFeatureSearchSourceFactory.js.map +1 -1
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/vector-source/Metadata.js.map +1 -1
- package/vector-source/NextStrategy.js.map +1 -1
- package/vector-source/OffsetStrategy.js.map +1 -1
- package/vector-source/OgcFeaturesVectorSource.js.map +1 -1
- package/vector-source/OgcFeaturesVectorSourceFactory.js.map +1 -1
- package/vector-source/requestUtils.d.ts +2 -2
- package/vector-source/requestUtils.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# @open-pioneer/ogc-features
|
|
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
|
+
- d862003: Update to trails core-packages 4.7.0
|
|
8
9
|
|
|
9
10
|
### Patch Changes
|
|
10
11
|
|
|
11
12
|
- 078bef5: Use private JavaScript properties (#) instead of TypeScript keyword.
|
|
13
|
+
- c16a401: Migrated from eslint to oxlint and from prettier to oxfmt.
|
|
12
14
|
|
|
13
15
|
## 1.3.0
|
|
14
16
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@open-pioneer/ogc-features",
|
|
4
|
-
"version": "1.4.0
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"description": "This package provides utilities to work with OGC API Features services.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"open-pioneer-trails"
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"directory": "src/packages/ogc-features"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@open-pioneer/core": "4.7.0
|
|
18
|
-
"@open-pioneer/http": "4.7.0
|
|
17
|
+
"@open-pioneer/core": "^4.7.0",
|
|
18
|
+
"@open-pioneer/http": "^4.7.0",
|
|
19
19
|
"ol": "^10.9.0",
|
|
20
20
|
"uuid": "^14.0.1"
|
|
21
21
|
},
|
|
@@ -1 +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":";;;;
|
|
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\n\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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"}
|
|
@@ -1 +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":";;
|
|
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\n\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":";;AAgBO,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,4 +1,4 @@
|
|
|
1
|
-
import { OgcFeaturesVectorSourceFactory } from "./vector-source/OgcFeaturesVectorSourceFactory";
|
|
2
1
|
import { OgcFeatureSearchSourceFactory } from "./search-source/OgcFeatureSearchSourceFactory";
|
|
2
|
+
import { OgcFeaturesVectorSourceFactory } from "./vector-source/OgcFeaturesVectorSourceFactory";
|
|
3
3
|
export { OgcFeaturesVectorSourceFactory as VectorSourceFactory };
|
|
4
4
|
export { OgcFeatureSearchSourceFactory as SearchSourceFactory };
|
package/services.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { OgcFeaturesVectorSourceFactory as VectorSourceFactory } from './vector-source/OgcFeaturesVectorSourceFactory.js';
|
|
2
1
|
export { OgcFeatureSearchSourceFactory as SearchSourceFactory } from './search-source/OgcFeatureSearchSourceFactory.js';
|
|
2
|
+
export { OgcFeaturesVectorSourceFactory as VectorSourceFactory } from './vector-source/OgcFeaturesVectorSourceFactory.js';
|
|
3
3
|
//# sourceMappingURL=services.js.map
|
|
@@ -1 +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":"
|
|
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\n\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":"AAqBA,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;;;;"}
|
|
@@ -1 +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 #options: NextStrategyOptions;\n\n constructor(options: NextStrategyOptions) {\n this.#options = options;\n }\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 //
|
|
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\n\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 #options: NextStrategyOptions;\n\n constructor(options: NextStrategyOptions) {\n this.#options = options;\n }\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 // oxlint-disable-next-line no-await-in-loop\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 // oxlint-disable-next-line no-constant-condition\n } while (1);\n return featureChunks.flat(1);\n }\n}\n"],"names":[],"mappings":";;AAmBA,MAAM,aAAA,GAAgB,GAAA;AASf,MAAM,YAAA,CAAa;AAAA,EACtB,QAAA;AAAA,EAEA,YAAY,OAAA,EAA8B;AACtC,IAAA,IAAA,CAAK,QAAA,GAAW,OAAA;AAAA,EACpB;AAAA,EAEA,MAAM,IAAA,GAA2B;AAC7B,IAAA,MAAM,UAAU,IAAA,CAAK,QAAA;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;AAEC,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;;;;"}
|
|
@@ -1 +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 #concurrency: number;\n #options: OffsetStrategyOptions;\n\n constructor(options: OffsetStrategyOptions) {\n this.#options = options;\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":";;;;
|
|
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\n\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\";\n// oxlint-disable-next-line no-unused-vars\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 #concurrency: number;\n #options: OffsetStrategyOptions;\n\n constructor(options: OffsetStrategyOptions) {\n this.#options = options;\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 // oxlint-disable-next-line no-await-in-loop\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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OgcFeaturesVectorSource.js","sources":["OgcFeaturesVectorSource.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createAbortError, createLogger, isAbortError, throwAbortError } from \"@open-pioneer/core\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport { Extent } from \"ol/extent\";\nimport Feature from \"ol/Feature\";\nimport GeoJSON from \"ol/format/GeoJSON\";\nimport { bbox } from \"ol/loadingstrategy\";\nimport { Projection } from \"ol/proj\";\nimport VectorSource from \"ol/source/Vector\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { OgcFeatureVectorSourceOptions } from \"../api\";\nimport { CollectionMetadata, findMatchingCrs, getCollectionMetadata } from \"./Metadata\";\nimport { NextStrategy } from \"./NextStrategy\";\nimport { OffsetStrategy, supportsOffsetStrategy } from \"./OffsetStrategy\";\nimport { createCollectionRequestUrl } from \"./requestUtils\";\n\nconst LOG = createLogger(sourceId);\n\nconst CRS_OGC_CRS84 = \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\";\n\nexport class OgcFeaturesVectorSource extends VectorSource {\n #featureFormat: GeoJSON;\n #httpService: HttpService;\n\n #options: OgcFeatureVectorSourceOptions;\n #itemsUrl: string;\n #collectionUrl: string;\n\n #metadataPromise: Promise<CollectionMetadata> | undefined;\n #loadingStrategyPromise: Promise<\"offset\" | \"next\"> | undefined;\n\n // Cancels pending feature load operations.\n #featuresAbortController: AbortController | undefined;\n\n // Maps a map CRS to the corresponding request CRS that should be used for requests to the OGC API Features service.\n #mapCrsToRequestCrs: Record<string, string> = {\n // Special case: When map is in EPSG:4326, which defines lat/long-order for coordinate values,\n // make sure features are requested in CRS84, which has long/lat-order.\n // Reason for this special case is that OpenLayers always expects coordinates to be in long/lat-order,\n // even when the CRS definition specifies lat/long-order (as is the case for EPSG:4326).\n // CRS84 is mandated to be supported by all OGC API Features services, so we can safely assume that the service will support it.\n [\"4326\"]: CRS_OGC_CRS84,\n [\"EPSG:4326\"]: CRS_OGC_CRS84\n };\n\n constructor(options: OgcFeatureVectorSourceOptions, httpService: HttpService) {\n const format = new GeoJSON();\n super({\n format,\n strategy: bbox,\n attributions: options.attributions,\n loader: (e, r, p) => {\n return this.#load(e, r, p);\n },\n ...options.additionalOptions\n });\n this.#featureFormat = format;\n this.#httpService = httpService;\n this.#options = options;\n this.#collectionUrl = `${options.baseUrl.replace(/\\/+$/, \"\")}/collections/${options.collectionId}`;\n this.#itemsUrl = `${this.#collectionUrl}/items`;\n }\n\n async #load(extent: Extent, _resolution: number, projection: Projection) {\n try {\n const features = await this.#loadImpl(extent, projection);\n return features;\n } catch (e) {\n if (!isAbortError(e)) {\n LOG.error(\"Failed to load features from ogc service\", e);\n }\n throw e;\n }\n }\n\n async #loadImpl(extent: Extent, projection: Projection): Promise<Feature[]> {\n const [collectionMetadata, strategy] = await Promise.all([\n this.#loadCollectionMetadata(),\n this.#getLoadingStrategy()\n ]);\n\n // An extent-change should cancel open requests for older extents, because otherwise,\n // old and expensive requests could block new requests for a new extent\n // => no features are drawn on the current map for a long time.\n //TODO: More context\n this.#featuresAbortController?.abort(createAbortError());\n const abortController = (this.#featuresAbortController = new AbortController());\n const requestCrs = this.#getRequestCrs(collectionMetadata, projection);\n const fullUrl = this.#getRequestUrl(extent, requestCrs);\n const sharedOptions = {\n fullUrl,\n featureFormat: this.#featureFormat,\n limit: this.#options.limit,\n httpService: this.#httpService,\n signal: abortController.signal,\n onFeaturesLoaded: (features: Feature[]) => {\n LOG.debug(`Adding ${features.length} features`);\n this.addFeatures(features);\n }\n };\n let strategyImpl;\n switch (strategy) {\n case \"next\": {\n strategyImpl = new NextStrategy(sharedOptions);\n break;\n }\n case \"offset\":\n strategyImpl = new OffsetStrategy({\n ...sharedOptions,\n concurrency: this.#options.maxConcurrentRequests\n });\n break;\n }\n\n const features = await strategyImpl.load();\n LOG.debug(\"Finished loading features for extent:\", extent);\n return features;\n }\n\n // Fetches collection metadata from the service (once).\n async #loadCollectionMetadata() {\n const run = async () => {\n let metadata;\n try {\n metadata = await getCollectionMetadata(this.#collectionUrl, this.#httpService);\n } catch (e) {\n LOG.error(\n `Failed to retrieve collection metadata for collection '${this.#collectionUrl}'`,\n e\n );\n throwAbortError(); // Report error up the stack but only log error once\n }\n\n try {\n if (this.getAttributions() == null && metadata.attribution) {\n this.setAttributions(metadata.attribution);\n }\n } catch (e) {\n LOG.error(\"Failed to apply attributions\", e);\n throwAbortError(); // Report error up the stack but only log error once\n }\n return metadata;\n };\n\n const promise = (this.#metadataPromise ??= run());\n return await promise;\n }\n\n // Runs feature detection on the service (once).\n async #getLoadingStrategy() {\n const run = async () => {\n let supportsOffset;\n try {\n supportsOffset = await supportsOffsetStrategy(this.#itemsUrl, this.#httpService);\n } catch (e) {\n LOG.error(\n `Failed to retrieve collection information for collection '${this.#collectionUrl}'`,\n e\n );\n throwAbortError(); // Report error up the stack but only log error once\n }\n\n const options = this.#options;\n let strategy = options?.strategy || (supportsOffset ? \"offset\" : \"next\");\n if (strategy === \"offset\" && !supportsOffset) {\n strategy = \"next\";\n }\n return strategy;\n };\n\n const promise = (this.#loadingStrategyPromise ??= run());\n return await promise;\n }\n\n // Computes the appropriate request crs for the current configuration.\n #getRequestCrs(collectionMetadata: CollectionMetadata | undefined, projection: Projection) {\n const mapCrs = projection.getCode();\n\n const requestCrs = this.#options.crs ?? this.#mapCrsToRequestCrs[mapCrs];\n if (requestCrs) {\n return requestCrs;\n }\n\n const matchingMapCrs = findMatchingCrs(mapCrs, collectionMetadata?.crs);\n if (matchingMapCrs) {\n this.#mapCrsToRequestCrs[mapCrs] = matchingMapCrs;\n return matchingMapCrs;\n } else {\n LOG.error(`Map CRS '${mapCrs}' not supported by collection '${this.#collectionUrl}'.`);\n throwAbortError();\n }\n }\n\n #getRequestUrl(extent: Extent, requestCrs: string) {\n let requestUrl = createCollectionRequestUrl(this.#itemsUrl, extent, requestCrs);\n const rewriteUrl = this.#options.rewriteUrl;\n if (rewriteUrl) {\n requestUrl = rewriteUrl(requestUrl) ?? requestUrl;\n }\n return requestUrl;\n }\n}\n"],"names":["features"],"mappings":";;;;;;;;;;AAiBA,MAAM,GAAA,GAAM,aAAa,QAAQ,CAAA;AAEjC,MAAM,aAAA,GAAgB,8CAAA;AAEf,MAAM,gCAAgC,YAAA,CAAa;AAAA,EACtD,cAAA;AAAA,EACA,YAAA;AAAA,EAEA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA,EAEA,gBAAA;AAAA,EACA,uBAAA;AAAA;AAAA,EAGA,wBAAA;AAAA;AAAA,EAGA,mBAAA,GAA8C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAM1C,CAAC,MAAM,GAAG,aAAA;AAAA,IACV,CAAC,WAAW,GAAG;AAAA,GACnB;AAAA,EAEA,WAAA,CAAY,SAAwC,WAAA,EAA0B;AAC1E,IAAA,MAAM,MAAA,GAAS,IAAI,OAAA,EAAQ;AAC3B,IAAA,KAAA,CAAM;AAAA,MACF,MAAA;AAAA,MACA,QAAA,EAAU,IAAA;AAAA,MACV,cAAc,OAAA,CAAQ,YAAA;AAAA,MACtB,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,KAAM;AACjB,QAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAAA,MAC7B,CAAA;AAAA,MACA,GAAG,OAAA,CAAQ;AAAA,KACd,CAAA;AACD,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAA;AACtB,IAAA,IAAA,CAAK,YAAA,GAAe,WAAA;AACpB,IAAA,IAAA,CAAK,QAAA,GAAW,OAAA;AAChB,IAAA,IAAA,CAAK,cAAA,GAAiB,CAAA,EAAG,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,QAAQ,EAAE,CAAC,CAAA,aAAA,EAAgB,OAAA,CAAQ,YAAY,CAAA,CAAA;AAChG,IAAA,IAAA,CAAK,SAAA,GAAY,CAAA,EAAG,IAAA,CAAK,cAAc,CAAA,MAAA,CAAA;AAAA,EAC3C;AAAA,EAEA,MAAM,KAAA,CAAM,MAAA,EAAgB,WAAA,EAAqB,UAAA,EAAwB;AACrE,IAAA,IAAI;AACA,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,SAAA,CAAU,QAAQ,UAAU,CAAA;AACxD,MAAA,OAAO,QAAA;AAAA,IACX,SAAS,CAAA,EAAG;AACR,MAAA,IAAI,CAAC,YAAA,CAAa,CAAC,CAAA,EAAG;AAClB,QAAA,GAAA,CAAI,KAAA,CAAM,4CAA4C,CAAC,CAAA;AAAA,MAC3D;AACA,MAAA,MAAM,CAAA;AAAA,IACV;AAAA,EACJ;AAAA,EAEA,MAAM,SAAA,CAAU,MAAA,EAAgB,UAAA,EAA4C;AACxE,IAAA,MAAM,CAAC,kBAAA,EAAoB,QAAQ,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MACrD,KAAK,uBAAA,EAAwB;AAAA,MAC7B,KAAK,mBAAA;AAAoB,KAC5B,CAAA;AAMD,IAAA,IAAA,CAAK,wBAAA,EAA0B,KAAA,CAAM,gBAAA,EAAkB,CAAA;AACvD,IAAA,MAAM,eAAA,GAAmB,IAAA,CAAK,wBAAA,GAA2B,IAAI,eAAA,EAAgB;AAC7E,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,cAAA,CAAe,kBAAA,EAAoB,UAAU,CAAA;AACrE,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,UAAU,CAAA;AACtD,IAAA,MAAM,aAAA,GAAgB;AAAA,MAClB,OAAA;AAAA,MACA,eAAe,IAAA,CAAK,cAAA;AAAA,MACpB,KAAA,EAAO,KAAK,QAAA,CAAS,KAAA;AAAA,MACrB,aAAa,IAAA,CAAK,YAAA;AAAA,MAClB,QAAQ,eAAA,CAAgB,MAAA;AAAA,MACxB,gBAAA,EAAkB,CAACA,SAAAA,KAAwB;AACvC,QAAA,GAAA,CAAI,KAAA,CAAM,CAAA,OAAA,EAAUA,SAAAA,CAAS,MAAM,CAAA,SAAA,CAAW,CAAA;AAC9C,QAAA,IAAA,CAAK,YAAYA,SAAQ,CAAA;AAAA,MAC7B;AAAA,KACJ;AACA,IAAA,IAAI,YAAA;AACJ,IAAA,QAAQ,QAAA;AAAU,MACd,KAAK,MAAA,EAAQ;AACT,QAAA,YAAA,GAAe,IAAI,aAAa,aAAa,CAAA;AAC7C,QAAA;AAAA,MACJ;AAAA,MACA,KAAK,QAAA;AACD,QAAA,YAAA,GAAe,IAAI,cAAA,CAAe;AAAA,UAC9B,GAAG,aAAA;AAAA,UACH,WAAA,EAAa,KAAK,QAAA,CAAS;AAAA,SAC9B,CAAA;AACD,QAAA;AAAA;AAGR,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,IAAA,EAAK;AACzC,IAAA,GAAA,CAAI,KAAA,CAAM,yCAAyC,MAAM,CAAA;AACzD,IAAA,OAAO,QAAA;AAAA,EACX;AAAA;AAAA,EAGA,MAAM,uBAAA,GAA0B;AAC5B,IAAA,MAAM,MAAM,YAAY;AACpB,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI;AACA,QAAA,QAAA,GAAW,MAAM,qBAAA,CAAsB,IAAA,CAAK,cAAA,EAAgB,KAAK,YAAY,CAAA;AAAA,MACjF,SAAS,CAAA,EAAG;AACR,QAAA,GAAA,CAAI,KAAA;AAAA,UACA,CAAA,uDAAA,EAA0D,KAAK,cAAc,CAAA,CAAA,CAAA;AAAA,UAC7E;AAAA,SACJ;AACA,QAAA,eAAA,EAAgB;AAAA,MACpB;AAEA,MAAA,IAAI;AACA,QAAA,IAAI,IAAA,CAAK,eAAA,EAAgB,IAAK,IAAA,IAAQ,SAAS,WAAA,EAAa;AACxD,UAAA,IAAA,CAAK,eAAA,CAAgB,SAAS,WAAW,CAAA;AAAA,QAC7C;AAAA,MACJ,SAAS,CAAA,EAAG;AACR,QAAA,GAAA,CAAI,KAAA,CAAM,gCAAgC,CAAC,CAAA;AAC3C,QAAA,eAAA,EAAgB;AAAA,MACpB;AACA,MAAA,OAAO,QAAA;AAAA,IACX,CAAA;AAEA,IAAA,MAAM,OAAA,GAAW,IAAA,CAAK,gBAAA,KAAqB,GAAA,EAAI;AAC/C,IAAA,OAAO,MAAM,OAAA;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,mBAAA,GAAsB;AACxB,IAAA,MAAM,MAAM,YAAY;AACpB,MAAA,IAAI,cAAA;AACJ,MAAA,IAAI;AACA,QAAA,cAAA,GAAiB,MAAM,sBAAA,CAAuB,IAAA,CAAK,SAAA,EAAW,KAAK,YAAY,CAAA;AAAA,MACnF,SAAS,CAAA,EAAG;AACR,QAAA,GAAA,CAAI,KAAA;AAAA,UACA,CAAA,0DAAA,EAA6D,KAAK,cAAc,CAAA,CAAA,CAAA;AAAA,UAChF;AAAA,SACJ;AACA,QAAA,eAAA,EAAgB;AAAA,MACpB;AAEA,MAAA,MAAM,UAAU,IAAA,CAAK,QAAA;AACrB,MAAA,IAAI,QAAA,GAAW,OAAA,EAAS,QAAA,KAAa,cAAA,GAAiB,QAAA,GAAW,MAAA,CAAA;AACjE,MAAA,IAAI,QAAA,KAAa,QAAA,IAAY,CAAC,cAAA,EAAgB;AAC1C,QAAA,QAAA,GAAW,MAAA;AAAA,MACf;AACA,MAAA,OAAO,QAAA;AAAA,IACX,CAAA;AAEA,IAAA,MAAM,OAAA,GAAW,IAAA,CAAK,uBAAA,KAA4B,GAAA,EAAI;AACtD,IAAA,OAAO,MAAM,OAAA;AAAA,EACjB;AAAA;AAAA,EAGA,cAAA,CAAe,oBAAoD,UAAA,EAAwB;AACvF,IAAA,MAAM,MAAA,GAAS,WAAW,OAAA,EAAQ;AAElC,IAAA,MAAM,aAAa,IAAA,CAAK,QAAA,CAAS,GAAA,IAAO,IAAA,CAAK,oBAAoB,MAAM,CAAA;AACvE,IAAA,IAAI,UAAA,EAAY;AACZ,MAAA,OAAO,UAAA;AAAA,IACX;AAEA,IAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,MAAA,EAAQ,kBAAA,EAAoB,GAAG,CAAA;AACtE,IAAA,IAAI,cAAA,EAAgB;AAChB,MAAA,IAAA,CAAK,mBAAA,CAAoB,MAAM,CAAA,GAAI,cAAA;AACnC,MAAA,OAAO,cAAA;AAAA,IACX,CAAA,MAAO;AACH,MAAA,GAAA,CAAI,MAAM,CAAA,SAAA,EAAY,MAAM,CAAA,+BAAA,EAAkC,IAAA,CAAK,cAAc,CAAA,EAAA,CAAI,CAAA;AACrF,MAAA,eAAA,EAAgB;AAAA,IACpB;AAAA,EACJ;AAAA,EAEA,cAAA,CAAe,QAAgB,UAAA,EAAoB;AAC/C,IAAA,IAAI,UAAA,GAAa,0BAAA,CAA2B,IAAA,CAAK,SAAA,EAAW,QAAQ,UAAU,CAAA;AAC9E,IAAA,MAAM,UAAA,GAAa,KAAK,QAAA,CAAS,UAAA;AACjC,IAAA,IAAI,UAAA,EAAY;AACZ,MAAA,UAAA,GAAa,UAAA,CAAW,UAAU,CAAA,IAAK,UAAA;AAAA,IAC3C;AACA,IAAA,OAAO,UAAA;AAAA,EACX;AACJ;;;;"}
|
|
1
|
+
{"version":3,"file":"OgcFeaturesVectorSource.js","sources":["OgcFeaturesVectorSource.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { createAbortError, createLogger, isAbortError, throwAbortError } from \"@open-pioneer/core\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport { Extent } from \"ol/extent\";\nimport Feature from \"ol/Feature\";\nimport GeoJSON from \"ol/format/GeoJSON\";\nimport { bbox } from \"ol/loadingstrategy\";\nimport { Projection } from \"ol/proj\";\nimport VectorSource from \"ol/source/Vector\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { OgcFeatureVectorSourceOptions } from \"../api\";\nimport { CollectionMetadata, findMatchingCrs, getCollectionMetadata } from \"./Metadata\";\nimport { NextStrategy } from \"./NextStrategy\";\nimport { OffsetStrategy, supportsOffsetStrategy } from \"./OffsetStrategy\";\nimport { createCollectionRequestUrl } from \"./requestUtils\";\n\nconst LOG = createLogger(sourceId);\n\nconst CRS_OGC_CRS84 = \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\";\n\nexport class OgcFeaturesVectorSource extends VectorSource {\n #featureFormat: GeoJSON;\n #httpService: HttpService;\n\n #options: OgcFeatureVectorSourceOptions;\n #itemsUrl: string;\n #collectionUrl: string;\n\n #metadataPromise: Promise<CollectionMetadata> | undefined;\n #loadingStrategyPromise: Promise<\"offset\" | \"next\"> | undefined;\n\n // Cancels pending feature load operations.\n #featuresAbortController: AbortController | undefined;\n\n // Maps a map CRS to the corresponding request CRS that should be used for requests to the OGC API Features service.\n #mapCrsToRequestCrs: Record<string, string> = {\n // Special case: When map is in EPSG:4326, which defines lat/long-order for coordinate values,\n // make sure features are requested in CRS84, which has long/lat-order.\n // Reason for this special case is that OpenLayers always expects coordinates to be in long/lat-order,\n // even when the CRS definition specifies lat/long-order (as is the case for EPSG:4326).\n // CRS84 is mandated to be supported by all OGC API Features services, so we can safely assume that the service will support it.\n [\"4326\"]: CRS_OGC_CRS84,\n [\"EPSG:4326\"]: CRS_OGC_CRS84\n };\n\n constructor(options: OgcFeatureVectorSourceOptions, httpService: HttpService) {\n const format = new GeoJSON();\n super({\n format,\n strategy: bbox,\n attributions: options.attributions,\n loader: (e, r, p) => {\n return this.#load(e, r, p);\n },\n ...options.additionalOptions\n });\n this.#featureFormat = format;\n this.#httpService = httpService;\n this.#options = options;\n this.#collectionUrl = `${options.baseUrl.replace(/\\/+$/, \"\")}/collections/${options.collectionId}`;\n this.#itemsUrl = `${this.#collectionUrl}/items`;\n }\n\n async #load(extent: Extent, _resolution: number, projection: Projection) {\n try {\n const features = await this.#loadImpl(extent, projection);\n return features;\n } catch (e) {\n if (!isAbortError(e)) {\n LOG.error(\"Failed to load features from ogc service\", e);\n }\n throw e;\n }\n }\n\n async #loadImpl(extent: Extent, projection: Projection): Promise<Feature[]> {\n const [collectionMetadata, strategy] = await Promise.all([\n this.#loadCollectionMetadata(),\n this.#getLoadingStrategy()\n ]);\n\n // An extent-change should cancel open requests for older extents, because otherwise,\n // old and expensive requests could block new requests for a new extent\n // => no features are drawn on the current map for a long time.\n //TODO: More context\n this.#featuresAbortController?.abort(createAbortError());\n const abortController = (this.#featuresAbortController = new AbortController());\n const requestCrs = this.#getRequestCrs(collectionMetadata, projection);\n const fullUrl = this.#getRequestUrl(extent, requestCrs);\n const sharedOptions = {\n fullUrl,\n featureFormat: this.#featureFormat,\n limit: this.#options.limit,\n httpService: this.#httpService,\n signal: abortController.signal,\n onFeaturesLoaded: (features: Feature[]) => {\n LOG.debug(`Adding ${features.length} features`);\n this.addFeatures(features);\n }\n };\n let strategyImpl;\n switch (strategy) {\n case \"next\": {\n strategyImpl = new NextStrategy(sharedOptions);\n break;\n }\n case \"offset\":\n strategyImpl = new OffsetStrategy({\n ...sharedOptions,\n concurrency: this.#options.maxConcurrentRequests\n });\n break;\n }\n\n const features = await strategyImpl.load();\n LOG.debug(\"Finished loading features for extent:\", extent);\n return features;\n }\n\n // Fetches collection metadata from the service (once).\n async #loadCollectionMetadata() {\n const run = async () => {\n let metadata;\n try {\n metadata = await getCollectionMetadata(this.#collectionUrl, this.#httpService);\n } catch (e) {\n LOG.error(\n `Failed to retrieve collection metadata for collection '${this.#collectionUrl}'`,\n e\n );\n throwAbortError(); // Report error up the stack but only log error once\n }\n\n try {\n if (this.getAttributions() == null && metadata.attribution) {\n this.setAttributions(metadata.attribution);\n }\n } catch (e) {\n LOG.error(\"Failed to apply attributions\", e);\n throwAbortError(); // Report error up the stack but only log error once\n }\n return metadata;\n };\n\n const promise = (this.#metadataPromise ??= run());\n return await promise;\n }\n\n // Runs feature detection on the service (once).\n async #getLoadingStrategy() {\n const run = async () => {\n let supportsOffset;\n try {\n supportsOffset = await supportsOffsetStrategy(this.#itemsUrl, this.#httpService);\n } catch (e) {\n LOG.error(\n `Failed to retrieve collection information for collection '${this.#collectionUrl}'`,\n e\n );\n throwAbortError(); // Report error up the stack but only log error once\n }\n\n const options = this.#options;\n let strategy = options?.strategy || (supportsOffset ? \"offset\" : \"next\");\n if (strategy === \"offset\" && !supportsOffset) {\n strategy = \"next\";\n }\n return strategy;\n };\n\n const promise = (this.#loadingStrategyPromise ??= run());\n return await promise;\n }\n\n // Computes the appropriate request crs for the current configuration.\n #getRequestCrs(collectionMetadata: CollectionMetadata | undefined, projection: Projection) {\n const mapCrs = projection.getCode();\n\n const requestCrs = this.#options.crs ?? this.#mapCrsToRequestCrs[mapCrs];\n if (requestCrs) {\n return requestCrs;\n }\n\n const matchingMapCrs = findMatchingCrs(mapCrs, collectionMetadata?.crs);\n if (matchingMapCrs) {\n this.#mapCrsToRequestCrs[mapCrs] = matchingMapCrs;\n return matchingMapCrs;\n } else {\n LOG.error(`Map CRS '${mapCrs}' not supported by collection '${this.#collectionUrl}'.`);\n throwAbortError();\n }\n }\n\n #getRequestUrl(extent: Extent, requestCrs: string) {\n let requestUrl = createCollectionRequestUrl(this.#itemsUrl, extent, requestCrs);\n const rewriteUrl = this.#options.rewriteUrl;\n if (rewriteUrl) {\n requestUrl = rewriteUrl(requestUrl) ?? requestUrl;\n }\n return requestUrl;\n }\n}\n"],"names":["features"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OgcFeaturesVectorSourceFactory.js","sources":["OgcFeaturesVectorSourceFactory.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 { Feature } from \"ol\";\nimport { Geometry } from \"ol/geom\";\nimport VectorSource from \"ol/source/Vector\";\nimport {\n OgcFeatureVectorSourceOptions,\n OgcFeaturesVectorSourceFactory as ServiceInterface\n} from \"../api\";\nimport { OgcFeaturesVectorSource } from \"./OgcFeaturesVectorSource\";\n\ninterface References {\n httpService: HttpService;\n}\n\nexport class OgcFeaturesVectorSourceFactory implements ServiceInterface {\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 new OgcFeaturesVectorSource(options, this.#httpService);\n }\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"OgcFeaturesVectorSourceFactory.js","sources":["OgcFeaturesVectorSourceFactory.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 { ServiceOptions } from \"@open-pioneer/runtime\";\nimport { Feature } from \"ol\";\nimport { Geometry } from \"ol/geom\";\nimport VectorSource from \"ol/source/Vector\";\nimport {\n OgcFeatureVectorSourceOptions,\n OgcFeaturesVectorSourceFactory as ServiceInterface\n} from \"../api\";\nimport { OgcFeaturesVectorSource } from \"./OgcFeaturesVectorSource\";\n\ninterface References {\n httpService: HttpService;\n}\n\nexport class OgcFeaturesVectorSourceFactory implements ServiceInterface {\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 new OgcFeaturesVectorSource(options, this.#httpService);\n }\n}\n"],"names":[],"mappings":";;AAkBO,MAAM,8BAAA,CAA2D;AAAA,EACpE,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,IAAI,uBAAA,CAAwB,OAAA,EAAS,IAAA,CAAK,YAAY,CAAA;AAAA,EACjE;AACJ;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { HttpService } from "@open-pioneer/http";
|
|
1
2
|
import { Extent } from "ol/extent";
|
|
2
|
-
import FeatureFormat from "ol/format/Feature";
|
|
3
3
|
import Feature from "ol/Feature";
|
|
4
|
-
import
|
|
4
|
+
import FeatureFormat from "ol/format/Feature";
|
|
5
5
|
/**
|
|
6
6
|
* Assembles the url to use for fetching features in the given extent.
|
|
7
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestUtils.js","sources":["requestUtils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport {
|
|
1
|
+
{"version":3,"file":"requestUtils.js","sources":["requestUtils.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 { Extent } from \"ol/extent\";\nimport Feature from \"ol/Feature\";\nimport FeatureFormat from \"ol/format/Feature\";\n\nconst NEXT_LINK_PROP = \"next\";\n\n/**\n * Assembles the url to use for fetching features in the given extent.\n */\nexport function createCollectionRequestUrl(\n collectionItemsUrl: string,\n extent: Extent,\n crs: string\n): URL {\n const url = new URL(collectionItemsUrl);\n const searchParams = url.searchParams;\n searchParams.set(\"bbox\", extent.join(\",\"));\n searchParams.set(\"bbox-crs\", crs);\n searchParams.set(\"crs\", crs);\n searchParams.set(\"f\", \"json\");\n return url;\n}\n\n/**\n * Extracts the `next` link from the service response's `links` property.\n */\nexport function getNextLink(rawLinks: unknown): string | undefined {\n if (!Array.isArray(rawLinks)) {\n return undefined;\n }\n\n interface ObjWithRelAndHref {\n href: string;\n rel: string;\n }\n\n // We just assume the correct object shape\n const links = rawLinks as ObjWithRelAndHref[];\n\n const nextLinks = links.filter((link) => link.rel === NEXT_LINK_PROP);\n if (nextLinks.length !== 1) return;\n return nextLinks[0]?.href;\n}\n\nexport interface FeatureResponse {\n features: Feature[];\n nextLink: string | undefined;\n numberMatched: number | undefined;\n}\n\n/**\n * Performs a single request against the service\n */\nexport async function queryFeatures(\n fullUrl: URL,\n featureFormat: FeatureFormat,\n httpService: HttpService,\n signal: AbortSignal | undefined\n): Promise<FeatureResponse> {\n const response = await httpService.fetch(fullUrl, {\n headers: {\n Accept: \"application/geo+json\"\n },\n signal\n });\n if (response.status !== 200) {\n throw new Error(`Failed to query features from service (status code ${response.status})`);\n }\n const geoJson = await response.json();\n const features = featureFormat.readFeatures(geoJson);\n const nextLink = getNextLink(geoJson.links);\n return {\n features,\n numberMatched: geoJson.numberMatched,\n nextLink\n };\n}\n"],"names":[],"mappings":"AAQA,MAAM,cAAA,GAAiB,MAAA;AAKhB,SAAS,0BAAA,CACZ,kBAAA,EACA,MAAA,EACA,GAAA,EACG;AACH,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,kBAAkB,CAAA;AACtC,EAAA,MAAM,eAAe,GAAA,CAAI,YAAA;AACzB,EAAA,YAAA,CAAa,GAAA,CAAI,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AACzC,EAAA,YAAA,CAAa,GAAA,CAAI,YAAY,GAAG,CAAA;AAChC,EAAA,YAAA,CAAa,GAAA,CAAI,OAAO,GAAG,CAAA;AAC3B,EAAA,YAAA,CAAa,GAAA,CAAI,KAAK,MAAM,CAAA;AAC5B,EAAA,OAAO,GAAA;AACX;AAKO,SAAS,YAAY,QAAA,EAAuC;AAC/D,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC1B,IAAA,OAAO,MAAA;AAAA,EACX;AAQA,EAAA,MAAM,KAAA,GAAQ,QAAA;AAEd,EAAA,MAAM,YAAY,KAAA,CAAM,MAAA,CAAO,CAAC,IAAA,KAAS,IAAA,CAAK,QAAQ,cAAc,CAAA;AACpE,EAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC5B,EAAA,OAAO,SAAA,CAAU,CAAC,CAAA,EAAG,IAAA;AACzB;AAWA,eAAsB,aAAA,CAClB,OAAA,EACA,aAAA,EACA,WAAA,EACA,MAAA,EACwB;AACxB,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,KAAA,CAAM,OAAA,EAAS;AAAA,IAC9C,OAAA,EAAS;AAAA,MACL,MAAA,EAAQ;AAAA,KACZ;AAAA,IACA;AAAA,GACH,CAAA;AACD,EAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AACzB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mDAAA,EAAsD,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC5F;AACA,EAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,IAAA,EAAK;AACpC,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,YAAA,CAAa,OAAO,CAAA;AACnD,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,OAAA,CAAQ,KAAK,CAAA;AAC1C,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA,eAAe,OAAA,CAAQ,aAAA;AAAA,IACvB;AAAA,GACJ;AACJ;;;;"}
|