Skip to content

Commit

Permalink
Merge pull request #15792 from jansule/ogc-api-collections
Browse files Browse the repository at this point in the history
feat: add collections option to OGCVectorTile source
  • Loading branch information
ahocevar committed May 7, 2024
2 parents faccc50 + f93b83a commit 5c3529a
Show file tree
Hide file tree
Showing 5 changed files with 2,171 additions and 8 deletions.
7 changes: 6 additions & 1 deletion src/ol/source/OGCMapTile.js
Expand Up @@ -33,13 +33,17 @@ import {error as logError} from '../console.js';
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
* @property {number} [transition] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {Array<string>} [collections] A list of geospatial data sub-resources to include. If not provided, the entire dataset will
* be included. This option is not applicable when requesting the tileset for a single collection.
*/

/**
* @classdesc
* Layer source for map tiles from an [OGC API - Tiles](https://ogcapi.ogc.org/tiles/) service that provides "map" type tiles.
* The service must conform to at least the core (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core)
* and tileset (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset) conformance classes.
* and tileset (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset) conformance classes. For supporting the `collections`
* option, the service must conform to the collections selection
* (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/collections-selection) conformance class.
* @api
*/
class OGCMapTile extends TileImage {
Expand All @@ -65,6 +69,7 @@ class OGCMapTile extends TileImage {
projection: this.getProjection(),
mediaType: options.mediaType,
context: options.context || null,
collections: options.collections,
};

getTileSetInfo(sourceInfo)
Expand Down
7 changes: 6 additions & 1 deletion src/ol/source/OGCVectorTile.js
Expand Up @@ -33,13 +33,17 @@ import {error as logError} from '../console.js';
* @property {number|import("../array.js").NearestDirectionFunction} [zDirection=1]
* Choose whether to use tiles with a higher or lower zoom level when between integer
* zoom levels. See {@link module:ol/tilegrid/TileGrid~TileGrid#getZForResolution}.
* @property {Array<string>} [collections] A list of geospatial data sub-resources to include. If not provided, the entire dataset will
* be included. This option is not applicable when requesting the tileset for a single collection.
*/

/**
* @classdesc
* Layer source for map tiles from an [OGC API - Tiles](https://ogcapi.ogc.org/tiles/) service that provides "vector" type tiles.
* The service must conform to at least the core (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core)
* and tileset (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset) conformance classes.
* and tileset (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset) conformance classes. For supporting the `collections`
* option, the service must conform to the collections selection
* (http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/collections-selection) conformance class.
*
* Vector tile sets may come in a variety of formats (e.g. GeoJSON, MVT). The `format` option is used to determine
* which of the advertised media types is used. If you need to force the use of a particular media type, you can
Expand Down Expand Up @@ -71,6 +75,7 @@ class OGCVectorTile extends VectorTileSource {
mediaType: options.mediaType,
supportedMediaTypes: options.format.supportedMediaTypes,
context: options.context || null,
collections: options.collections,
};

getTileSetInfo(sourceInfo)
Expand Down
51 changes: 50 additions & 1 deletion src/ol/source/ogcTileUtil.js
Expand Up @@ -6,6 +6,7 @@ import TileGrid from '../tilegrid/TileGrid.js';
import {getJSON, resolveUrl} from '../net.js';
import {get as getProjection} from '../proj.js';
import {getIntersection as intersectExtents} from '../extent.js';
import {error as logError} from '../console.js';

/**
* See https://ogcapi.ogc.org/tiles/.
Expand Down Expand Up @@ -96,14 +97,50 @@ const knownVectorMediaTypes = {
* @property {Array<string>} [supportedMediaTypes] The supported media types.
* @property {import("../proj/Projection.js").default} projection The source projection.
* @property {Object} [context] Optional context for constructing the URL.
* @property {Array<string>} [collections] Optional collections to append the URL with.
*/

/**
* @param {string} tileUrlTemplate Tile URL template.
* @param {Array<string>} collections List of collections to include as query parameter.
* @return {string} The tile URL template with appended collections query parameter.
*/
export function appendCollectionsQueryParam(tileUrlTemplate, collections) {
if (!collections.length) {
return tileUrlTemplate;
}

// making sure we can always construct a URL instance.
const url = new URL(tileUrlTemplate, 'file://');

if (url.pathname.split('/').includes('collections')) {
logError(
'The "collections" query parameter cannot be added to collection endpoints',
);
return tileUrlTemplate;
}
// According to conformance class
// http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/collections-selection
// commata in the identifiers of the `collections` query parameter
// need to be URLEncoded, while the commata separating the identifiers
// should not.
const encodedCollections = collections
.map((c) => encodeURIComponent(c))
.join(',');

url.searchParams.append('collections', encodedCollections);
const baseUrl = tileUrlTemplate.split('?')[0];
const queryParams = decodeURIComponent(url.searchParams.toString());
return `${baseUrl}?${queryParams}`;
}

/**
* @param {Array<Link>} links Tileset links.
* @param {string} [mediaType] The preferred media type.
* @param {Array<string>} [collections] Optional collections to append the URL with.
* @return {string} The tile URL template.
*/
export function getMapTileUrlTemplate(links, mediaType) {
export function getMapTileUrlTemplate(links, mediaType, collections) {
let tileUrlTemplate;
let fallbackUrlTemplate;
for (let i = 0; i < links.length; ++i) {
Expand All @@ -129,19 +166,25 @@ export function getMapTileUrlTemplate(links, mediaType) {
}
}

if (collections) {
tileUrlTemplate = appendCollectionsQueryParam(tileUrlTemplate, collections);
}

return tileUrlTemplate;
}

/**
* @param {Array<Link>} links Tileset links.
* @param {string} [mediaType] The preferred media type.
* @param {Array<string>} [supportedMediaTypes] The media types supported by the parser.
* @param {Array<string>} [collections] Optional collections to append the URL with.
* @return {string} The tile URL template.
*/
export function getVectorTileUrlTemplate(
links,
mediaType,
supportedMediaTypes,
collections,
) {
let tileUrlTemplate;
let fallbackUrlTemplate;
Expand Down Expand Up @@ -184,6 +227,10 @@ export function getVectorTileUrlTemplate(
}
}

if (collections) {
tileUrlTemplate = appendCollectionsQueryParam(tileUrlTemplate, collections);
}

return tileUrlTemplate;
}

Expand Down Expand Up @@ -362,12 +409,14 @@ function parseTileSetMetadata(sourceInfo, tileSet) {
tileUrlTemplate = getMapTileUrlTemplate(
tileSet.links,
sourceInfo.mediaType,
sourceInfo.collections,
);
} else if (tileSet.dataType === 'vector') {
tileUrlTemplate = getVectorTileUrlTemplate(
tileSet.links,
sourceInfo.mediaType,
sourceInfo.supportedMediaTypes,
sourceInfo.collections,
);
} else {
throw new Error('Expected tileset data type to be "map" or "vector"');
Expand Down

0 comments on commit 5c3529a

Please sign in to comment.