Respect tile matrix set limits
This commit is contained in:
+103
-13
@@ -6,6 +6,7 @@ import TileGrid from '../tilegrid/TileGrid.js';
|
|||||||
import {assign} from '../obj.js';
|
import {assign} from '../obj.js';
|
||||||
import {getJSON, resolveUrl} from '../net.js';
|
import {getJSON, resolveUrl} from '../net.js';
|
||||||
import {get as getProjection} from '../proj.js';
|
import {get as getProjection} from '../proj.js';
|
||||||
|
import {getIntersection as intersectExtents} from '../extent.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://ogcapi.ogc.org/tiles/.
|
* See https://ogcapi.ogc.org/tiles/.
|
||||||
@@ -24,7 +25,7 @@ const TileType = {
|
|||||||
* @property {TileType} dataType Type of data represented in the tileset.
|
* @property {TileType} dataType Type of data represented in the tileset.
|
||||||
* @property {string} [tileMatrixSetDefinition] Reference to a tile matrix set definition.
|
* @property {string} [tileMatrixSetDefinition] Reference to a tile matrix set definition.
|
||||||
* @property {TileMatrixSet} [tileMatrixSet] Tile matrix set definition.
|
* @property {TileMatrixSet} [tileMatrixSet] Tile matrix set definition.
|
||||||
* @property {Array<TileMatrixSetLimits>} [tileMatrixSetLimits] Tile matrix set limits.
|
* @property {Array<TileMatrixSetLimit>} [tileMatrixSetLimits] Tile matrix set limits.
|
||||||
* @property {Array<Link>} links Tileset links.
|
* @property {Array<Link>} links Tileset links.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ const TileType = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} TileMatrixSetLimits
|
* @typedef {Object} TileMatrixSetLimit
|
||||||
* @property {string} tileMatrix The tile matrix id.
|
* @property {string} tileMatrix The tile matrix id.
|
||||||
* @property {number} minTileRow The minimum tile row.
|
* @property {number} minTileRow The minimum tile row.
|
||||||
* @property {number} maxTileRow The maximum tile row.
|
* @property {number} maxTileRow The maximum tile row.
|
||||||
@@ -169,13 +170,17 @@ export function getVectorTileUrlTemplate(links, mediaType) {
|
|||||||
* @return {Promise<TileSetInfo>} Tile set info.
|
* @return {Promise<TileSetInfo>} Tile set info.
|
||||||
*/
|
*/
|
||||||
export function getTileSetInfo(sourceInfo) {
|
export function getTileSetInfo(sourceInfo) {
|
||||||
let tileUrlTemplate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TileMatrixSet} tileMatrixSet Tile matrix set.
|
* @param {TileMatrixSet} tileMatrixSet Tile matrix set.
|
||||||
|
* @param {string} tileUrlTemplate Tile URL template.
|
||||||
|
* @param {Array<TileMatrixSetLimit>} [tileMatrixSetLimits] Tile matrix set limits.
|
||||||
* @return {TileSetInfo} Tile set info.
|
* @return {TileSetInfo} Tile set info.
|
||||||
*/
|
*/
|
||||||
function parseTileMatrixSet(tileMatrixSet) {
|
function parseTileMatrixSet(
|
||||||
|
tileMatrixSet,
|
||||||
|
tileUrlTemplate,
|
||||||
|
tileMatrixSetLimits
|
||||||
|
) {
|
||||||
let projection = sourceInfo.projection;
|
let projection = sourceInfo.projection;
|
||||||
if (!projection) {
|
if (!projection) {
|
||||||
projection = getProjection(tileMatrixSet.crs);
|
projection = getProjection(tileMatrixSet.crs);
|
||||||
@@ -185,15 +190,51 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
}
|
}
|
||||||
const backwards = projection.getAxisOrientation().substr(0, 2) !== 'en';
|
const backwards = projection.getAxisOrientation().substr(0, 2) !== 'en';
|
||||||
|
|
||||||
// TODO: deal with limits
|
|
||||||
const matrices = tileMatrixSet.tileMatrices;
|
const matrices = tileMatrixSet.tileMatrices;
|
||||||
const length = matrices.length;
|
|
||||||
|
/**
|
||||||
|
* @type {Object<string, TileMatrix>}
|
||||||
|
*/
|
||||||
|
const matrixLookup = {};
|
||||||
|
for (let i = 0; i < matrices.length; ++i) {
|
||||||
|
const matrix = matrices[i];
|
||||||
|
matrixLookup[matrix.id] = matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object<string, TileMatrixSetLimit>}
|
||||||
|
*/
|
||||||
|
const limitLookup = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
const matrixIds = [];
|
||||||
|
|
||||||
|
if (tileMatrixSetLimits) {
|
||||||
|
for (let i = 0; i < tileMatrixSetLimits.length; ++i) {
|
||||||
|
const limit = tileMatrixSetLimits[i];
|
||||||
|
const id = limit.tileMatrix;
|
||||||
|
matrixIds.push(id);
|
||||||
|
limitLookup[id] = limit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < matrices.length; ++i) {
|
||||||
|
const id = matrices[i].id;
|
||||||
|
matrixIds.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const length = matrixIds.length;
|
||||||
const origins = new Array(length);
|
const origins = new Array(length);
|
||||||
const resolutions = new Array(length);
|
const resolutions = new Array(length);
|
||||||
const sizes = new Array(length);
|
const sizes = new Array(length);
|
||||||
const tileSizes = new Array(length);
|
const tileSizes = new Array(length);
|
||||||
for (let i = 0; i < matrices.length; ++i) {
|
const extent = [-Infinity, -Infinity, Infinity, Infinity];
|
||||||
const matrix = matrices[i];
|
|
||||||
|
for (let i = 0; i < length; ++i) {
|
||||||
|
const id = matrixIds[i];
|
||||||
|
const matrix = matrixLookup[id];
|
||||||
const origin = matrix.pointOfOrigin;
|
const origin = matrix.pointOfOrigin;
|
||||||
if (backwards) {
|
if (backwards) {
|
||||||
origins[i] = [origin[1], origin[0]];
|
origins[i] = [origin[1], origin[0]];
|
||||||
@@ -203,6 +244,27 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
resolutions[i] = matrix.cellSize;
|
resolutions[i] = matrix.cellSize;
|
||||||
sizes[i] = [matrix.matrixWidth, matrix.matrixHeight];
|
sizes[i] = [matrix.matrixWidth, matrix.matrixHeight];
|
||||||
tileSizes[i] = [matrix.tileWidth, matrix.tileHeight];
|
tileSizes[i] = [matrix.tileWidth, matrix.tileHeight];
|
||||||
|
const limit = limitLookup[id];
|
||||||
|
if (limit) {
|
||||||
|
const tileMapWidth = matrix.cellSize * matrix.tileWidth;
|
||||||
|
const minX = origins[i][0] + limit.minTileCol * tileMapWidth;
|
||||||
|
const maxX = origins[i][0] + (limit.maxTileCol + 1) * tileMapWidth;
|
||||||
|
|
||||||
|
const tileMapHeight = matrix.cellSize * matrix.tileHeight;
|
||||||
|
const upsideDown = matrix.cornerOfOrigin === BOTTOM_LEFT_ORIGIN;
|
||||||
|
|
||||||
|
let minY;
|
||||||
|
let maxY;
|
||||||
|
if (upsideDown) {
|
||||||
|
minY = origins[i][1] + limit.minTileRow * tileMapHeight;
|
||||||
|
maxY = origins[i][1] + (limit.maxTileRow + 1) * tileMapHeight;
|
||||||
|
} else {
|
||||||
|
minY = origins[i][1] - (limit.maxTileRow + 1) * tileMapHeight;
|
||||||
|
maxY = origins[i][1] - limit.minTileRow * tileMapHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectExtents(extent, [minX, minY, maxX, maxY], extent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tileGrid = new TileGrid({
|
const tileGrid = new TileGrid({
|
||||||
@@ -210,6 +272,7 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
resolutions: resolutions,
|
resolutions: resolutions,
|
||||||
sizes: sizes,
|
sizes: sizes,
|
||||||
tileSizes: tileSizes,
|
tileSizes: tileSizes,
|
||||||
|
extent: tileMatrixSetLimits ? extent : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = sourceInfo.context;
|
const context = sourceInfo.context;
|
||||||
@@ -220,14 +283,28 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const matrix = matrices[tileCoord[0]];
|
const id = matrixIds[tileCoord[0]];
|
||||||
|
const matrix = matrixLookup[id];
|
||||||
const upsideDown = matrix.cornerOfOrigin === BOTTOM_LEFT_ORIGIN;
|
const upsideDown = matrix.cornerOfOrigin === BOTTOM_LEFT_ORIGIN;
|
||||||
|
|
||||||
const localContext = {
|
const localContext = {
|
||||||
tileMatrix: matrix.id,
|
tileMatrix: id,
|
||||||
tileCol: tileCoord[1],
|
tileCol: tileCoord[1],
|
||||||
tileRow: upsideDown ? -tileCoord[2] - 1 : tileCoord[2],
|
tileRow: upsideDown ? -tileCoord[2] - 1 : tileCoord[2],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (tileMatrixSetLimits) {
|
||||||
|
const limit = limitLookup[matrix.id];
|
||||||
|
if (
|
||||||
|
localContext.tileCol < limit.minTileCol ||
|
||||||
|
localContext.tileCol > limit.maxTileCol ||
|
||||||
|
localContext.tileRow < limit.minTileRow ||
|
||||||
|
localContext.tileRow > limit.maxTileRow
|
||||||
|
) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assign(localContext, context);
|
assign(localContext, context);
|
||||||
|
|
||||||
const url = tileUrlTemplate.replace(/\{(\w+?)\}/g, function (m, p) {
|
const url = tileUrlTemplate.replace(/\{(\w+?)\}/g, function (m, p) {
|
||||||
@@ -249,6 +326,9 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
* @return {TileSetInfo|Promise<TileSetInfo>} Tile set info.
|
* @return {TileSetInfo|Promise<TileSetInfo>} Tile set info.
|
||||||
*/
|
*/
|
||||||
function parseTileSetMetadata(tileSet) {
|
function parseTileSetMetadata(tileSet) {
|
||||||
|
const tileMatrixSetLimits = tileSet.tileMatrixSetLimits;
|
||||||
|
let tileUrlTemplate;
|
||||||
|
|
||||||
if (tileSet.dataType === TileType.MAP) {
|
if (tileSet.dataType === TileType.MAP) {
|
||||||
tileUrlTemplate = getMapTileUrlTemplate(
|
tileUrlTemplate = getMapTileUrlTemplate(
|
||||||
tileSet.links,
|
tileSet.links,
|
||||||
@@ -264,7 +344,11 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tileSet.tileMatrixSet) {
|
if (tileSet.tileMatrixSet) {
|
||||||
return parseTileMatrixSet(tileSet.tileMatrixSet);
|
return parseTileMatrixSet(
|
||||||
|
tileSet.tileMatrixSet,
|
||||||
|
tileUrlTemplate,
|
||||||
|
tileMatrixSetLimits
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tileSet.tileMatrixSetDefinition) {
|
if (!tileSet.tileMatrixSetDefinition) {
|
||||||
@@ -272,7 +356,13 @@ export function getTileSetInfo(sourceInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = resolveUrl(sourceInfo.url, tileSet.tileMatrixSetDefinition);
|
const url = resolveUrl(sourceInfo.url, tileSet.tileMatrixSetDefinition);
|
||||||
return getJSON(url).then(parseTileMatrixSet);
|
return getJSON(url).then(function (tileMatrixSet) {
|
||||||
|
return parseTileMatrixSet(
|
||||||
|
tileMatrixSet,
|
||||||
|
tileUrlTemplate,
|
||||||
|
tileMatrixSetLimits
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return getJSON(sourceInfo.url).then(parseTileSetMetadata);
|
return getJSON(sourceInfo.url).then(parseTileSetMetadata);
|
||||||
|
|||||||
@@ -76,11 +76,15 @@ describe('ol/source/ogcTileUtil.js', () => {
|
|||||||
);
|
);
|
||||||
expect(tileInfo.grid).to.be.a(TileGrid);
|
expect(tileInfo.grid).to.be.a(TileGrid);
|
||||||
expect(tileInfo.grid.getTileSize(0)).to.eql([256, 256]);
|
expect(tileInfo.grid.getTileSize(0)).to.eql([256, 256]);
|
||||||
expect(tileInfo.grid.getResolutions()).to.have.length(30);
|
expect(tileInfo.grid.getResolutions()).to.have.length(10);
|
||||||
expect(tileInfo.urlFunction).to.be.a(Function);
|
expect(tileInfo.urlFunction).to.be.a(Function);
|
||||||
expect(tileInfo.urlFunction([1, 2, 3])).to.be(
|
expect(tileInfo.urlFunction([3, 2, 1])).to.be(
|
||||||
'https://maps.ecere.com/ogcapi/collections/blueMarble/map/tiles/WebMercatorQuad/1/3/2.jpg'
|
'https://maps.ecere.com/ogcapi/collections/blueMarble/map/tiles/WebMercatorQuad/3/1/2.jpg'
|
||||||
);
|
);
|
||||||
|
expect(tileInfo.urlFunction([3, -1, 0])).to.be(undefined); // below min x
|
||||||
|
expect(tileInfo.urlFunction([3, 8, 0])).to.be(undefined); // above max x
|
||||||
|
expect(tileInfo.urlFunction([3, 0, -1])).to.be(undefined); // below min y
|
||||||
|
expect(tileInfo.urlFunction([3, 0, 8])).to.be(undefined); // above max y
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows preferred media type to be configured', async () => {
|
it('allows preferred media type to be configured', async () => {
|
||||||
@@ -95,8 +99,8 @@ describe('ol/source/ogcTileUtil.js', () => {
|
|||||||
'/ogcapi/collections/blueMarble/map/tiles/WebMercatorQuad/{tileMatrix}/{tileRow}/{tileCol}.png'
|
'/ogcapi/collections/blueMarble/map/tiles/WebMercatorQuad/{tileMatrix}/{tileRow}/{tileCol}.png'
|
||||||
);
|
);
|
||||||
expect(tileInfo.urlFunction).to.be.a(Function);
|
expect(tileInfo.urlFunction).to.be.a(Function);
|
||||||
expect(tileInfo.urlFunction([1, 2, 3])).to.be(
|
expect(tileInfo.urlFunction([3, 2, 1])).to.be(
|
||||||
'https://maps.ecere.com/ogcapi/collections/blueMarble/map/tiles/WebMercatorQuad/1/3/2.png'
|
'https://maps.ecere.com/ogcapi/collections/blueMarble/map/tiles/WebMercatorQuad/3/1/2.png'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,11 +116,15 @@ describe('ol/source/ogcTileUtil.js', () => {
|
|||||||
);
|
);
|
||||||
expect(tileInfo.grid).to.be.a(TileGrid);
|
expect(tileInfo.grid).to.be.a(TileGrid);
|
||||||
expect(tileInfo.grid.getTileSize(0)).to.eql([256, 256]);
|
expect(tileInfo.grid.getTileSize(0)).to.eql([256, 256]);
|
||||||
expect(tileInfo.grid.getResolutions()).to.have.length(30);
|
expect(tileInfo.grid.getResolutions()).to.have.length(8);
|
||||||
expect(tileInfo.urlFunction).to.be.a(Function);
|
expect(tileInfo.urlFunction).to.be.a(Function);
|
||||||
expect(tileInfo.urlFunction([1, 2, 3])).to.be(
|
expect(tileInfo.urlFunction([3, 2, 1])).to.be(
|
||||||
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/1/3/2.json'
|
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/3/1/2.json'
|
||||||
);
|
);
|
||||||
|
expect(tileInfo.urlFunction([2, -1, 0])).to.be(undefined); // below min x
|
||||||
|
expect(tileInfo.urlFunction([2, 4, 0])).to.be(undefined); // above max x
|
||||||
|
expect(tileInfo.urlFunction([2, 0, -1])).to.be(undefined); // below min y
|
||||||
|
expect(tileInfo.urlFunction([2, 0, 4])).to.be(undefined); // above max y
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows preferred media type to be configured', async () => {
|
it('allows preferred media type to be configured', async () => {
|
||||||
@@ -131,8 +139,8 @@ describe('ol/source/ogcTileUtil.js', () => {
|
|||||||
'/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/{tileMatrix}/{tileRow}/{tileCol}.mvt'
|
'/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).to.be.a(Function);
|
||||||
expect(tileInfo.urlFunction([1, 2, 3])).to.be(
|
expect(tileInfo.urlFunction([3, 2, 1])).to.be(
|
||||||
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/1/3/2.mvt'
|
'https://maps.ecere.com/ogcapi/collections/NaturalEarth:cultural:ne_10m_admin_0_countries/tiles/WebMercatorQuad/3/1/2.mvt'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user