Skip to content

Commit

Permalink
OGC-Features Vector Source: customize strategy and the URL used to lo…
Browse files Browse the repository at this point in the history
…ad features (#328)

Co-authored-by: Michael Beckemeyer <m.beckemeyer@conterra.de>
Co-authored-by: Antonia van Eek <a.vaneek@conterra.de>
  • Loading branch information
3 people committed Jun 18, 2024
1 parent d025602 commit 1efd592
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 29 deletions.
3 changes: 3 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ strict-peer-dependencies=true
# Workaround for current problems regarding our vite plugin + vite's optimizeDeps feature
shamefully-hoist=true

# Limits the path length of the virtual store directory (Default is 120).
# This is mostly helpful for windows users, where long paths can cause issues.
virtual-store-dir-max-length=60
2 changes: 1 addition & 1 deletion src/packages/ogc-features/OgcFeatureSearchSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async function fetchJson(
const response = await httpService.fetch(url, {
signal,
headers: {
"Accept": "application/json"
Accept: "application/json"
}
});
if (!response.ok) {
Expand Down
80 changes: 59 additions & 21 deletions src/packages/ogc-features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,36 +35,74 @@ const vectorLayer = new VectorLayer({
baseUrl: "https://ogc-api.nrw.de/inspire-us-kindergarten/v1",
collectionId: "governmentalservice",
crs: "http://www.opengis.net/def/crs/EPSG/0/25832",

/**
* The maximum number of features to fetch within a single request.
* Corresponds to the `limit` parameter in the URL.
*
* When the `offset` strategy is used for feature fetching, the limit
* is used for the page size.
*
* Defaults to `5000`.
*/
limit: 5000,

/** The maximum number of concurrent requests. Defaults to `6`. */
maxConcurrentRequests: 6,

attributions:
"<a href='https://www.govdata.de/dl-de/by-2-0'>Datenlizenz Deutschland - Namensnennung - Version 2.0</a>",

additionalOptions: {} // (Optional)
/** Optional: number of features loaded per request. */
limit: 5000,

/** Optional: passed to the Open Layers Vector Source constructor. */
additionalOptions: {},
})
});
```

The optional `limit` configures the concurrent execution of requests by using the `offset` URL property for pagination.
If the service returns a `numberMatched` property together with its results, it is used alongside the configured pageSize to calculate the optimal number of concurrent requests.
The number of concurrent requests is never higher than `maxConcurrentRequests`.

Additional options of the `VectorSource` (see [OpenLayers documentation](https://openlayers.org/en/latest/apidoc/module-ol_source_Vector-VectorSource.html)) can be given by the property
`additionalOptions`.

#### Loading strategies

The vector source supports two different strategies to load features from the server:

- `"next"`: Fetch large feature results by walking the `next` link of the previous response.
This is well supported by most implementations, but can be slow for very large result sets
because it does not allow for parallel requests.
- `"offset"`: Fetch large feature results using parallel requests.
Each request fetches a page of results using an `"offset"` and `"limit"` parameter.
This can be much faster than the `"next"` strategy, but it is not supported by all server implementations.

By default, the vector source will attempt to detect the server's capabilities and will prefer `"offset"`, if supported.
You can overwrite the default behavior by explicitly defining the `strategy` option.

If the `"offset"` strategy is used, you can configure the maximum number of concurrent requests (default: 6).

Example:

```ts
vectorSourceFactory.createVectorSource({
baseUrl: "https://ogc-api.nrw.de/inspire-us-kindergarten/v1",
collectionId: "governmentalservice",
crs: "http://www.opengis.net/def/crs/EPSG/0/25832",

strategy: "offset",
limit: 2500,
maxConcurrentRequests: 6
});
```

#### Rewriting request URLs

The optional `rewriteUrl` option can be used to modify the feature requests made by the vector source.
This is useful, for example, to filter the OGC service on the server side.

Note that modifying the vector source's URL requires some care: existing query parameters should not be overwritten unless you know what you're doing.
The vector source may add additional query parameters in the future, which might conflict the changes done by custom `rewriteUrl` implementations.

Example:

```ts
vectorSourceFactory.createVectorSource({
baseUrl: "https://ogc-api.nrw.de/inspire-us-kindergarten/v1",
collectionId: "governmentalservice",
crs: "http://www.opengis.net/def/crs/EPSG/0/25832",

rewriteUrl(url) {
url.searchParams.set("property", "value");
return url;
}
});
```

### Search source

This search source is used to search in OGC API features.
Expand All @@ -87,7 +125,7 @@ export default defineBuildConfig({
});
```

and create a search search instance:
and create a search source instance:

```ts
const ogcSearchSourceFactory = ...; // injected
Expand Down
33 changes: 32 additions & 1 deletion src/packages/ogc-features/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ import { Geometry } from "ol/geom";
import { AttributionLike } from "ol/source/Source";
import VectorSource, { Options } from "ol/source/Vector";

/**
* The strategy to fetch features from an OGC API Features service.
*
* - `"next"`: Fetch large feature results by walking the `next` link of the previous response.
* This is well supported by most implementations, but can be slow for very large result sets
* because it does not allow for parallel requests.
* - `"offset"`: Fetch large feature results using parallel requests.
* Each request fetches a page of results using an `"offset"` and `"limit"` parameter.
* This can be much faster than the `"next"` strategy, but it is not supported by all server implementations.
*/
export type OgcFetchStrategy = "next" | "offset";

/**
* These are properties for OGC API Features vector source.
*/
Expand Down Expand Up @@ -40,6 +52,25 @@ export interface OgcFeatureVectorSourceOptions {

/** Optional additional options for the VectorSource. */
additionalOptions?: Options<Feature<Geometry>>;

/**
* Use this property to define the feature fetching strategy.
* Allowed value are `offset` and `next`.
* By default, the vector source attempts to detect the server's capabilities and will prefer `"offset"`, if possible.
*/
strategy?: OgcFetchStrategy;

/**
* Use this function to rewrite the URL used to fetch features from the OGC API Features service.
* This is useful, for example, to filter the OGC service on the server side.
*
* NOTE: Do not update the `url` argument. Return a new `URL` instance instead.
*
* NOTE: Be careful with existing URL parameters. The vector source may not work correctly if
* predefined parameters (such as the CRS or the response format) are overwritten.
* The vector source might add additional parameters to its request URLs in the future.
*/
rewriteUrl?: (url: URL) => URL | undefined;
}

/**
Expand All @@ -56,7 +87,7 @@ export interface OgcFeaturesVectorSourceFactory
createVectorSource(options: OgcFeatureVectorSourceOptions): VectorSource;
}

/** Options for {@link OgcFeatureSearchSource}. */
/** Options for {@link OgcFeaturesVectorSourceFactory.createVectorSource()}. */
export interface OgcFeatureSearchSourceOptions {
/** The source's label. May be used as a title for results from this source. */
label: string;
Expand Down
16 changes: 14 additions & 2 deletions src/packages/ogc-features/createVectorSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,20 @@ export function _createVectorSource(
abortController?.abort("Extent changed");
abortController = new AbortController();

const fullURL = createCollectionRequestUrl(collectionItemsURL, extent, options.crs);
const strategy = collectionInfos?.supportsOffsetStrategy ? "offset" : "next";
const fullURL = createCollectionRequestUrl(
collectionItemsURL,
extent,
options.crs,
options.rewriteUrl
);

let strategy =
options?.strategy || (collectionInfos?.supportsOffsetStrategy ? "offset" : "next");

if (strategy === "offset" && !collectionInfos?.supportsOffsetStrategy) {
strategy = "next";
}

try {
const features = await loadAllFeatures(strategy, {
fullURL: fullURL.toString(),
Expand Down
5 changes: 3 additions & 2 deletions src/packages/ogc-features/requestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ const NEXT_LINK_PROP = "next";
export function createCollectionRequestUrl(
collectionItemsURL: string,
extent: Extent,
crs: string
crs: string,
rewriteUrl?: (url: URL) => URL | undefined
): URL {
const urlObj = new URL(collectionItemsURL);
const searchParams = urlObj.searchParams;
searchParams.set("bbox", extent.join(","));
searchParams.set("bbox-crs", crs);
searchParams.set("crs", crs);
searchParams.set("f", "json");
return urlObj;
return rewriteUrl?.(new URL(urlObj)) ?? urlObj;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/packages/selection/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export interface VectorLayerSelectionSource extends Required<SelectionSource>, R
export interface VectorLayerSelectionSourceFactory
extends DeclaredService<"selection.VectorSelectionSourceFactory"> {
/**
* Returns a new {@link VectorLayerSelectionSourceImpl} that operates on the given OpenLayers VectorLayer.
* Returns a new {@link VectorLayerSelectionSource} that operates on the given OpenLayers VectorLayer.
*/
createSelectionSource(options: VectorLayerSelectionSourceOptions): VectorLayerSelectionSource;
}
1 change: 1 addition & 0 deletions src/samples/map-sample/ol-app/MapConfigProviderImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ function createKrankenhausLayer(vectorSourceFactory: OgcFeaturesVectorSourceFact
const baseURL = "https://ogc-api-test.nrw.de/inspire-us-krankenhaus/v1";
const collectionId = "governmentalservice";
const source = vectorSourceFactory.createVectorSource({
strategy: "next",
baseUrl: baseURL,
collectionId: collectionId,
limit: 1000,
Expand Down
2 changes: 1 addition & 1 deletion src/samples/showcase/showcase-app/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
content: "";
position: absolute;
inset: 0;
pointer-events:none;
pointer-events: none;
border: 3px solid var(--chakra-colors-trails-500);
}

Expand Down

0 comments on commit 1efd592

Please sign in to comment.