From 57d9b87fb310643ae566c26ae665151b010a7e09 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Tue, 24 Mar 2015 17:13:07 +0100 Subject: [PATCH 1/3] Add wrapX option for ol.source.WMTS --- examples/wmts-layer-from-capabilities.js | 4 ---- examples/wmts.js | 4 ++-- externs/olx.js | 11 +++++++++- src/ol/source/wmtssource.js | 27 +++++++++++------------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/examples/wmts-layer-from-capabilities.js b/examples/wmts-layer-from-capabilities.js index 921912588f..c84850f426 100644 --- a/examples/wmts-layer-from-capabilities.js +++ b/examples/wmts-layer-from-capabilities.js @@ -2,7 +2,6 @@ goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.format.WMTSCapabilities'); goog.require('ol.layer.Tile'); -goog.require('ol.proj'); goog.require('ol.source.OSM'); goog.require('ol.source.WMTS'); @@ -14,8 +13,6 @@ $.ajax('data/WMTSCapabilities.xml').then(function(response) { var options = ol.source.WMTS.optionsFromCapabilities(result, {layer: 'layer-7328', matrixSet: 'EPSG:3857'}); - var projection = ol.proj.get('EPSG:3857'); - var projectionExtent = projection.getExtent(); map = new ol.Map({ layers: [ new ol.layer.Tile({ @@ -24,7 +21,6 @@ $.ajax('data/WMTSCapabilities.xml').then(function(response) { }), new ol.layer.Tile({ opacity: 1, - extent: projectionExtent, source: new ol.source.WMTS(options) }) ], diff --git a/examples/wmts.js b/examples/wmts.js index b8daeb176a..0b3d2bd456 100644 --- a/examples/wmts.js +++ b/examples/wmts.js @@ -35,7 +35,6 @@ var map = new ol.Map({ }), new ol.layer.Tile({ opacity: 0.7, - extent: projectionExtent, source: new ol.source.WMTS({ attributions: [attribution], url: 'http://services.arcgisonline.com/arcgis/rest/' + @@ -49,7 +48,8 @@ var map = new ol.Map({ resolutions: resolutions, matrixIds: matrixIds }), - style: 'default' + style: 'default', + wrapX: true }) }) ], diff --git a/externs/olx.js b/externs/olx.js index be41aa7676..33c38bf9cc 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -5530,7 +5530,8 @@ olx.source.StaticVectorOptions.prototype.urls; * urls: (Array.|undefined), * tileClass: (function(new: ol.ImageTile, ol.TileCoord, * ol.TileState, string, ?string, - * ol.TileLoadFunctionType)|undefined)}} + * ol.TileLoadFunctionType)|undefined), + * wrapX: (boolean|undefined)}} * @api */ olx.source.WMTSOptions; @@ -5689,6 +5690,14 @@ olx.source.WMTSOptions.prototype.tileLoadFunction; olx.source.WMTSOptions.prototype.urls; +/** + * Whether to wrap the world horizontally. Default is `false`. + * @type {boolean|undefined} + * @api + */ +olx.source.WMTSOptions.prototype.wrapX; + + /** * @typedef {{attributions: (Array.|undefined), * crossOrigin: (null|string|undefined), diff --git a/src/ol/source/wmtssource.js b/src/ol/source/wmtssource.js index 2fca62db1e..19fb88f5e5 100644 --- a/src/ol/source/wmtssource.js +++ b/src/ol/source/wmtssource.js @@ -3,7 +3,6 @@ goog.provide('ol.source.WMTSRequestEncoding'); goog.require('goog.array'); goog.require('goog.asserts'); -goog.require('goog.math'); goog.require('goog.object'); goog.require('goog.string'); goog.require('goog.uri.utils'); @@ -170,7 +169,6 @@ ol.source.WMTS = function(options) { } var tmpExtent = ol.extent.createEmpty(); - var tmpTileCoord = [0, 0, 0]; tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( /** * @param {ol.TileCoord} tileCoord Tile coordinate. @@ -188,16 +186,6 @@ ol.source.WMTS = function(options) { var tileExtent = tileGrid.getTileCoordExtent(tileCoord, tmpExtent); var extent = projection.getExtent(); - if (!goog.isNull(extent) && projection.isGlobal()) { - var numCols = Math.ceil( - ol.extent.getWidth(extent) / - ol.extent.getWidth(tileExtent)); - x = goog.math.modulo(x, numCols); - tmpTileCoord[0] = tileCoord[0]; - tmpTileCoord[1] = x; - tmpTileCoord[2] = tileCoord[2]; - tileExtent = tileGrid.getTileCoordExtent(tmpTileCoord, tmpExtent); - } if (!ol.extent.intersects(tileExtent, extent) || ol.extent.touches(tileExtent, extent)) { return null; @@ -215,7 +203,8 @@ ol.source.WMTS = function(options) { tileGrid: tileGrid, tileLoadFunction: options.tileLoadFunction, tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: tileUrlFunction + tileUrlFunction: tileUrlFunction, + wrapX: goog.isDef(options.wrapX) ? options.wrapX : false }); }; @@ -348,7 +337,7 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { goog.asserts.assert(!goog.isNull(l)); goog.asserts.assert(l['TileMatrixSetLink'].length > 0); - var idx, matrixSet; + var idx, matrixSet, wrapX; if (l['TileMatrixSetLink'].length > 1) { idx = goog.array.findIndex(l['TileMatrixSetLink'], function(elt, index, array) { @@ -372,6 +361,13 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { goog.asserts.assert(!goog.isNull(matrixSet)); + var wgs84BoundingBox = l['WGS84BoundingBox']; + if (goog.isDef(wgs84BoundingBox)) { + var wgs84ProjectionExtent = ol.proj.get('EPSG:4326').getExtent(); + wrapX = (wgs84BoundingBox[0] == wgs84ProjectionExtent[0] && + wgs84BoundingBox[2] == wgs84ProjectionExtent[2]); + } + var format = /** @type {string} */ (l['Format'][0]); if (goog.isDef(config['format'])) { format = config['format']; @@ -463,7 +459,8 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { requestEncoding: requestEncoding, tileGrid: tileGrid, style: style, - dimensions: dimensions + dimensions: dimensions, + wrapX: wrapX }; /* jshint +W069 */ From 4822ca273f2f4f744f4cae856ae8cfd9e105d331 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Tue, 24 Mar 2015 19:03:09 +0100 Subject: [PATCH 2/3] Give tile grids a width --- externs/olx.js | 24 ++++++++++- src/ol/attribution.js | 4 +- src/ol/source/tilesource.js | 9 ++--- src/ol/tilecoord.js | 13 +++--- src/ol/tilegrid/tilegrid.js | 71 ++++++++++++++++++++++++++++++++- src/ol/tilegrid/wmtstilegrid.js | 9 ++++- 6 files changed, 112 insertions(+), 18 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index 33c38bf9cc..42cc2d2a67 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -6479,7 +6479,8 @@ olx.tilegrid; * origins: (Array.|undefined), * resolutions: !Array., * tileSize: (number|undefined), - * tileSizes: (Array.|undefined)}} + * tileSizes: (Array.|undefined), + * widths: (Array.|undefined)}} * @api */ olx.tilegrid.TileGridOptions; @@ -6535,13 +6536,23 @@ olx.tilegrid.TileGridOptions.prototype.tileSize; olx.tilegrid.TileGridOptions.prototype.tileSizes; +/** + * Widths in tiles. If given, the array length should match the length of the + * `resolutions` array, i.e. each resolution will have a number of tile columns. + * @type {Array.|undefined} + * @api + */ +olx.tilegrid.TileGridOptions.prototype.widths; + + /** * @typedef {{origin: (ol.Coordinate|undefined), * origins: (Array.|undefined), * resolutions: !Array., * matrixIds: !Array., * tileSize: (number|undefined), - * tileSizes: (Array.|undefined)}} + * tileSizes: (Array.|undefined), + * widths: (Array.|undefined)}} * @api */ olx.tilegrid.WMTSOptions; @@ -6595,6 +6606,15 @@ olx.tilegrid.WMTSOptions.prototype.tileSize; olx.tilegrid.WMTSOptions.prototype.tileSizes; +/** + * Widths in tiles. If given, the array length should match the length of the + * `resolutions` array, i.e. each resolution will have a number of tile columns. + * @type {Array.|undefined} + * @api + */ +olx.tilegrid.WMTSOptions.prototype.widths; + + /** * @typedef {{extent: (ol.Extent|undefined), * maxZoom: (number|undefined), diff --git a/src/ol/attribution.js b/src/ol/attribution.js index d234c36515..aeeb7bdf12 100644 --- a/src/ol/attribution.js +++ b/src/ol/attribution.js @@ -77,8 +77,8 @@ ol.Attribution.prototype.intersectsAnyTileRange = if (testTileRange.intersects(tileRange)) { return true; } - var extentTileRange = tileGrid.getTileRangeForExtentAndZ( - ol.tilegrid.extentFromProjection(projection), parseInt(zKey, 10)); + var extentTileRange = tileGrid.getTileRange( + parseInt(zKey, 10), projection); var width = extentTileRange.getWidth(); if (tileRange.minX < extentTileRange.minX || tileRange.maxX > extentTileRange.maxX) { diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index 78c81d8071..bdb602a7d3 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -224,12 +224,11 @@ ol.source.Tile.prototype.getWrapXTileCoord = function(tileCoord, opt_projection) { var projection = goog.isDef(opt_projection) ? opt_projection : this.getProjection(); - if (goog.isDef(this.wrapX_) && projection.isGlobal()) { - var tileGrid = this.getTileGridForProjection(projection); - var extent = ol.tilegrid.extentFromProjection(projection); + var tileGrid = this.getTileGridForProjection(projection); + if (goog.isDef(this.wrapX_) && tileGrid.isGlobal(tileCoord[0], projection)) { return this.wrapX_ ? - ol.tilecoord.wrapX(tileCoord, tileGrid, extent) : - ol.tilecoord.clipX(tileCoord, tileGrid, extent); + ol.tilecoord.wrapX(tileCoord, tileGrid, projection) : + ol.tilecoord.clipX(tileCoord, tileGrid, projection); } else { return tileCoord; } diff --git a/src/ol/tilecoord.js b/src/ol/tilecoord.js index 1ddc43b005..03e451ab94 100644 --- a/src/ol/tilecoord.js +++ b/src/ol/tilecoord.js @@ -143,15 +143,15 @@ ol.tilecoord.toString = function(tileCoord) { /** * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.tilegrid.TileGrid} tilegrid Tile grid. - * @param {ol.Extent} extent Extent. + * @param {ol.proj.Projection} projection Projection. * @return {ol.TileCoord} Tile coordinate. */ ol.tilecoord.wrapX = (function() { var tmpTileCoord = [0, 0, 0]; - return function(tileCoord, tileGrid, extent) { + return function(tileCoord, tileGrid, projection) { var z = tileCoord[0]; var x = tileCoord[1]; - var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + var tileRange = tileGrid.getTileRange(z, projection); if (x < tileRange.minX || x > tileRange.maxX) { x = goog.math.modulo(x, tileRange.getWidth()); return ol.tilecoord.createOrUpdate(z, x, tileCoord[2], tmpTileCoord); @@ -164,11 +164,12 @@ ol.tilecoord.wrapX = (function() { /** * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @param {ol.Extent} extent Extent. + * @param {ol.proj.Projection} projection Projection. * @return {ol.TileCoord} Tile coordinate. */ -ol.tilecoord.clipX = function(tileCoord, tileGrid, extent) { +ol.tilecoord.clipX = function(tileCoord, tileGrid, projection) { + var z = tileCoord[0]; var x = tileCoord[1]; - var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, tileCoord[0]); + var tileRange = tileGrid.getTileRange(z, projection); return (x < tileRange.minX || x > tileRange.maxX) ? null : tileCoord; }; diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index 1e0039770c..8169dc1c06 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -92,6 +92,16 @@ ol.tilegrid.TileGrid = function(options) { (!goog.isDef(this.tileSize_) && !goog.isNull(this.tileSizes_)) || (goog.isDef(this.tileSize_) && goog.isNull(this.tileSizes_))); + /** + * @private + * @type {Array.} + */ + this.widths_ = null; + if (goog.isDef(options.widths)) { + this.widths_ = options.widths; + goog.asserts.assert(this.widths_.length == this.resolutions_.length); + } + }; @@ -373,6 +383,25 @@ ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) { }; +/** + * @param {number} z Zoom level. + * @param {ol.proj.Projection} projection Projection. + * @param {ol.TileRange=} opt_tileRange Tile range. + * @return {ol.TileRange} Tile range. + */ +ol.tilegrid.TileGrid.prototype.getTileRange = + function(z, projection, opt_tileRange) { + var projectionExtentTileRange = this.getTileRangeForExtentAndZ( + ol.tilegrid.extentFromProjection(projection), z); + var width = this.getWidth(z); + if (!goog.isDef(width)) { + width = projectionExtentTileRange.getWidth(); + } + return ol.TileRange.createOrUpdate( + 0, width - 1, 0, projectionExtentTileRange.getHeight(), opt_tileRange); +}; + + /** * @param {number} z Z. * @return {number} Tile size. @@ -389,6 +418,19 @@ ol.tilegrid.TileGrid.prototype.getTileSize = function(z) { }; +/** + * @param {number} z Zoom level. + * @return {number|undefined} Width for the specified zoom level or `undefined` + * if unknown. + */ +ol.tilegrid.TileGrid.prototype.getWidth = function(z) { + if (!goog.isNull(this.widths_)) { + goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom); + return this.widths_[z]; + } +}; + + /** * @param {number} resolution Resolution. * @return {number} Z. @@ -399,6 +441,26 @@ ol.tilegrid.TileGrid.prototype.getZForResolution = function(resolution) { }; +/** + * @param {number} z Zoom level. + * @param {ol.proj.Projection} projection Projection. + * @return {boolean} Whether the tile grid is defined for the whole globe when + * used with the provided `projection` at zoom level `z`. + */ +ol.tilegrid.TileGrid.prototype.isGlobal = function(z, projection) { + var width = this.getWidth(z); + if (goog.isDef(width)) { + var projTileGrid = ol.tilegrid.getForProjection(projection); + var projExtent = projection.getExtent(); + return this.getTileSize(z) * width == + projTileGrid.getTileSize(z) * + projTileGrid.getTileRangeForExtentAndZ(projExtent, z).getWidth(); + } else { + return projection.isGlobal(); + } +}; + + /** * @param {ol.proj.Projection} projection Projection. * @return {ol.tilegrid.TileGrid} Default tile grid for the passed projection. @@ -433,10 +495,17 @@ ol.tilegrid.createForExtent = var resolutions = ol.tilegrid.resolutionsFromExtent( extent, opt_maxZoom, tileSize); + var widths = new Array(resolutions.length); + var extentWidth = ol.extent.getWidth(extent); + for (var z = resolutions.length - 1; z >= 0; --z) { + widths[z] = extentWidth / tileSize / resolutions[z]; + } + return new ol.tilegrid.TileGrid({ origin: ol.extent.getCorner(extent, corner), resolutions: resolutions, - tileSize: tileSize + tileSize: tileSize, + widths: widths }); }; diff --git a/src/ol/tilegrid/wmtstilegrid.js b/src/ol/tilegrid/wmtstilegrid.js index 228d25715a..9f381c1682 100644 --- a/src/ol/tilegrid/wmtstilegrid.js +++ b/src/ol/tilegrid/wmtstilegrid.js @@ -34,7 +34,8 @@ ol.tilegrid.WMTS = function(options) { origins: options.origins, resolutions: options.resolutions, tileSize: options.tileSize, - tileSizes: options.tileSizes + tileSizes: options.tileSizes, + widths: options.widths }); }; @@ -77,6 +78,8 @@ ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = var origins = []; /** @type {!Array.} */ var tileSizes = []; + /** @type {!Array.} */ + var widths = []; var supportedCRSPropName = 'SupportedCRS'; var matrixIdsPropName = 'TileMatrix'; @@ -112,12 +115,14 @@ ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = var tileHeight = elt[tileHeightPropName]; goog.asserts.assert(tileWidth == tileHeight); tileSizes.push(tileWidth); + widths.push(elt['MatrixWidth']); }); return new ol.tilegrid.WMTS({ origins: origins, resolutions: resolutions, matrixIds: matrixIds, - tileSizes: tileSizes + tileSizes: tileSizes, + widths: widths }); }; From ebb40c80469b999dc727d806feb425cb6bb61531 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Tue, 24 Mar 2015 19:03:33 +0100 Subject: [PATCH 3/3] Clean up docs and consistently work with resolutions --- externs/olx.js | 25 ++++++++++++++++--------- src/ol/tilegrid/tilegrid.js | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index 42cc2d2a67..bbaac3bbcb 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -6503,8 +6503,8 @@ olx.tilegrid.TileGridOptions.prototype.origin; /** - * Origins. If given, the array should match the `resolutions` array, i.e. - * each resolution can have a different origin. + * Origins. If given, the array length should match the length of the + * `resolutions` array, i.e. each resolution can have a different origin. * @type {Array.|undefined} * @api stable */ @@ -6512,7 +6512,9 @@ olx.tilegrid.TileGridOptions.prototype.origins; /** - * Resolutions. + * Resolutions. The array index of each resolution needs to match the zoom + * level. This means that even if a `minZoom` is configured, the resolutions + * array will have a length of `maxZoom + 1`. * @type {!Array.} * @api stable */ @@ -6528,8 +6530,8 @@ olx.tilegrid.TileGridOptions.prototype.tileSize; /** - * Tile sizes. If given, the array should match the `resolutions` array, i.e. - * each resolution can have a different tile size. + * Tile sizes. If given, the array length should match the length of the + * `resolutions` array, i.e. each resolution can have a different tile size. * @type {Array.|undefined} * @api stable */ @@ -6567,7 +6569,8 @@ olx.tilegrid.WMTSOptions.prototype.origin; /** - * Origins. + * Origins. The length of this array needs to match the length of the + * `resolutions` array. * @type {Array.|undefined} * @api */ @@ -6575,7 +6578,9 @@ olx.tilegrid.WMTSOptions.prototype.origins; /** - * Resolutions. + * Resolutions. The array index of each resolution needs to match the zoom + * level. This means that even if a `minZoom` is configured, the resolutions + * array will have a length of `maxZoom + 1` * @type {!Array.} * @api */ @@ -6583,7 +6588,8 @@ olx.tilegrid.WMTSOptions.prototype.resolutions; /** - * matrix IDs. + * matrix IDs. The length of this array needs to match the length of the + * `resolutions` array. * @type {!Array.} * @api */ @@ -6599,7 +6605,8 @@ olx.tilegrid.WMTSOptions.prototype.tileSize; /** - * Tile sizes. + * Tile sizes. The length of this array needs to match the length of the + * `resolutions` array. * @type {Array.|undefined} * @api */ diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index 8169dc1c06..8807e6e657 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -65,7 +65,7 @@ ol.tilegrid.TileGrid = function(options) { this.origins_ = null; if (goog.isDef(options.origins)) { this.origins_ = options.origins; - goog.asserts.assert(this.origins_.length == this.maxZoom + 1); + goog.asserts.assert(this.origins_.length == this.resolutions_.length); } goog.asserts.assert( (goog.isNull(this.origin_) && !goog.isNull(this.origins_)) || @@ -78,7 +78,7 @@ ol.tilegrid.TileGrid = function(options) { this.tileSizes_ = null; if (goog.isDef(options.tileSizes)) { this.tileSizes_ = options.tileSizes; - goog.asserts.assert(this.tileSizes_.length == this.maxZoom + 1); + goog.asserts.assert(this.tileSizes_.length == this.resolutions_.length); } /**