@open-pioneer/ogc-features 1.0.0 → 1.2.0-dev.20251128143231

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,5 +1,23 @@
1
1
  # @open-pioneer/ogc-features
2
2
 
3
+ ## 1.2.0-dev.20251128143231
4
+
5
+ ### Patch Changes
6
+
7
+ - 279ca67: Use `workspace:*` instead of `workspace:^` for local package references as default. This ensures that trails packages from this repository are always referenced with their exact version to avoid potential issues with version mismatches. If a project specifically wants to use other versions for some trails packages, a pnpm override can be used to force other versions.
8
+
9
+ ## 1.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 10338fa: Update OpenLayers to 10.7.0
14
+ - a8b8a36: Update trails core packages to 4.3.0
15
+ - 10338fa: Update Chakra to 3.29.0
16
+
17
+ ### Patch Changes
18
+
19
+ - fce7fa9: Implement stricter null safety checks.
20
+
3
21
  ## 1.0.0
4
22
 
5
23
  ### Minor Changes
@@ -40,7 +40,7 @@ type AddFeaturesFunc = (features: FeatureLike[]) => void;
40
40
  export interface LoadFeatureOptions {
41
41
  fullURL: string;
42
42
  httpService: HttpService;
43
- featureFormat: FeatureFormat;
43
+ featureFormat: FeatureFormat | null;
44
44
  queryFeatures: QueryFeaturesFunc;
45
45
  addFeatures: AddFeaturesFunc;
46
46
  limit: number;
@@ -61,5 +61,5 @@ export declare function loadFeatures(requestUrl: string, featureFormat: FeatureF
61
61
  *
62
62
  * @internal
63
63
  */
64
- export declare function loadPages(allUrls: string[], featureFormat: FeatureFormat, httpService: HttpService, signal: AbortSignal | undefined, addFeaturesFunc: AddFeaturesFunc, queryFeaturesFunc?: QueryFeaturesFunc): Promise<FeatureResponse>;
64
+ export declare function loadPages(allUrls: string[], featureFormat: FeatureFormat | null, httpService: HttpService, signal: AbortSignal | undefined, addFeaturesFunc: AddFeaturesFunc, queryFeaturesFunc?: QueryFeaturesFunc): Promise<FeatureResponse>;
65
65
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"createVectorSource.js","sources":["createVectorSource.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger, isAbortError } from \"@open-pioneer/core\";\nimport { FeatureLike } from \"ol/Feature\";\nimport { FeatureLoader } from \"ol/featureloader\";\nimport FeatureFormat from \"ol/format/Feature\";\nimport GeoJSON from \"ol/format/GeoJSON\";\nimport { bbox } from \"ol/loadingstrategy\";\nimport VectorSource from \"ol/source/Vector\";\nimport { CollectionInfos, getCollectionInfos, loadAllFeaturesWithOffset } from \"./OffsetStrategy\";\nimport { FeatureResponse, createCollectionRequestUrl, queryFeatures } from \"./requestUtils\";\nimport { OgcFeatureVectorSourceOptions } from \"./api\";\nimport { HttpService } from \"@open-pioneer/http\";\n\nconst LOG = createLogger(\"ogc-features:OgcFeatureSourceFactory\");\nconst DEFAULT_LIMIT = 5000;\nconst DEFAULT_CONCURRENTY = 6;\n\n/**\n * This function creates an OpenLayers VectorSource for OGC API Features services to be used inside\n * an OpenLayers VectorLayer.\n *\n * @param options Options for the vector source.\n * @param httpService Reference to httpService for fetching the features from the service.\n */\nexport function createVectorSource(\n options: OgcFeatureVectorSourceOptions,\n httpService: HttpService\n): VectorSource {\n return _createVectorSource(options, { httpService });\n}\n\n/**\n * @internal\n * Exported for tests\n */\nexport interface InternalOptions {\n httpService: HttpService;\n queryFeaturesParam?: QueryFeaturesFunc | undefined;\n addFeaturesParam?: AddFeaturesFunc | undefined;\n getCollectionInfosParam?: GetCollectionInfosFunc | undefined;\n}\n\n/**\n * @internal\n * Creates the actual vector source.\n * Exported for testing.\n * Exposes `queryFeatures`, `addFeatures` and `getCollectionInfos` for easier testing.\n */\nexport function _createVectorSource(\n options: OgcFeatureVectorSourceOptions,\n internals: InternalOptions\n): VectorSource {\n const httpService = internals.httpService;\n const collectionItemsURL = `${options.baseUrl}/collections/${options.collectionId}/items?`;\n const vectorSrc = new VectorSource({\n format: new GeoJSON(),\n strategy: bbox,\n attributions: options.attributions,\n ...options.additionalOptions\n });\n\n const queryFeaturesFunc = internals.queryFeaturesParam ?? queryFeatures;\n const getCollectionInfosFunc = internals.getCollectionInfosParam ?? getCollectionInfos;\n const addFeaturesFunc =\n internals.addFeaturesParam ||\n function (features: FeatureLike[]) {\n LOG.debug(`Adding ${features.length} features`);\n\n // Type mismatch FeatureLike <--> Feature<Geometry>\n // MIGHT be incorrect! We will see.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n vectorSrc.addFeatures(features as any);\n };\n\n // Abort controller for the currently pending request(s).\n // Used to cancel outdated requests.\n let abortController: AbortController;\n let collectionInfosPromise: Promise<CollectionInfos | undefined> | undefined;\n\n const loaderFunction: FeatureLoader = async (\n extent,\n _,\n __,\n success,\n failure\n ): Promise<void> => {\n collectionInfosPromise ??= getCollectionInfosFunc(collectionItemsURL, httpService);\n let collectionInfos;\n try {\n collectionInfos = await collectionInfosPromise;\n } catch (e) {\n LOG.error(\"Failed to retrieve collection information\", e);\n failure?.();\n collectionInfosPromise = undefined;\n return;\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 abortController?.abort(\"Extent changed\");\n abortController = new AbortController();\n\n const fullURL = createCollectionRequestUrl(\n collectionItemsURL,\n extent,\n options.crs,\n options.rewriteUrl\n );\n\n let strategy =\n options?.strategy || (collectionInfos?.supportsOffsetStrategy ? \"offset\" : \"next\");\n\n if (strategy === \"offset\" && !collectionInfos?.supportsOffsetStrategy) {\n strategy = \"next\";\n }\n\n try {\n const features = await loadAllFeatures(strategy, {\n fullURL: fullURL.toString(),\n httpService: httpService,\n featureFormat: vectorSrc.getFormat()!,\n queryFeatures: queryFeaturesFunc,\n addFeatures: addFeaturesFunc,\n limit: options.limit ?? DEFAULT_LIMIT,\n maxConcurrentRequests: options.maxConcurrentRequests ?? DEFAULT_CONCURRENTY,\n signal: abortController.signal,\n collectionInfos: collectionInfos\n });\n // Type mismatch FeatureLike <--> Feature<Geometry>\n // MIGHT be incorrect! We will see.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n success?.(features as any);\n LOG.debug(\"Finished loading features for extent:\", extent);\n } catch (e) {\n if (!isAbortError(e)) {\n LOG.error(\"Failed to load features\", e);\n } else {\n LOG.debug(\"Query-Feature-Request aborted\", e);\n vectorSrc.removeLoadedExtent(extent);\n failure?.();\n }\n }\n };\n vectorSrc.setLoader(loaderFunction);\n return vectorSrc;\n}\n\n/** @internal **/\ntype QueryFeaturesFunc = typeof queryFeatures;\n/** @internal **/\ntype GetCollectionInfosFunc = typeof getCollectionInfos;\n/** @internal **/\ntype AddFeaturesFunc = (features: FeatureLike[]) => void;\n\n/** @internal **/\nexport interface LoadFeatureOptions {\n fullURL: string;\n httpService: HttpService;\n featureFormat: FeatureFormat;\n queryFeatures: QueryFeaturesFunc;\n addFeatures: AddFeaturesFunc;\n limit: number;\n maxConcurrentRequests: number;\n signal?: AbortSignal;\n collectionInfos?: CollectionInfos;\n}\n\n/**\n * @internal\n * Fetches _all_ features according to the given strategy.\n */\nfunction loadAllFeatures(\n strategy: \"next\" | \"offset\",\n options: LoadFeatureOptions\n): Promise<FeatureLike[]> {\n switch (strategy) {\n case \"next\":\n return loadAllFeaturesNextStrategy(options);\n case \"offset\":\n return loadAllFeaturesWithOffset(options);\n }\n}\n\n/**\n * @internal\n * Fetches features by following the `next` links in the server's response.\n */\nexport async function loadAllFeaturesNextStrategy(\n options: Omit<LoadFeatureOptions, \"offsetRequestProps\" | \"collectionInfos\">\n): Promise<FeatureLike[]> {\n const limit = options.limit;\n\n let url = new URL(options.fullURL);\n url.searchParams.set(\"limit\", limit.toString());\n let allFeatures: FeatureLike[] = [];\n do {\n const featureResp = await loadPages(\n [url.toString()],\n options.featureFormat,\n options.httpService,\n options.signal,\n options.addFeatures,\n options.queryFeatures\n );\n\n allFeatures = allFeatures.concat(featureResp.features);\n if (!featureResp.nextURL) {\n break;\n }\n\n url = new URL(featureResp.nextURL);\n // eslint-disable-next-line no-constant-condition\n } while (1);\n return allFeatures;\n}\n\nexport async function loadFeatures(\n requestUrl: string,\n featureFormat: FeatureFormat,\n httpService: HttpService,\n signal: AbortSignal | undefined,\n addFeaturesFunc: AddFeaturesFunc,\n queryFeaturesFunc: QueryFeaturesFunc = queryFeatures\n): Promise<FeatureResponse> {\n const featureResponse = await queryFeaturesFunc(requestUrl, featureFormat, httpService, signal);\n const features = featureResponse.features as FeatureLike[];\n addFeaturesFunc(features);\n return featureResponse;\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 * @internal\n */\nexport async function loadPages(\n allUrls: string[],\n featureFormat: FeatureFormat,\n httpService: HttpService,\n signal: AbortSignal | undefined,\n addFeaturesFunc: AddFeaturesFunc,\n queryFeaturesFunc: QueryFeaturesFunc = queryFeatures\n): Promise<FeatureResponse> {\n const allFeatureResponse: FeatureResponse = {\n nextURL: 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 featureResponse = await queryFeaturesFunc(\n singleUrl,\n featureFormat,\n httpService,\n signal\n );\n addFeaturesFunc(featureResponse.features as FeatureLike[]);\n\n LOG.debug(\n `NextURL for index = ${index} (isLast = ${isLast}): ${\n featureResponse.nextURL || \"No Next URL\"\n }`\n );\n allFeatureResponse.features.push(...featureResponse.features);\n if (isLast) {\n allFeatureResponse.numberMatched = featureResponse.numberMatched;\n allFeatureResponse.nextURL = featureResponse.nextURL;\n }\n });\n await Promise.all(allRequestPromises);\n return allFeatureResponse;\n}\n"],"names":[],"mappings":";;;;;;;AAcA,MAAM,GAAA,GAAM,aAAa,sCAAsC,CAAA;AAC/D,MAAM,aAAA,GAAgB,GAAA;AACtB,MAAM,mBAAA,GAAsB,CAAA;AASrB,SAAS,kBAAA,CACZ,SACA,WAAA,EACY;AACZ,EAAA,OAAO,mBAAA,CAAoB,OAAA,EAAS,EAAE,WAAA,EAAa,CAAA;AACvD;AAmBO,SAAS,mBAAA,CACZ,SACA,SAAA,EACY;AACZ,EAAA,MAAM,cAAc,SAAA,CAAU,WAAA;AAC9B,EAAA,MAAM,qBAAqB,CAAA,EAAG,OAAA,CAAQ,OAAO,CAAA,aAAA,EAAgB,QAAQ,YAAY,CAAA,OAAA,CAAA;AACjF,EAAA,MAAM,SAAA,GAAY,IAAI,YAAA,CAAa;AAAA,IAC/B,MAAA,EAAQ,IAAI,OAAA,EAAQ;AAAA,IACpB,QAAA,EAAU,IAAA;AAAA,IACV,cAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,GAAG,OAAA,CAAQ;AAAA,GACd,CAAA;AAED,EAAA,MAAM,iBAAA,GAAoB,UAAU,kBAAA,IAAsB,aAAA;AAC1D,EAAA,MAAM,sBAAA,GAAyB,UAAU,uBAAA,IAA2B,kBAAA;AACpE,EAAA,MAAM,eAAA,GACF,SAAA,CAAU,gBAAA,IACV,SAAU,QAAA,EAAyB;AAC/B,IAAA,GAAA,CAAI,KAAA,CAAM,CAAA,OAAA,EAAU,QAAA,CAAS,MAAM,CAAA,SAAA,CAAW,CAAA;AAK9C,IAAA,SAAA,CAAU,YAAY,QAAe,CAAA;AAAA,EACzC,CAAA;AAIJ,EAAA,IAAI,eAAA;AACJ,EAAA,IAAI,sBAAA;AAEJ,EAAA,MAAM,iBAAgC,OAClC,MAAA,EACA,CAAA,EACA,EAAA,EACA,SACA,OAAA,KACgB;AAChB,IAAA,sBAAA,KAA2B,sBAAA,CAAuB,oBAAoB,WAAW,CAAA;AACjF,IAAA,IAAI,eAAA;AACJ,IAAA,IAAI;AACA,MAAA,eAAA,GAAkB,MAAM,sBAAA;AAAA,IAC5B,SAAS,CAAA,EAAG;AACR,MAAA,GAAA,CAAI,KAAA,CAAM,6CAA6C,CAAC,CAAA;AACxD,MAAA,OAAA,IAAU;AACV,MAAA,sBAAA,GAAyB,MAAA;AACzB,MAAA;AAAA,IACJ;AAKA,IAAA,eAAA,EAAiB,MAAM,gBAAgB,CAAA;AACvC,IAAA,eAAA,GAAkB,IAAI,eAAA,EAAgB;AAEtC,IAAA,MAAM,OAAA,GAAU,0BAAA;AAAA,MACZ,kBAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA,CAAQ,GAAA;AAAA,MACR,OAAA,CAAQ;AAAA,KACZ;AAEA,IAAA,IAAI,QAAA,GACA,OAAA,EAAS,QAAA,KAAa,eAAA,EAAiB,yBAAyB,QAAA,GAAW,MAAA,CAAA;AAE/E,IAAA,IAAI,QAAA,KAAa,QAAA,IAAY,CAAC,eAAA,EAAiB,sBAAA,EAAwB;AACnE,MAAA,QAAA,GAAW,MAAA;AAAA,IACf;AAEA,IAAA,IAAI;AACA,MAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,QAAA,EAAU;AAAA,QAC7C,OAAA,EAAS,QAAQ,QAAA,EAAS;AAAA,QAC1B,WAAA;AAAA,QACA,aAAA,EAAe,UAAU,SAAA,EAAU;AAAA,QACnC,aAAA,EAAe,iBAAA;AAAA,QACf,WAAA,EAAa,eAAA;AAAA,QACb,KAAA,EAAO,QAAQ,KAAA,IAAS,aAAA;AAAA,QACxB,qBAAA,EAAuB,QAAQ,qBAAA,IAAyB,mBAAA;AAAA,QACxD,QAAQ,eAAA,CAAgB,MAAA;AAAA,QACxB;AAAA,OACH,CAAA;AAID,MAAA,OAAA,GAAU,QAAe,CAAA;AACzB,MAAA,GAAA,CAAI,KAAA,CAAM,yCAAyC,MAAM,CAAA;AAAA,IAC7D,SAAS,CAAA,EAAG;AACR,MAAA,IAAI,CAAC,YAAA,CAAa,CAAC,CAAA,EAAG;AAClB,QAAA,GAAA,CAAI,KAAA,CAAM,2BAA2B,CAAC,CAAA;AAAA,MAC1C,CAAA,MAAO;AACH,QAAA,GAAA,CAAI,KAAA,CAAM,iCAAiC,CAAC,CAAA;AAC5C,QAAA,SAAA,CAAU,mBAAmB,MAAM,CAAA;AACnC,QAAA,OAAA,IAAU;AAAA,MACd;AAAA,IACJ;AAAA,EACJ,CAAA;AACA,EAAA,SAAA,CAAU,UAAU,cAAc,CAAA;AAClC,EAAA,OAAO,SAAA;AACX;AA0BA,SAAS,eAAA,CACL,UACA,OAAA,EACsB;AACtB,EAAA,QAAQ,QAAA;AAAU,IACd,KAAK,MAAA;AACD,MAAA,OAAO,4BAA4B,OAAO,CAAA;AAAA,IAC9C,KAAK,QAAA;AACD,MAAA,OAAO,0BAA0B,OAAO,CAAA;AAAA;AAEpD;AAMA,eAAsB,4BAClB,OAAA,EACsB;AACtB,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA;AAEtB,EAAA,IAAI,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,OAAO,CAAA;AACjC,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,KAAA,CAAM,UAAU,CAAA;AAC9C,EAAA,IAAI,cAA6B,EAAC;AAClC,EAAA,GAAG;AACC,IAAA,MAAM,cAAc,MAAM,SAAA;AAAA,MACtB,CAAC,GAAA,CAAI,QAAA,EAAU,CAAA;AAAA,MACf,OAAA,CAAQ,aAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,OAAA,CAAQ;AAAA,KACZ;AAEA,IAAA,WAAA,GAAc,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,QAAQ,CAAA;AACrD,IAAA,IAAI,CAAC,YAAY,OAAA,EAAS;AACtB,MAAA;AAAA,IACJ;AAEA,IAAA,GAAA,GAAM,IAAI,GAAA,CAAI,WAAA,CAAY,OAAO,CAAA;AAAA,EAErC,CAAA,QAAS,CAAA;AACT,EAAA,OAAO,WAAA;AACX;AAuBA,eAAsB,UAClB,OAAA,EACA,aAAA,EACA,aACA,MAAA,EACA,eAAA,EACA,oBAAuC,aAAA,EACf;AACxB,EAAA,MAAM,kBAAA,GAAsC;AAAA,IACxC,OAAA,EAAS,MAAA;AAAA,IACT,aAAA,EAAe,MAAA;AAAA,IACf,UAAU;AAAC,GACf;AACA,EAAA,MAAM,kBAAA,GAAqB,OAAA,CAAQ,GAAA,CAAI,OAAO,WAAW,KAAA,KAAyB;AAC9E,IAAA,MAAM,MAAA,GAAS,KAAA,KAAU,OAAA,CAAQ,MAAA,GAAS,CAAA;AAE1C,IAAA,MAAM,kBAAkB,MAAM,iBAAA;AAAA,MAC1B,SAAA;AAAA,MACA,aAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,KACJ;AACA,IAAA,eAAA,CAAgB,gBAAgB,QAAyB,CAAA;AAEzD,IAAA,GAAA,CAAI,KAAA;AAAA,MACA,uBAAuB,KAAK,CAAA,WAAA,EAAc,MAAM,CAAA,GAAA,EAC5C,eAAA,CAAgB,WAAW,aAC/B,CAAA;AAAA,KACJ;AACA,IAAA,kBAAA,CAAmB,QAAA,CAAS,IAAA,CAAK,GAAG,eAAA,CAAgB,QAAQ,CAAA;AAC5D,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,kBAAA,CAAmB,gBAAgB,eAAA,CAAgB,aAAA;AACnD,MAAA,kBAAA,CAAmB,UAAU,eAAA,CAAgB,OAAA;AAAA,IACjD;AAAA,EACJ,CAAC,CAAA;AACD,EAAA,MAAM,OAAA,CAAQ,IAAI,kBAAkB,CAAA;AACpC,EAAA,OAAO,kBAAA;AACX;;;;"}
1
+ {"version":3,"file":"createVectorSource.js","sources":["createVectorSource.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger, isAbortError } from \"@open-pioneer/core\";\nimport { FeatureLike } from \"ol/Feature\";\nimport { FeatureLoader } from \"ol/featureloader\";\nimport FeatureFormat from \"ol/format/Feature\";\nimport GeoJSON from \"ol/format/GeoJSON\";\nimport { bbox } from \"ol/loadingstrategy\";\nimport VectorSource from \"ol/source/Vector\";\nimport { CollectionInfos, getCollectionInfos, loadAllFeaturesWithOffset } from \"./OffsetStrategy\";\nimport { FeatureResponse, createCollectionRequestUrl, queryFeatures } from \"./requestUtils\";\nimport { OgcFeatureVectorSourceOptions } from \"./api\";\nimport { HttpService } from \"@open-pioneer/http\";\n\nconst LOG = createLogger(\"ogc-features:OgcFeatureSourceFactory\");\nconst DEFAULT_LIMIT = 5000;\nconst DEFAULT_CONCURRENTY = 6;\n\n/**\n * This function creates an OpenLayers VectorSource for OGC API Features services to be used inside\n * an OpenLayers VectorLayer.\n *\n * @param options Options for the vector source.\n * @param httpService Reference to httpService for fetching the features from the service.\n */\nexport function createVectorSource(\n options: OgcFeatureVectorSourceOptions,\n httpService: HttpService\n): VectorSource {\n return _createVectorSource(options, { httpService });\n}\n\n/**\n * @internal\n * Exported for tests\n */\nexport interface InternalOptions {\n httpService: HttpService;\n queryFeaturesParam?: QueryFeaturesFunc | undefined;\n addFeaturesParam?: AddFeaturesFunc | undefined;\n getCollectionInfosParam?: GetCollectionInfosFunc | undefined;\n}\n\n/**\n * @internal\n * Creates the actual vector source.\n * Exported for testing.\n * Exposes `queryFeatures`, `addFeatures` and `getCollectionInfos` for easier testing.\n */\nexport function _createVectorSource(\n options: OgcFeatureVectorSourceOptions,\n internals: InternalOptions\n): VectorSource {\n const httpService = internals.httpService;\n const collectionItemsURL = `${options.baseUrl}/collections/${options.collectionId}/items?`;\n const vectorSrc = new VectorSource({\n format: new GeoJSON(),\n strategy: bbox,\n attributions: options.attributions,\n ...options.additionalOptions\n });\n\n const queryFeaturesFunc = internals.queryFeaturesParam ?? queryFeatures;\n const getCollectionInfosFunc = internals.getCollectionInfosParam ?? getCollectionInfos;\n const addFeaturesFunc =\n internals.addFeaturesParam ||\n function (features: FeatureLike[]) {\n LOG.debug(`Adding ${features.length} features`);\n\n // Type mismatch FeatureLike <--> Feature<Geometry>\n // MIGHT be incorrect! We will see.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n vectorSrc.addFeatures(features as any);\n };\n\n // Abort controller for the currently pending request(s).\n // Used to cancel outdated requests.\n let abortController: AbortController;\n let collectionInfosPromise: Promise<CollectionInfos | undefined> | undefined;\n\n const loaderFunction: FeatureLoader = async (\n extent,\n _,\n __,\n success,\n failure\n ): Promise<void> => {\n collectionInfosPromise ??= getCollectionInfosFunc(collectionItemsURL, httpService);\n let collectionInfos;\n try {\n collectionInfos = await collectionInfosPromise;\n } catch (e) {\n LOG.error(\"Failed to retrieve collection information\", e);\n failure?.();\n collectionInfosPromise = undefined;\n return;\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 abortController?.abort(\"Extent changed\");\n abortController = new AbortController();\n\n const fullURL = createCollectionRequestUrl(\n collectionItemsURL,\n extent,\n options.crs,\n options.rewriteUrl\n );\n\n let strategy =\n options?.strategy || (collectionInfos?.supportsOffsetStrategy ? \"offset\" : \"next\");\n\n if (strategy === \"offset\" && !collectionInfos?.supportsOffsetStrategy) {\n strategy = \"next\";\n }\n\n try {\n const features = await loadAllFeatures(strategy, {\n fullURL: fullURL.toString(),\n httpService: httpService,\n featureFormat: vectorSrc.getFormat(),\n queryFeatures: queryFeaturesFunc,\n addFeatures: addFeaturesFunc,\n limit: options.limit ?? DEFAULT_LIMIT,\n maxConcurrentRequests: options.maxConcurrentRequests ?? DEFAULT_CONCURRENTY,\n signal: abortController.signal,\n collectionInfos: collectionInfos\n });\n // Type mismatch FeatureLike <--> Feature<Geometry>\n // MIGHT be incorrect! We will see.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n success?.(features as any);\n LOG.debug(\"Finished loading features for extent:\", extent);\n } catch (e) {\n if (!isAbortError(e)) {\n LOG.error(\"Failed to load features\", e);\n } else {\n LOG.debug(\"Query-Feature-Request aborted\", e);\n vectorSrc.removeLoadedExtent(extent);\n failure?.();\n }\n }\n };\n vectorSrc.setLoader(loaderFunction);\n return vectorSrc;\n}\n\n/** @internal **/\ntype QueryFeaturesFunc = typeof queryFeatures;\n/** @internal **/\ntype GetCollectionInfosFunc = typeof getCollectionInfos;\n/** @internal **/\ntype AddFeaturesFunc = (features: FeatureLike[]) => void;\n\n/** @internal **/\nexport interface LoadFeatureOptions {\n fullURL: string;\n httpService: HttpService;\n featureFormat: FeatureFormat | null;\n queryFeatures: QueryFeaturesFunc;\n addFeatures: AddFeaturesFunc;\n limit: number;\n maxConcurrentRequests: number;\n signal?: AbortSignal;\n collectionInfos?: CollectionInfos;\n}\n\n/**\n * @internal\n * Fetches _all_ features according to the given strategy.\n */\nfunction loadAllFeatures(\n strategy: \"next\" | \"offset\",\n options: LoadFeatureOptions\n): Promise<FeatureLike[]> {\n switch (strategy) {\n case \"next\":\n return loadAllFeaturesNextStrategy(options);\n case \"offset\":\n return loadAllFeaturesWithOffset(options);\n }\n}\n\n/**\n * @internal\n * Fetches features by following the `next` links in the server's response.\n */\nexport async function loadAllFeaturesNextStrategy(\n options: Omit<LoadFeatureOptions, \"offsetRequestProps\" | \"collectionInfos\">\n): Promise<FeatureLike[]> {\n const limit = options.limit;\n\n let url = new URL(options.fullURL);\n url.searchParams.set(\"limit\", limit.toString());\n let allFeatures: FeatureLike[] = [];\n do {\n const featureResp = await loadPages(\n [url.toString()],\n options.featureFormat,\n options.httpService,\n options.signal,\n options.addFeatures,\n options.queryFeatures\n );\n\n allFeatures = allFeatures.concat(featureResp.features);\n if (!featureResp.nextURL) {\n break;\n }\n\n url = new URL(featureResp.nextURL);\n // eslint-disable-next-line no-constant-condition\n } while (1);\n return allFeatures;\n}\n\nexport async function loadFeatures(\n requestUrl: string,\n featureFormat: FeatureFormat,\n httpService: HttpService,\n signal: AbortSignal | undefined,\n addFeaturesFunc: AddFeaturesFunc,\n queryFeaturesFunc: QueryFeaturesFunc = queryFeatures\n): Promise<FeatureResponse> {\n const featureResponse = await queryFeaturesFunc(requestUrl, featureFormat, httpService, signal);\n const features = featureResponse.features as FeatureLike[];\n addFeaturesFunc(features);\n return featureResponse;\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 * @internal\n */\nexport async function loadPages(\n allUrls: string[],\n featureFormat: FeatureFormat | null,\n httpService: HttpService,\n signal: AbortSignal | undefined,\n addFeaturesFunc: AddFeaturesFunc,\n queryFeaturesFunc: QueryFeaturesFunc = queryFeatures\n): Promise<FeatureResponse> {\n const allFeatureResponse: FeatureResponse = {\n nextURL: 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 featureResponse = await queryFeaturesFunc(\n singleUrl,\n featureFormat,\n httpService,\n signal\n );\n addFeaturesFunc(featureResponse.features as FeatureLike[]);\n\n LOG.debug(\n `NextURL for index = ${index} (isLast = ${isLast}): ${\n featureResponse.nextURL || \"No Next URL\"\n }`\n );\n allFeatureResponse.features.push(...featureResponse.features);\n if (isLast) {\n allFeatureResponse.numberMatched = featureResponse.numberMatched;\n allFeatureResponse.nextURL = featureResponse.nextURL;\n }\n });\n await Promise.all(allRequestPromises);\n return allFeatureResponse;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@open-pioneer/ogc-features",
4
- "version": "1.0.0",
4
+ "version": "1.2.0-dev.20251128143231",
5
5
  "description": "This package provides utilities to work with OGC API Features services.",
6
6
  "keywords": [
7
7
  "open-pioneer-trails"
@@ -14,9 +14,9 @@
14
14
  "directory": "src/packages/ogc-features"
15
15
  },
16
16
  "dependencies": {
17
- "@open-pioneer/core": "^4.2.0",
18
- "@open-pioneer/http": "^4.2.0",
19
- "ol": "^10.6.1",
17
+ "@open-pioneer/core": "^4.3.0",
18
+ "@open-pioneer/http": "^4.3.0",
19
+ "ol": "^10.7.0",
20
20
  "uuid": "^13.0.0"
21
21
  },
22
22
  "exports": {
package/requestUtils.d.ts CHANGED
@@ -22,4 +22,4 @@ export interface FeatureResponse {
22
22
  /**
23
23
  * Performs a single request against the service
24
24
  */
25
- export declare function queryFeatures(fullURL: string, featureFormat: FeatureFormat | undefined, httpService: HttpService, signal: AbortSignal | undefined): Promise<FeatureResponse>;
25
+ export declare function queryFeatures(fullURL: string, featureFormat: FeatureFormat | null, httpService: HttpService, signal: AbortSignal | undefined): Promise<FeatureResponse>;
@@ -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 { Extent } from \"ol/extent\";\nimport FeatureFormat from \"ol/format/Feature\";\nimport { FeatureLike } from \"ol/Feature\";\nimport { HttpService } from \"@open-pioneer/http\";\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 rewriteUrl?: (url: URL) => URL | undefined\n): URL {\n const urlObj = new URL(collectionItemsURL);\n const searchParams = urlObj.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 rewriteUrl?.(new URL(urlObj)) ?? urlObj;\n}\n\n/**\n * Adds (or replaces) offset/limit params on the given url.\n */\nexport function createOffsetURL(fullURL: string, offset: number, pageSize: number): string {\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.toString();\n}\n\n/**\n * Extracts the `next` link from the service response's `links` property.\n */\nexport function getNextURL(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: FeatureLike[];\n nextURL: string | undefined;\n numberMatched: number | undefined;\n}\n\n/**\n * Performs a single request against the service\n */\nexport async function queryFeatures(\n fullURL: string,\n featureFormat: FeatureFormat | undefined,\n httpService: HttpService,\n signal: AbortSignal | undefined\n): Promise<FeatureResponse> {\n let features: FeatureLike[] = [];\n const requestInit: RequestInit = {\n headers: {\n Accept: \"application/geo+json\"\n },\n signal\n };\n const response = await httpService.fetch(fullURL, requestInit);\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 if (featureFormat) {\n features = featureFormat.readFeatures(geoJson);\n }\n const nextURL = getNextURL(geoJson.links);\n return {\n features: features,\n numberMatched: geoJson.numberMatched,\n nextURL: nextURL\n };\n}\n"],"names":[],"mappings":"AAOA,MAAM,cAAA,GAAiB,MAAA;AAKhB,SAAS,0BAAA,CACZ,kBAAA,EACA,MAAA,EACA,GAAA,EACA,UAAA,EACG;AACH,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,kBAAkB,CAAA;AACzC,EAAA,MAAM,eAAe,MAAA,CAAO,YAAA;AAC5B,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,UAAA,GAAa,IAAI,GAAA,CAAI,MAAM,CAAC,CAAA,IAAK,MAAA;AAC5C;AAKO,SAAS,eAAA,CAAgB,OAAA,EAAiB,MAAA,EAAgB,QAAA,EAA0B;AACvF,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAO,CAAA;AAC3B,EAAA,MAAM,eAAe,GAAA,CAAI,YAAA;AACzB,EAAA,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,QAAA,EAAU,CAAA;AAC5C,EAAA,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,QAAA,CAAS,QAAA,EAAU,CAAA;AAC7C,EAAA,OAAO,IAAI,QAAA,EAAS;AACxB;AAKO,SAAS,WAAW,QAAA,EAAuC;AAC9D,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,IAAI,WAA0B,EAAC;AAC/B,EAAA,MAAM,WAAA,GAA2B;AAAA,IAC7B,OAAA,EAAS;AAAA,MACL,MAAA,EAAQ;AAAA,KACZ;AAAA,IACA;AAAA,GACJ;AACA,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,KAAA,CAAM,SAAS,WAAW,CAAA;AAC7D,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,IAAI,aAAA,EAAe;AACf,IAAA,QAAA,GAAW,aAAA,CAAc,aAAa,OAAO,CAAA;AAAA,EACjD;AACA,EAAA,MAAM,OAAA,GAAU,UAAA,CAAW,OAAA,CAAQ,KAAK,CAAA;AACxC,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA,eAAe,OAAA,CAAQ,aAAA;AAAA,IACvB;AAAA,GACJ;AACJ;;;;"}
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 { Extent } from \"ol/extent\";\nimport FeatureFormat from \"ol/format/Feature\";\nimport { FeatureLike } from \"ol/Feature\";\nimport { HttpService } from \"@open-pioneer/http\";\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 rewriteUrl?: (url: URL) => URL | undefined\n): URL {\n const urlObj = new URL(collectionItemsURL);\n const searchParams = urlObj.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 rewriteUrl?.(new URL(urlObj)) ?? urlObj;\n}\n\n/**\n * Adds (or replaces) offset/limit params on the given url.\n */\nexport function createOffsetURL(fullURL: string, offset: number, pageSize: number): string {\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.toString();\n}\n\n/**\n * Extracts the `next` link from the service response's `links` property.\n */\nexport function getNextURL(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: FeatureLike[];\n nextURL: string | undefined;\n numberMatched: number | undefined;\n}\n\n/**\n * Performs a single request against the service\n */\nexport async function queryFeatures(\n fullURL: string,\n featureFormat: FeatureFormat | null,\n httpService: HttpService,\n signal: AbortSignal | undefined\n): Promise<FeatureResponse> {\n let features: FeatureLike[] = [];\n const requestInit: RequestInit = {\n headers: {\n Accept: \"application/geo+json\"\n },\n signal\n };\n const response = await httpService.fetch(fullURL, requestInit);\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 if (featureFormat) {\n features = featureFormat.readFeatures(geoJson);\n }\n const nextURL = getNextURL(geoJson.links);\n return {\n features: features,\n numberMatched: geoJson.numberMatched,\n nextURL: nextURL\n };\n}\n"],"names":[],"mappings":"AAOA,MAAM,cAAA,GAAiB,MAAA;AAKhB,SAAS,0BAAA,CACZ,kBAAA,EACA,MAAA,EACA,GAAA,EACA,UAAA,EACG;AACH,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,kBAAkB,CAAA;AACzC,EAAA,MAAM,eAAe,MAAA,CAAO,YAAA;AAC5B,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,UAAA,GAAa,IAAI,GAAA,CAAI,MAAM,CAAC,CAAA,IAAK,MAAA;AAC5C;AAKO,SAAS,eAAA,CAAgB,OAAA,EAAiB,MAAA,EAAgB,QAAA,EAA0B;AACvF,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAO,CAAA;AAC3B,EAAA,MAAM,eAAe,GAAA,CAAI,YAAA;AACzB,EAAA,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,QAAA,EAAU,CAAA;AAC5C,EAAA,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,QAAA,CAAS,QAAA,EAAU,CAAA;AAC7C,EAAA,OAAO,IAAI,QAAA,EAAS;AACxB;AAKO,SAAS,WAAW,QAAA,EAAuC;AAC9D,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,IAAI,WAA0B,EAAC;AAC/B,EAAA,MAAM,WAAA,GAA2B;AAAA,IAC7B,OAAA,EAAS;AAAA,MACL,MAAA,EAAQ;AAAA,KACZ;AAAA,IACA;AAAA,GACJ;AACA,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,KAAA,CAAM,SAAS,WAAW,CAAA;AAC7D,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,IAAI,aAAA,EAAe;AACf,IAAA,QAAA,GAAW,aAAA,CAAc,aAAa,OAAO,CAAA;AAAA,EACjD;AACA,EAAA,MAAM,OAAA,GAAU,UAAA,CAAW,OAAA,CAAQ,KAAK,CAAA;AACxC,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA,eAAe,OAAA,CAAQ,aAAA;AAAA,IACvB;AAAA,GACJ;AACJ;;;;"}