Add supported media type list to feature formats

This commit is contained in:
Tim Schaub
2021-09-04 11:06:52 -06:00
parent ac4e472353
commit c8067bebbb
9 changed files with 104 additions and 5 deletions

View File

@@ -10,7 +10,6 @@ const map = new Map({
new VectorTileLayer({
source: new OGCVectorTile({
url: 'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad',
mediaType: 'application/vnd.mapbox-vector-tile',
format: new MVT(),
}),
}),

View File

@@ -76,6 +76,12 @@ class FeatureFormat {
* @type {import("../proj/Projection.js").default|undefined}
*/
this.defaultFeatureProjection = undefined;
/**
* A list media types supported by the format in descending order of preference.
* @type {Array<string>}
*/
this.supportedMediaTypes = null;
}
/**

View File

@@ -132,6 +132,8 @@ class GMLBase extends XMLFeature {
'featureMember': makeArrayPusher(this.readFeaturesInternal),
'featureMembers': makeReplacer(this.readFeaturesInternal),
};
this.supportedMediaTypes = ['application/gml+xml'];
}
/**

View File

@@ -82,6 +82,11 @@ class GeoJSON extends JSONFeature {
* @private
*/
this.extractGeometryName_ = options.extractGeometryName;
this.supportedMediaTypes = [
'application/geo+json',
'application/vnd.geo+json',
];
}
/**

View File

@@ -489,6 +489,8 @@ class KML extends XMLFeature {
this.iconUrlFunction_ = options.iconUrlFunction
? options.iconUrlFunction
: defaultIconUrlFunction;
this.supportedMediaTypes = ['application/vnd.google-earth.kml+xml'];
}
/**

View File

@@ -89,6 +89,11 @@ class MVT extends FeatureFormat {
* @type {string}
*/
this.idProperty_ = options.idProperty;
this.supportedMediaTypes = [
'application/vnd.mapbox-vector-tile',
'application/x-protobuf',
];
}
/**

View File

@@ -13,8 +13,7 @@ import {getTileSetInfo} from './ogcTileUtil.js';
* (zoom level), `{tileRow}`, and `{tileCol}` variables in the URL will always be provided by the source.
* @property {import("../format/Feature.js").default} format Feature parser for tiles.
* @property {string} [mediaType] The content type for the tiles (e.g. "application/vnd.mapbox-vector-tile"). If not provided,
* the source will try to find a link with rel="item" that uses a supported vector type. The chosen media type
* must be parseable by the configured format.
* the source will try to find a link with rel="item" that uses a vector type supported by the configured format.
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least twice the number of tiles in the viewport.
@@ -41,6 +40,10 @@ import {getTileSetInfo} from './ogcTileUtil.js';
* 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.
*
* 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
* provide the `mediaType` option.
*/
class OGCVectorTile extends VectorTile {
/**
@@ -65,6 +68,7 @@ class OGCVectorTile extends VectorTile {
url: options.url,
projection: this.getProjection(),
mediaType: options.mediaType,
supportedMediaTypes: options.format.supportedMediaTypes,
context: options.context || null,
};

View File

@@ -93,6 +93,7 @@ const knownVectorMediaTypes = {
* @typedef {Object} SourceInfo
* @property {string} url The tile set URL.
* @property {string} mediaType The preferred tile media type.
* @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.
*/
@@ -134,13 +135,26 @@ export function getMapTileUrlTemplate(links, mediaType) {
/**
* @param {Array<Link>} links Tileset links.
* @param {string} [mediaType] The preferred media type.
* @param {Array<string>} [supportedMediaTypes] The media types supported by the parser.
* @return {string} The tile URL template.
*/
export function getVectorTileUrlTemplate(links, mediaType) {
export function getVectorTileUrlTemplate(
links,
mediaType,
supportedMediaTypes
) {
let tileUrlTemplate;
let fallbackUrlTemplate;
/**
* Lookup of URL by media type.
* @type {Object<string, string>}
*/
const hrefLookup = {};
for (let i = 0; i < links.length; ++i) {
const link = links[i];
hrefLookup[link.type] = link.href;
if (link.rel === 'item') {
if (link.type === mediaType) {
tileUrlTemplate = link.href;
@@ -152,6 +166,16 @@ export function getVectorTileUrlTemplate(links, mediaType) {
}
}
if (!tileUrlTemplate && supportedMediaTypes) {
for (let i = 0; i < supportedMediaTypes.length; ++i) {
const supportedMediaType = supportedMediaTypes[i];
if (hrefLookup[supportedMediaType]) {
tileUrlTemplate = hrefLookup[supportedMediaType];
break;
}
}
}
if (!tileUrlTemplate) {
if (fallbackUrlTemplate) {
tileUrlTemplate = fallbackUrlTemplate;
@@ -333,7 +357,8 @@ function parseTileSetMetadata(sourceInfo, tileSet) {
} else if (tileSet.dataType === 'vector') {
tileUrlTemplate = getVectorTileUrlTemplate(
tileSet.links,
sourceInfo.mediaType
sourceInfo.mediaType,
sourceInfo.supportedMediaTypes
);
} else {
throw new Error('Expected tileset data type to be "map" or "vector"');

View File

@@ -143,6 +143,48 @@ describe('ol/source/ogcTileUtil.js', () => {
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/3/1/2.mvt'
);
});
it('uses supported media types if available', async () => {
baseUrl = 'https://maps.ecere.com/';
const sourceInfo = {
url: 'https://maps.ecere.com/ogcapi/collections/ne_10m_admin_0_countries/tiles/WebMercatorQuad',
supportedMediaTypes: [
'bogus-media-type',
'application/vnd.mapbox-vector-tile',
'application/geo+json', // should not be used
],
};
const tileInfo = await getTileSetInfo(sourceInfo);
expect(tileInfo).to.be.an(Object);
expect(tileInfo.urlTemplate).to.be(
'/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/{tileMatrix}/{tileRow}/{tileCol}.mvt'
);
expect(tileInfo.urlFunction).to.be.a(Function);
expect(tileInfo.urlFunction([3, 2, 1])).to.be(
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/3/1/2.mvt'
);
});
it('treats supported media types in descending order of priority', async () => {
baseUrl = 'https://maps.ecere.com/';
const sourceInfo = {
url: 'https://maps.ecere.com/ogcapi/collections/ne_10m_admin_0_countries/tiles/WebMercatorQuad',
supportedMediaTypes: [
'bogus-media-type',
'application/geo+json', // should be preferred
'application/vnd.mapbox-vector-tile',
],
};
const tileInfo = await getTileSetInfo(sourceInfo);
expect(tileInfo).to.be.an(Object);
expect(tileInfo.urlTemplate).to.be(
'/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/{tileMatrix}/{tileRow}/{tileCol}.json'
);
expect(tileInfo.urlFunction).to.be.a(Function);
expect(tileInfo.urlFunction([3, 2, 1])).to.be(
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/3/1/2.json'
);
});
});
describe('getVectorTileUrlTemplate()', () => {
@@ -173,6 +215,15 @@ describe('ol/source/ogcTileUtil.js', () => {
);
});
it('uses supported media types is preferred media type is not given', () => {
const urlTemplate = getVectorTileUrlTemplate(links, undefined, [
'application/vnd.mapbox-vector-tile',
]);
expect(urlTemplate).to.be(
'/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/{tileMatrix}/{tileRow}/{tileCol}.mvt'
);
});
it('throws if it cannot find preferred media type or a known fallback', () => {
function call() {
getVectorTileUrlTemplate([], 'application/vnd.mapbox-vector-tile');