Add supported media type list to feature formats
This commit is contained in:
@@ -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(),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,6 +132,8 @@ class GMLBase extends XMLFeature {
|
||||
'featureMember': makeArrayPusher(this.readFeaturesInternal),
|
||||
'featureMembers': makeReplacer(this.readFeaturesInternal),
|
||||
};
|
||||
|
||||
this.supportedMediaTypes = ['application/gml+xml'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -82,6 +82,11 @@ class GeoJSON extends JSONFeature {
|
||||
* @private
|
||||
*/
|
||||
this.extractGeometryName_ = options.extractGeometryName;
|
||||
|
||||
this.supportedMediaTypes = [
|
||||
'application/geo+json',
|
||||
'application/vnd.geo+json',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -489,6 +489,8 @@ class KML extends XMLFeature {
|
||||
this.iconUrlFunction_ = options.iconUrlFunction
|
||||
? options.iconUrlFunction
|
||||
: defaultIconUrlFunction;
|
||||
|
||||
this.supportedMediaTypes = ['application/vnd.google-earth.kml+xml'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -89,6 +89,11 @@ class MVT extends FeatureFormat {
|
||||
* @type {string}
|
||||
*/
|
||||
this.idProperty_ = options.idProperty;
|
||||
|
||||
this.supportedMediaTypes = [
|
||||
'application/vnd.mapbox-vector-tile',
|
||||
'application/x-protobuf',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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"');
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user