@open-pioneer/ogc-features 1.3.0-dev.20260512095810 → 1.3.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
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @open-pioneer/ogc-features
|
|
2
2
|
|
|
3
|
-
## 1.3.0
|
|
3
|
+
## 1.3.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
7
|
- 91cf8f1: Derive request CRS from map CRS.
|
|
8
8
|
The `crs` property of the layer configuration for OGC API Features layers is now _optional_.
|
|
9
9
|
- 6f73670: Changes the behavior when no 'attributions' properties is set for OGC API Features layers. In that case, the 'attribution' value provided by the collection metadata is used, if available.
|
|
10
|
-
- d54ccfd: Update to Chakra UI 3.
|
|
11
|
-
- 206b397: Update to trails core packages 4.
|
|
10
|
+
- d54ccfd: Update to Chakra UI 3.35.0
|
|
11
|
+
- 206b397: Update to trails core packages 4.6.0
|
|
12
12
|
|
|
13
13
|
### Patch Changes
|
|
14
14
|
|
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
|
|
4
|
+
"version": "1.3.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.6.0
|
|
18
|
-
"@open-pioneer/http": "4.6.0
|
|
17
|
+
"@open-pioneer/core": "^4.6.0",
|
|
18
|
+
"@open-pioneer/http": "^4.6.0",
|
|
19
19
|
"ol": "^10.9.0",
|
|
20
20
|
"uuid": "^14.0.0"
|
|
21
21
|
},
|
|
@@ -71,6 +71,6 @@
|
|
|
71
71
|
"references": []
|
|
72
72
|
},
|
|
73
73
|
"properties": [],
|
|
74
|
-
"packageFormatVersion": "1.0.
|
|
74
|
+
"packageFormatVersion": "1.0.1"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -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 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,
|
|
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,EAA/B,OAAA;AAAA,EAEpB,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;;;;"}
|
|
@@ -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 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":";;;;AAUA,MAAM,GAAA,GAAM,aAAa,QAAQ,CAAA;AAEjC,MAAM,mBAAA,GAAsB,CAAA;AAC5B,MAAM,aAAA,GAAgB,IAAA;AAoBf,MAAM,cAAA,CAAe;AAAA,EAGxB,YAAoB,OAAA,EAAgC;AAAhC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAChB,IAAA,IAAA,CAAK,WAAA,GAAc,QAAQ,WAAA,IAAe,mBAAA;AAC1C,IAAA,IAAI,IAAA,CAAK,cAAc,CAAA,EAAG;AACtB,MAAA,MAAM,IAAI,KAAA,CAAM,uBAAA,GAA0B,IAAA,CAAK,WAAW,CAAA;AAAA,IAC9D;AAAA,EACJ;AAAA,
|
|
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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"}
|