ogcopenapis 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +158 -92
  3. package/package.json +3 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,74 @@
1
+ # Changelog
2
+
3
+ All notable changes to ogcopenapis are in this file. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ Each pull request adds its changes under Unreleased. A release renames Unreleased to its version and date.
8
+
9
+ ## [Unreleased]
10
+
11
+ ## [1.0.2] - 2026-10-27
12
+
13
+ ### Fixed
14
+ - The README examples take every value from the capabilities: the formats of the links, the CRS of a map (the
15
+ reference of the map when the collection offers it, otherwise CRS84), the tileset in the reference of the map, the
16
+ styles for its tile matrix set, and the extents. The examples are functions that run as written; two of them failed on
17
+ servers whose links have no type or whose tilesets list no URI.
18
+ - Demo: the CRS of a map and the encoding of features come from the capabilities, and a tileset bounding box without a
19
+ CRS is not taken as CRS84.
20
+
21
+ ## [1.0.1] - 2026-10-27
22
+
23
+ ### Fixed
24
+ - The Features example of the README keeps the feature ids of the server, `new GeoJsonCodec({generateIDs: false})`.
25
+ `generateIDs: true` replaced them with ids of its own on every query: `get(id)` of the store then requested ids that
26
+ do not exist, and LuciadRIA re-added every feature on every query. The library is unchanged, the application passes
27
+ the codec.
28
+
29
+ ## [1.0.0] - 2026-10-27
30
+
31
+ ### Changed
32
+ - **BREAKING:** requires LuciadRIA 2026.1 or higher, as a peer dependency: the application installs its single copy,
33
+ the package never bundles it.
34
+ - **BREAKING:** the package is native ES modules (`"type": "module"`) with an `exports` map. Import components as
35
+ `ogcopenapis/OgcOpenApiFeatureStore`, the paths of 0.0.12 with `lib/` still work. TypeScript projects need
36
+ `moduleResolution` `bundler`, `node16` or `nodenext` for the paths without `lib/`.
37
+ - OgcOpenApiTilesModel uses LuciadRIA's WMTS support: create it with `createFromTileSet`, which handles tile matrix
38
+ sets that are not quad-trees and `tileMatrixSetLimits`.
39
+ - OgcOpenApiFeatureStore extends LuciadRIA's `Store`. Its requests always settle, and it supports `abortSignal`.
40
+ - `fetchTileSetsInFull` leaves out tile matrix sets whose definition cannot be read, instead of failing, and rejects
41
+ only when none can be read.
42
+ - OgcOpenApiMapsModel keeps the `f` value of the map link, such as `f=png`, and encodes its query.
43
+ - Tile matrix set definitions are requested by their link as the server gives it, with an `Accept: application/json`
44
+ header.
45
+
46
+ ### Added
47
+ - Vector tiles (Mapbox Vector Tiles): OgcOpenApiVectorTilesModel, on LuciadRIA's MapboxVectorTileSetModel. It reads
48
+ the layers from a few tiles when a tileset does not list them (`discoverLayers`).
49
+ - OGC API Styles in the Mapbox GL encoding: OgcOpenApiStyleTools, with a stylesheet or a style per tile matrix set
50
+ (`pickStylesheet`, `getStylesForTileMatrixSet`) and a generated default style (`createDefaultStyle`).
51
+ - The tilesets of a collection, only the tile matrix sets it is tiled in: `fetchCollectionTileSets`, matched to the
52
+ tile matrix sets of the server by URI (`matchTileMatrixSet`, `fetchTileMatrixSetOfTileSet`).
53
+ - The tilesets of the dataset, all collections in each tile: `hasDatasetTileSets`, `fetchDatasetTileSets`, and
54
+ `fetchCollectionTileSet` with collection `null`.
55
+ - A tile matrix set included in a tileset is used as is.
56
+ - A demo, test servers with recorded, live and browser tests, and a check of the package as published.
57
+
58
+ ### Deprecated
59
+ - The constructor `new OgcOpenApiTilesModel(options)`: it supports quad-tree tile matrix sets only.
60
+
61
+ ### Fixed
62
+ - Relative links are resolved against the URL of their document, instead of the host of the server.
63
+ - Tile matrix set URIs match regardless of `http`/`https`, the case of the host and a trailing slash.
64
+ - `orderedAxes` `Y, X` (EPSG:3035) is northing first: `pointOfOrigin` is read in that order.
65
+ - `fromURL` without options no longer hangs.
66
+
67
+ ## 0.0.12 and earlier
68
+
69
+ See the [git history](https://github.com/felipecarrillo100/ogcopenapis/commits/main).
70
+
71
+ [Unreleased]: https://github.com/felipecarrillo100/ogcopenapis/compare/v1.0.2...HEAD
72
+ [1.0.2]: https://github.com/felipecarrillo100/ogcopenapis/compare/v1.0.1...v1.0.2
73
+ [1.0.1]: https://github.com/felipecarrillo100/ogcopenapis/compare/v1.0.0...v1.0.1
74
+ [1.0.0]: https://github.com/felipecarrillo100/ogcopenapis/releases/tag/v1.0.0
package/README.md CHANGED
@@ -63,7 +63,7 @@ npm run test:record
63
63
  GNOSIS (https://maps.gnosis.earth/ogcapi/) is in the list but not tested: its `/collections` response is 9 MB, and its tiles need a login.
64
64
 
65
65
  The package check builds and packs the package, and checks it as published: only `lib/`, the sources of its source
66
- maps, README, LICENSE and package.json are in it, nothing of LuciadRIA; `lib/` imports only its own modules and
66
+ maps, README, CHANGELOG, LICENSE and package.json are in it, nothing of LuciadRIA; `lib/` imports only its own modules and
67
67
  `@luciad/ria`; [publint](https://publint.dev) and [Are the Types Wrong](https://arethetypeswrong.github.io) find no
68
68
  problems; and every module loads through the package exports, installed in a `node_modules` folder, by Node's own
69
69
  ES module loader, as in the tests of an application:
@@ -100,12 +100,15 @@ The package is native ES modules. Import each component by its name, `ogcopenapi
100
100
  instance. The paths of version 0.0.12, `ogcopenapis/lib/OgcOpenApiFeatureStore`, still work.
101
101
 
102
102
  ### Capabilities
103
- `fromURL` reads the landing page of a server and its collections, optionally only those with links of one type:
103
+ `fromURL` reads the landing page of a server and its collections, optionally only those with links of one type. Everything
104
+ the examples below need comes from these capabilities: links and their formats, extents, the CRSs a collection offers,
105
+ tilesets, tile matrix sets and styles. Nothing is configured by hand.
104
106
  ```typescript
105
107
  import {CollectionLinkType, OgcOpenApiGetCapabilities} from "ogcopenapis/OgcOpenApiGetCapabilities";
106
108
 
107
- const capabilities = await OgcOpenApiGetCapabilities.fromURL("https://demo.pygeoapi.io/master/", {filterCollectionsByLinkType: CollectionLinkType.Items});
108
- const collection = capabilities.collections.find(c => c.id === "lakes");
109
+ const capabilities = await OgcOpenApiGetCapabilities.fromURL(serverUrl, {filterCollectionsByLinkType: CollectionLinkType.Items});
110
+ // The collections, as the server lists them: id, title, extent, crs and links
111
+ const collection = capabilities.collections.find(c => c.id === collectionId);
109
112
  ```
110
113
  Links that are relative are resolved against the document that has them.
111
114
 
@@ -116,6 +119,19 @@ const proxify = (url: string, fetchOptions: RequestInit) => ({url: "https://prox
116
119
  const capabilities = await OgcOpenApiGetCapabilities.fromURL(serverUrl, {proxify});
117
120
  ```
118
121
 
122
+ The examples below share these imports and constants:
123
+ ```typescript
124
+ import type {Map} from "@luciad/ria/view/Map.js";
125
+ import {getReference} from "@luciad/ria/reference/ReferenceProvider.js";
126
+ import {
127
+ CollectionLinkType, OgcOpenApiCapabilitiesCollection, OgcOpenApiCapabilitiesObject, OgcOpenApiGetCapabilities,
128
+ CollectionTileSet, CollectionTileSetInfo, TileSetData
129
+ } from "ogcopenapis/OgcOpenApiGetCapabilities";
130
+ import {OgcOpenApiCrsTools} from "ogcopenapis/OgcOpenApiCrsTools";
131
+
132
+ const CRS84 = "http://www.opengis.net/def/crs/OGC/1.3/CRS84";
133
+ ```
134
+
119
135
  ### Features
120
136
  OgcOpenApiFeatureStore implements a LuciadRIA store, to use with FeatureModel and FeatureLayer:
121
137
  ```typescript
@@ -123,116 +139,175 @@ import {FeatureModel} from "@luciad/ria/model/feature/FeatureModel.js";
123
139
  import {FeatureLayer} from "@luciad/ria/view/feature/FeatureLayer.js";
124
140
  import {LoadSpatially} from "@luciad/ria/view/feature/loadingstrategy/LoadSpatially.js";
125
141
  import {GeoJsonCodec} from "@luciad/ria/model/codec/GeoJsonCodec.js";
126
- import {getReference} from "@luciad/ria/reference/ReferenceProvider.js";
127
142
  import {OgcOpenApiFeatureStore} from "ogcopenapis/OgcOpenApiFeatureStore";
128
143
 
129
- const items = OgcOpenApiGetCapabilities.filterCollectionLinks(collection.links, CollectionLinkType.Items);
130
- const store = new OgcOpenApiFeatureStore({
131
- dataUrl: items.find(l => l.type === "application/geo+json").href,
132
- outputFormat: "application/geo+json",
133
- codec: new GeoJsonCodec({generateIDs: true}),
134
- crs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
135
- extent: collection.extent,
136
- useCrs84Bounds: true,
137
- });
138
- const model = new FeatureModel(store, {reference: getReference("CRS:84")});
139
- map.layerTree.addChild(new FeatureLayer(model, {label: collection.title, loadingStrategy: new LoadSpatially()}));
144
+ function createFeatureLayer(collection: OgcOpenApiCapabilitiesCollection) {
145
+ const items = OgcOpenApiGetCapabilities.filterCollectionLinks(collection.links, CollectionLinkType.Items);
146
+ // GeoJSON, the encoding that GeoJsonCodec reads
147
+ const link = items.find(l => l.type === "application/geo+json") || items.find(l => l.type === "application/json");
148
+ if (!link) throw new Error("Collection " + collection.id + " has no GeoJSON items");
149
+ // CRS84: OGC API Features requires every server to support it
150
+ const crs = CRS84;
151
+ const store = new OgcOpenApiFeatureStore({
152
+ dataUrl: link.href,
153
+ outputFormat: link.type,
154
+ // The ids of the server: get(id) requests items/{id} with them
155
+ codec: new GeoJsonCodec({generateIDs: false}),
156
+ crs,
157
+ extent: collection.extent,
158
+ useCrs84Bounds: true,
159
+ });
160
+ const model = new FeatureModel(store, {reference: getReference(OgcOpenApiCrsTools.getReferenceName(crs))});
161
+ return new FeatureLayer(model, {label: collection.title || collection.id, loadingStrategy: new LoadSpatially()});
162
+ }
140
163
  ```
141
164
  The store requests the features in view, a query does not follow the `next` links of a response yet (no paging).
142
165
 
143
- ### Maps and map tiles
144
- For maps and map tiles use OgcOpenApiMapsModel and OgcOpenApiTilesModel. OgcOpenApiMapsModel extends WMSTileSetModel
145
- and OgcOpenApiTilesModel extends WMTSTileSetModel, so you use them in combination with RasterTileSetLayer.
166
+ Keep the ids of the server: `generateIDs: false`. Each feature of an OGC API Features collection has an id, `get(id)` of
167
+ the store requests `items/{id}` with it, and LuciadRIA updates only the features that changed when the ids stay the same
168
+ between queries. `generateIDs: true` replaces them with ids of its own on every query.
146
169
 
147
- OgcOpenApiMapsModel requests the map as tiles. The map link carries the `f` value the server accepts, such as `f=png`:
170
+ ### Maps
171
+ OgcOpenApiMapsModel extends WMSTileSetModel, use it with a RasterTileSetLayer. It requests the map as tiles, in the
172
+ reference of the map when the collection offers it, otherwise in CRS84, which every server supports, and LuciadRIA
173
+ reprojects it:
148
174
  ```typescript
149
175
  import {RasterTileSetLayer} from "@luciad/ria/view/tileset/RasterTileSetLayer.js";
150
176
  import {OgcOpenApiMapsModel} from "ogcopenapis/OgcOpenApiMapsModel";
151
177
 
152
- const mapLinks = OgcOpenApiGetCapabilities.filterCollectionLinks(collection.links, CollectionLinkType.Map);
153
- const mapsModel = new OgcOpenApiMapsModel({
154
- crs: "http://www.opengis.net/def/crs/EPSG/0/3857",
155
- baseURL: (mapLinks.find(l => l.type === "image/png") || mapLinks[0]).href,
156
- collection: collection.id,
157
- });
158
- map.layerTree.addChild(new RasterTileSetLayer(mapsModel, {label: collection.title}));
178
+ function createMapLayer(map: Map, collection: OgcOpenApiCapabilitiesCollection) {
179
+ const mapLinks = OgcOpenApiGetCapabilities.filterCollectionLinks(collection.links, CollectionLinkType.Map);
180
+ // PNG, with transparency, when the server types its links. The link carries the f value the server accepts
181
+ const link = mapLinks.find(l => l.type === "image/png") || mapLinks[0];
182
+ if (!link) throw new Error("Collection " + collection.id + " has no map");
183
+ const crs = (collection.crs || []).find(c => OgcOpenApiCrsTools.getReferenceName(c) === map.reference.identifier) || CRS84;
184
+ const model = new OgcOpenApiMapsModel({crs, baseURL: link.href, collection: collection.id});
185
+ return new RasterTileSetLayer(model, {label: collection.title || collection.id});
186
+ }
187
+ ```
188
+
189
+ ### Tilesets
190
+ Map tiles and vector tiles start from the tilesets: of a collection, or of the dataset (`collection` null), all its
191
+ collections in each tile. Each tileset is in one tile matrix set. Servers give tile matrix sets their own ids (CubeWerx
192
+ calls WebMercatorQuad `smerc`), `fetchTileMatrixSetOfTileSet` matches them by URI.
193
+ ```typescript
194
+ import {OgcOpenApiTileMatrixTools} from "ogcopenapis/OgcOpenApiTileMatrixTools";
195
+
196
+ interface UsableTileSet {
197
+ tileSet: CollectionTileSetInfo;
198
+ tileMatrix: TileSetData;
199
+ }
200
+
201
+ // The tilesets that LuciadRIA can use, each with its tile matrix set. Left out: tile matrix sets in a reference that is
202
+ // not registered (see References below), and for vector tiles those that LuciadRIA cannot address as a quad-tree
203
+ async function fetchUsableTileSets(capabilities: OgcOpenApiCapabilitiesObject, collection: OgcOpenApiCapabilitiesCollection | null, linkType: CollectionLinkType) {
204
+ // fetchTileSetsInFull leaves out definitions that cannot be read, it rejects only when none can
205
+ const tileMatrixSets = await OgcOpenApiGetCapabilities.fetchTileSetsInFull(capabilities);
206
+ const tileSets = collection ?
207
+ await OgcOpenApiGetCapabilities.fetchCollectionTileSets(capabilities, collection, linkType) :
208
+ await OgcOpenApiGetCapabilities.fetchDatasetTileSets(capabilities, linkType);
209
+ const usable = await Promise.all(tileSets.map(async (tileSet): Promise<UsableTileSet | null> => {
210
+ try {
211
+ const tileMatrix = await OgcOpenApiGetCapabilities.fetchTileMatrixSetOfTileSet(capabilities, tileSet, tileMatrixSets);
212
+ const grid = OgcOpenApiTileMatrixTools.createTileGrid(tileMatrix);
213
+ return linkType === CollectionLinkType.VectorTiles && grid.levelCount <= 1 ? null : {tileSet, tileMatrix};
214
+ } catch (e) {
215
+ return null;
216
+ }
217
+ }));
218
+ return usable.filter(t => t !== null);
219
+ }
220
+
221
+ // The tileset in the reference of the map, which needs no reprojection, otherwise the first one
222
+ function pickTileSet(map: Map, tileSets: UsableTileSet[]) {
223
+ if (tileSets.length === 0) throw new Error("No tileset in a tile matrix set that LuciadRIA can use");
224
+ return tileSets.find(t => OgcOpenApiCrsTools.getReferenceName(t.tileMatrix.crs) === map.reference.identifier) || tileSets[0];
225
+ }
226
+
227
+ // Where the data is, in CRS84 as [minX, minY, maxX, maxY]: the extent of the collection, or for the dataset the
228
+ // bounding box of the tileset, when the server gives them in CRS84
229
+ function getExtent(collection: OgcOpenApiCapabilitiesCollection | null, tileSet: CollectionTileSet): number[] | undefined {
230
+ if (collection) {
231
+ const spatial = collection.extent && collection.extent.spatial;
232
+ return spatial && spatial.bbox && (!spatial.crs || /CRS84$/.test(spatial.crs)) ? spatial.bbox[0] : undefined;
233
+ }
234
+ const box = tileSet.boundingBox;
235
+ return box && box.crs && /CRS84$/.test(box.crs) ? [...box.lowerLeft, ...box.upperRight] : undefined;
236
+ }
159
237
  ```
160
238
 
161
- Create OgcOpenApiTilesModel with `createFromTileSet`. It describes the tileset to LuciadRIA's WMTS support, which handles
162
- tile matrix sets that are quad-trees and tile matrix sets that are not (such as GlobalCRS84Scale), and it does not request
163
- tiles outside the `tileMatrixSetLimits`. The tilesets and tile matrix sets are found as for vector tiles, below:
239
+ ### Map tiles
240
+ OgcOpenApiTilesModel extends WMTSTileSetModel, use it with a RasterTileSetLayer. Create it with `createFromTileSet`: it
241
+ describes the tileset to LuciadRIA's WMTS support, which handles tile matrix sets that are quad-trees and tile matrix sets
242
+ that are not (such as GlobalCRS84Scale), and it does not request tiles outside the `tileMatrixSetLimits`:
164
243
  ```typescript
165
244
  import {OgcOpenApiTilesModel} from "ogcopenapis/OgcOpenApiTilesModel";
166
245
 
167
- const tileSet = await OgcOpenApiGetCapabilities.fetchCollectionTileSet(capabilities, collection, tileSetInfo, {}, CollectionLinkType.Tiles);
168
- const model = await OgcOpenApiTilesModel.createFromTileSet({
169
- tileMatrix,
170
- baseURL: tileSet.links.find(l => l.type === "image/png").href,
171
- collection: collection.id,
172
- tileMatrixSetLimits: tileSet.tileMatrixSetLimits,
173
- extent: collection.extent.spatial.bbox[0],
174
- });
175
- map.layerTree.addChild(new RasterTileSetLayer(model, {label: collection.title}));
246
+ async function createTileLayer(map: Map, capabilities: OgcOpenApiCapabilitiesObject, collection: OgcOpenApiCapabilitiesCollection | null) {
247
+ const {tileSet: tileSetInfo, tileMatrix} = pickTileSet(map, await fetchUsableTileSets(capabilities, collection, CollectionLinkType.Tiles));
248
+ const tileSet = await OgcOpenApiGetCapabilities.fetchCollectionTileSet(capabilities, collection, tileSetInfo, {}, CollectionLinkType.Tiles);
249
+ // PNG, with transparency, when the server types its links
250
+ const link = tileSet.links.find(l => l.type === "image/png") || tileSet.links[0];
251
+ const model = await OgcOpenApiTilesModel.createFromTileSet({
252
+ tileMatrix,
253
+ baseURL: link.href,
254
+ collection: collection ? collection.id : "dataset",
255
+ format: link.type && link.type.indexOf("image/") === 0 ? link.type : undefined,
256
+ tileMatrixSetLimits: tileSet.tileMatrixSetLimits,
257
+ extent: getExtent(collection, tileSet),
258
+ });
259
+ return new RasterTileSetLayer(model, {label: collection ? collection.title || collection.id : "All collections"});
260
+ }
176
261
  ```
177
262
  The constructor `new OgcOpenApiTilesModel(options)` still works, but is deprecated: it supports quad-tree tile matrix sets only.
178
- Look at the LuciadRIA documentation if you need further information on using RasterTileSetLayer.
179
263
 
180
264
  ### Vector tiles
181
- For vector tiles use OgcOpenApiVectorTilesModel. It extends LuciadRIA's MapboxVectorTileSetModel, so you use it in combination with
182
- MapboxVectorTileSetLayer and a MapboxStyle:
265
+ OgcOpenApiVectorTilesModel extends LuciadRIA's MapboxVectorTileSetModel, use it with a MapboxVectorTileSetLayer and a
266
+ MapboxStyle. The style is one of the server (OGC API Styles) for the tile matrix set in use, or a style generated from
267
+ the layers in the tiles when the server has none:
183
268
  ```typescript
184
269
  import {MapboxVectorTileSetLayer} from "@luciad/ria/view/mapbox/MapboxVectorTileSetLayer.js";
185
270
  import {MapboxStyle} from "@luciad/ria/view/mapbox/MapboxStyle.js";
186
- import {CollectionLinkType, OgcOpenApiGetCapabilities} from "ogcopenapis/OgcOpenApiGetCapabilities";
187
271
  import {OgcOpenApiVectorTilesModel} from "ogcopenapis/OgcOpenApiVectorTilesModel";
188
272
  import {OgcOpenApiStyleTools} from "ogcopenapis/OgcOpenApiStyleTools";
189
273
 
190
- const capabilities = await OgcOpenApiGetCapabilities.fromURL("https://demo.ldproxy.net/daraa/", {filterCollectionsByLinkType: CollectionLinkType.VectorTiles});
191
- const collection = capabilities.collections.find(c => c.id === "TransportationGroundCrv");
192
-
193
- // The tilesets of the collection: the tile matrix sets it is tiled in, which can be fewer than the ones of the server.
194
- // Servers give tile matrix sets their own ids (CubeWerx calls WebMercatorQuad smerc), they are matched by URI.
195
- // fetchTileSetsInFull leaves out definitions that cannot be read, it rejects only when none can
196
- const tileMatrixSets = await OgcOpenApiGetCapabilities.fetchTileSetsInFull(capabilities);
197
- const tileSets = await OgcOpenApiGetCapabilities.fetchCollectionTileSets(capabilities, collection, CollectionLinkType.VectorTiles);
198
- const webMercator = tileSets.find(t => /WebMercatorQuad\/?$/.test(t.tileMatrixSetURI));
199
- const tileMatrix = await OgcOpenApiGetCapabilities.fetchTileMatrixSetOfTileSet(capabilities, webMercator, tileMatrixSets);
200
- const tileSet = await OgcOpenApiGetCapabilities.fetchCollectionTileSet(capabilities, collection, webMercator, {}, CollectionLinkType.VectorTiles);
201
-
202
- const model = new OgcOpenApiVectorTilesModel({
203
- tileMatrix,
204
- baseURL: tileSet.links.find(l => l.type === "application/vnd.mapbox-vector-tile").href,
205
- collection: collection.id,
206
- tileMatrixSetLimits: tileSet.tileMatrixSetLimits,
207
- });
208
-
209
- // A style of the server (OGC API Styles), or a default style when the server has none.
210
- // The default style needs the layers in the tiles: from the tileset, or read from a few tiles when the tileset does not list them
211
- const styles = await OgcOpenApiStyleTools.fetchStyles(capabilities, collection);
212
- const layers = tileSet.layers.length > 0 ? tileSet.layers : await model.discoverLayers({extent: collection.extent.spatial.bbox[0]});
213
- const styleJson = styles.length > 0 ?
214
- await OgcOpenApiStyleTools.fetchMapboxStyle(capabilities, OgcOpenApiStyleTools.pickStylesheet(styles[0], tileMatrix.id)) :
215
- OgcOpenApiStyleTools.createDefaultStyle(layers);
216
-
217
- map.layerTree.addChild(new MapboxVectorTileSetLayer(model, {label: collection.title, style: MapboxStyle.createFromObject(styleJson)}));
274
+ async function createVectorTileLayer(map: Map, capabilities: OgcOpenApiCapabilitiesObject, collection: OgcOpenApiCapabilitiesCollection | null) {
275
+ const tileSets = await fetchUsableTileSets(capabilities, collection, CollectionLinkType.VectorTiles);
276
+ const {tileSet: tileSetInfo, tileMatrix} = pickTileSet(map, tileSets);
277
+ const tileSet = await OgcOpenApiGetCapabilities.fetchCollectionTileSet(capabilities, collection, tileSetInfo, {}, CollectionLinkType.VectorTiles);
278
+ // Mapbox Vector Tiles, the vector tile encoding LuciadRIA reads
279
+ const link = tileSet.links.find(l => l.type === "application/vnd.mapbox-vector-tile") || tileSet.links.find(l => !l.type);
280
+ if (!link) throw new Error("The tileset has no Mapbox Vector Tiles");
281
+ const label = collection ? collection.title || collection.id : "All collections";
282
+ const model = new OgcOpenApiVectorTilesModel({
283
+ tileMatrix,
284
+ baseURL: link.href,
285
+ collection: collection ? collection.id : "dataset",
286
+ tileMatrixSetLimits: tileSet.tileMatrixSetLimits,
287
+ });
288
+ // The styles of the collection, or of the dataset, for this tile matrix set. An application can let the user pick one
289
+ const styles = OgcOpenApiStyleTools.getStylesForTileMatrixSet(
290
+ await OgcOpenApiStyleTools.fetchStyles(capabilities, collection), tileMatrix.id, tileSets.map(t => t.tileMatrix.id));
291
+ let styleJson;
292
+ if (styles.length > 0) {
293
+ styleJson = await OgcOpenApiStyleTools.fetchMapboxStyle(capabilities, OgcOpenApiStyleTools.pickStylesheet(styles[0], tileMatrix.id));
294
+ } else {
295
+ // The layers in the tiles: from the tileset, or read from a few tiles where the data is
296
+ const layers = tileSet.layers.length > 0 ? tileSet.layers : await model.discoverLayers({extent: getExtent(collection, tileSet)});
297
+ styleJson = OgcOpenApiStyleTools.createDefaultStyle(layers, label);
298
+ }
299
+ return new MapboxVectorTileSetLayer(model, {label, style: MapboxStyle.createFromObject(styleJson)});
300
+ }
218
301
  ```
219
302
 
220
303
  ### Tiles of the dataset
221
304
  Some servers tile the dataset as a whole, with all its collections in each tile, and some only the dataset (PDOK).
222
- The tilesets and styles of the dataset are linked from the landing page:
223
-
305
+ The tilesets and styles of the dataset are linked from the landing page. The functions above take `null` for them:
224
306
  ```typescript
225
307
  if (OgcOpenApiGetCapabilities.hasDatasetTileSets(capabilities, CollectionLinkType.VectorTiles)) {
226
- const datasetTileSets = await OgcOpenApiGetCapabilities.fetchDatasetTileSets(capabilities, CollectionLinkType.VectorTiles);
227
- const tileMatrix = await OgcOpenApiGetCapabilities.fetchTileMatrixSetOfTileSet(capabilities, datasetTileSets[0], tileMatrixSets);
228
- // null instead of a collection: the tileset of the dataset
229
- const tileSet = await OgcOpenApiGetCapabilities.fetchCollectionTileSet(capabilities, null, datasetTileSets[0], {}, CollectionLinkType.VectorTiles);
230
- // The styles of the dataset, only those for the tile matrix set in use
231
- const datasetStyles = OgcOpenApiStyleTools.getStylesForTileMatrixSet(
232
- await OgcOpenApiStyleTools.fetchStyles(capabilities, null), tileMatrix.id, [tileMatrix.id /* and the ids of the other tilesets */]);
308
+ map.layerTree.addChild(await createVectorTileLayer(map, capabilities, null));
233
309
  }
234
310
  ```
235
- A tileset of the dataset can have a `boundingBox`, where its data is. A collection has its `extent` instead.
236
311
 
237
312
  ### Styles
238
313
  * LuciadRIA reads the Mapbox GL encoding of OGC API Styles only, not SLD or other encodings.
@@ -291,17 +366,8 @@ which MapEvolution uses) do. The WKT of epsg.io that the demo uses has no `AXIS`
291
366
  level 0, and does not request tiles outside the `tileMatrixSetLimits` of the tileset.
292
367
  * LuciadRIA 2026.1.1 does not paint Mapbox `circle` layers. The default style draws points as a text symbol for that reason.
293
368
 
294
- ## Changes in 1.0.0
295
- Version 1.0.0 is a breaking change from 0.0.12:
296
- * It requires LuciadRIA 2026.1 or higher, as a peer dependency: the application installs it.
297
- * The package is native ES modules (`"type": "module"`), with an `exports` map. Import components as
298
- `ogcopenapis/OgcOpenApiFeatureStore`, the paths with `lib/` still work.
299
- * New: vector tiles (OgcOpenApiVectorTilesModel), OGC API Styles (OgcOpenApiStyleTools), the tilesets of a collection
300
- (`fetchCollectionTileSets`) and of the dataset (`fetchDatasetTileSets`).
301
- * OgcOpenApiTilesModel uses LuciadRIA's WMTS support, create it with `createFromTileSet`. The constructor is deprecated.
302
- * OgcOpenApiFeatureStore extends LuciadRIA's `Store`.
303
- * `fetchTileSetsInFull` leaves out tile matrix sets whose definition cannot be read, instead of failing.
304
- * Relative links are resolved against the document that has them, instead of the host of the server.
369
+ ## Changes
370
+ See [CHANGELOG.md](CHANGELOG.md) for the changes of each version.
305
371
 
306
372
  ## Requirements
307
373
  * LuciadRIA 2026.1 or higher, a peer dependency that the application installs (place it on a local npm repository,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ogcopenapis",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "OGC API Features, Maps, Tiles and Styles for LuciadRIA",
5
5
  "type": "module",
6
6
  "exports": {
@@ -18,7 +18,8 @@
18
18
  "lib/",
19
19
  "src/",
20
20
  "!src/**/*.test.ts",
21
- "!src/fixtures/"
21
+ "!src/fixtures/",
22
+ "CHANGELOG.md"
22
23
  ],
23
24
  "sideEffects": false,
24
25
  "scripts": {