diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index 530f30dabe..7279cac26c 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -1,5 +1,15 @@ ## Upgrade notes +### v3.6.0 + +#### `ol.tilegrid` changes + +* The `ol.tilegrid.XYZ` constructor has been replaced by a static `ol.tilegrid.createXYZ()` function. The `ol.tilegrid.createXYZ()` function takes the same arguments as the previous `ol.tilegrid.XYZ` constructor, but returns an `ol.tilegrid.TileGrid` instance. +* The internal tile coordinate scheme for XYZ sources has been changed. Previously, the `y` of tile coordinates was transformed to the coordinates used by sources by calculating `-y-1`. Now, it is transformed by calculating `height-y-1`, where height is the number of rows of the tile grid at the zoom level of the tile coordinate. +* The `widths` constructor option of `ol.tilegrid.TileGrid` and subclasses is no longer available, and it is no longer necessary to get proper wrapping at the 180° meridian. However, for `ol.tilegrid.WMTS`, there is a new option `sizes`, where each entry is an `ol.Size` with the `width` ('TileMatrixWidth' in WMTS capabilities) as first and the `height` ('TileMatrixHeight') as second entry of the array. For other tile grids, users can +now specify an `extent` instead of `widths`. These settings are used to restrict the range of tiles that sources will request. +* For `ol.source.TileWMS`, the default value of `warpX` used to be `undefined`, meaning that WMS requests with out-of-extent tile BBOXes would be sent. Now `wrapX` can only be `true` or `false`, and the new default is `true`. No application code changes should be required, but the resulting WMS requests for out-of-extent tiles will uses BBOXes that are shifted to real world coordinates, and no more out-of-extent BBOXes. + ### v3.5.0 #### `ol.Object` and `bindTo` diff --git a/examples/canvas-tiles.js b/examples/canvas-tiles.js index 1f46f51e0f..a3bf7004bb 100644 --- a/examples/canvas-tiles.js +++ b/examples/canvas-tiles.js @@ -5,7 +5,6 @@ goog.require('ol.layer.Tile'); goog.require('ol.proj'); goog.require('ol.source.OSM'); goog.require('ol.source.TileDebug'); -goog.require('ol.tilegrid.XYZ'); var map = new ol.Map({ @@ -16,9 +15,7 @@ var map = new ol.Map({ new ol.layer.Tile({ source: new ol.source.TileDebug({ projection: 'EPSG:3857', - tileGrid: new ol.tilegrid.XYZ({ - maxZoom: 22 - }) + tileGrid: ol.tilegrid.createXYZ({maxZoom: 22}) }) }) ], diff --git a/examples/tile-vector.js b/examples/tile-vector.js index 245e8db64c..40644545fd 100644 --- a/examples/tile-vector.js +++ b/examples/tile-vector.js @@ -7,13 +7,12 @@ goog.require('ol.source.TileVector'); goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); -goog.require('ol.tilegrid.XYZ'); var waterLayer = new ol.layer.Vector({ source: new ol.source.TileVector({ format: new ol.format.TopoJSON(), projection: 'EPSG:3857', - tileGrid: new ol.tilegrid.XYZ({ + tileGrid: ol.tilegrid.createXYZ({ maxZoom: 19 }), url: 'http://{a-c}.tile.openstreetmap.us/' + @@ -31,7 +30,7 @@ var roadLayer = new ol.layer.Vector({ source: new ol.source.TileVector({ format: new ol.format.TopoJSON(), projection: 'EPSG:3857', - tileGrid: new ol.tilegrid.XYZ({ + tileGrid: ol.tilegrid.createXYZ({ maxZoom: 19 }), url: 'http://{a-c}.tile.openstreetmap.us/' + @@ -87,7 +86,7 @@ var buildingLayer = new ol.layer.Vector({ defaultProjection: 'EPSG:4326' }), projection: 'EPSG:3857', - tileGrid: new ol.tilegrid.XYZ({ + tileGrid: ol.tilegrid.createXYZ({ maxZoom: 19 }), url: 'http://{a-c}.tile.openstreetmap.us/' + @@ -106,7 +105,7 @@ var landuseLayer = new ol.layer.Vector({ defaultProjection: 'EPSG:4326' }), projection: 'EPSG:3857', - tileGrid: new ol.tilegrid.XYZ({ + tileGrid: ol.tilegrid.createXYZ({ maxZoom: 19 }), url: 'http://{a-c}.tile.openstreetmap.us/' + diff --git a/examples/vector-esri-edit.js b/examples/vector-esri-edit.js index 5ebb58bf7f..d646f18b26 100644 --- a/examples/vector-esri-edit.js +++ b/examples/vector-esri-edit.js @@ -12,7 +12,6 @@ goog.require('ol.loadingstrategy'); goog.require('ol.proj'); goog.require('ol.source.Vector'); goog.require('ol.source.XYZ'); -goog.require('ol.tilegrid.XYZ'); var serviceUrl = 'http://services.arcgis.com/rOo16HdIMeOBI4Mb/arcgis/rest/' + @@ -45,7 +44,7 @@ var vectorSource = new ol.source.Vector({ } }}); }, - strategy: ol.loadingstrategy.tile(new ol.tilegrid.XYZ({ + strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({ tileSize: 512 })) }); diff --git a/examples/vector-esri.js b/examples/vector-esri.js index 0ba8581813..655062f372 100644 --- a/examples/vector-esri.js +++ b/examples/vector-esri.js @@ -11,7 +11,6 @@ goog.require('ol.source.XYZ'); goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); -goog.require('ol.tilegrid.XYZ'); var serviceUrl = 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/' + @@ -91,7 +90,7 @@ var vectorSource = new ol.source.Vector({ } }}); }, - strategy: ol.loadingstrategy.tile(new ol.tilegrid.XYZ({ + strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({ tileSize: 512 })) }); diff --git a/examples/vector-osm.js b/examples/vector-osm.js index eb1f5659a1..30526090f5 100644 --- a/examples/vector-osm.js +++ b/examples/vector-osm.js @@ -12,7 +12,6 @@ goog.require('ol.style.Circle'); goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); -goog.require('ol.tilegrid.XYZ'); var styles = { 'amenity': { @@ -102,7 +101,7 @@ var vectorSource = new ol.source.Vector({ vectorSource.addFeatures(features); }); }, - strategy: ol.loadingstrategy.tile(new ol.tilegrid.XYZ({ + strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({ maxZoom: 19 })) }); diff --git a/examples/vector-wfs.js b/examples/vector-wfs.js index 47386c71d7..4604537bf9 100644 --- a/examples/vector-wfs.js +++ b/examples/vector-wfs.js @@ -8,7 +8,6 @@ goog.require('ol.source.BingMaps'); goog.require('ol.source.Vector'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); -goog.require('ol.tilegrid.XYZ'); // format used to parse WFS GetFeature responses @@ -24,7 +23,7 @@ var vectorSource = new ol.source.Vector({ // parameter to the URL $.ajax({url: url, dataType: 'jsonp', jsonp: false}); }, - strategy: ol.loadingstrategy.tile(new ol.tilegrid.XYZ({ + strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({ maxZoom: 19 })) }); diff --git a/examples/wms-custom-tilegrid-512x256.js b/examples/wms-custom-tilegrid-512x256.js index 8a6226d030..5c7d86644a 100644 --- a/examples/wms-custom-tilegrid-512x256.js +++ b/examples/wms-custom-tilegrid-512x256.js @@ -15,7 +15,7 @@ for (var i = 0, ii = resolutions.length; i < ii; ++i) { resolutions[i] = startResolution / Math.pow(2, i); } var tileGrid = new ol.tilegrid.TileGrid({ - origin: ol.extent.getBottomLeft(projExtent), + extent: [-13884991, 2870341, -7455066, 6338219], resolutions: resolutions, tileSize: [512, 256] }); @@ -25,7 +25,6 @@ var layers = [ source: new ol.source.MapQuest({layer: 'sat'}) }), new ol.layer.Tile({ - extent: [-13884991, 2870341, -7455066, 6338219], source: new ol.source.TileWMS({ url: 'http://demo.boundlessgeo.com/geoserver/wms', params: {'LAYERS': 'topp:states', 'TILED': true}, diff --git a/examples/wms-tiled-wrap-180.js b/examples/wms-tiled-wrap-180.js index fdf864b7e8..bf606e013f 100644 --- a/examples/wms-tiled-wrap-180.js +++ b/examples/wms-tiled-wrap-180.js @@ -13,8 +13,7 @@ var layers = [ source: new ol.source.TileWMS({ url: 'http://demo.boundlessgeo.com/geoserver/ne/wms', params: {'LAYERS': 'ne:ne_10m_admin_0_countries', 'TILED': true}, - serverType: 'geoserver', - wrapX: true + serverType: 'geoserver' }) }) ]; diff --git a/examples/xyz-esri-4326-512.js b/examples/xyz-esri-4326-512.js index c10c5d270b..45cc0fa316 100644 --- a/examples/xyz-esri-4326-512.js +++ b/examples/xyz-esri-4326-512.js @@ -35,7 +35,7 @@ var map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ - /* ol.source.XYZ and ol.tilegrid.XYZ have no resolutions config */ + /* ol.source.XYZ and ol.tilegrid.TileGrid have no resolutions config */ source: new ol.source.TileImage({ attributions: [attribution], tileUrlFunction: function(tileCoord, pixelRatio, projection) { diff --git a/externs/olx.js b/externs/olx.js index f258393c28..ed472c0581 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4942,11 +4942,10 @@ olx.source.TileWMSOptions.prototype.urls; /** - * Whether to wrap the world horizontally. The default, `undefined`, is to - * request out-of-bounds tiles from the server. This works well in e.g. - * GeoServer. When set to `false`, only one world will be rendered. When set to - * `true`, tiles will be requested for one world only, but they will be wrapped - * horizontally to render multiple worlds. + * Whether to wrap the world horizontally. When set to `false`, only one world + * will be rendered. When `true`, tiles will be requested for one world only, + * but they will be wrapped horizontally to render multiple worlds. The default + * is `true`. * @type {boolean|undefined} * @api */ @@ -6014,18 +6013,30 @@ olx.tilegrid; /** - * @typedef {{minZoom: (number|undefined), + * @typedef {{extent: (ol.Extent|undefined), + * minZoom: (number|undefined), * origin: (ol.Coordinate|undefined), * origins: (Array.|undefined), * resolutions: !Array., + * sizes: (Array.|undefined), * tileSize: (number|ol.Size|undefined), - * tileSizes: (Array.|undefined), - * widths: (Array.|undefined)}} + * tileSizes: (Array.|undefined)}} * @api */ olx.tilegrid.TileGridOptions; +/** + * Extent for the tile grid. No tiles outside this extent will be requested by + * {@link ol.source.Tile} sources. When no `origin` or `origins` are + * configured, the `origin` will be set to the bottom-left corner of the extent. + * When no `sizes` are configured, they will be calculated from the extent. + * @type {ol.Extent|undefined} + * @api + */ +olx.tilegrid.TileGridOptions.prototype.extent; + + /** * Minimum zoom. Default is 0. * @type {number|undefined} @@ -6035,7 +6046,7 @@ olx.tilegrid.TileGridOptions.prototype.minZoom; /** - * Origin. Default is null. + * Origin, i.e. the bottom-left corner of the grid. Default is null. * @type {ol.Coordinate|undefined} * @api stable */ @@ -6043,8 +6054,9 @@ olx.tilegrid.TileGridOptions.prototype.origin; /** - * Origins. If given, the array length should match the length of the - * `resolutions` array, i.e. each resolution can have a different origin. + * Origins, i.e. the bottom-left corners of the grid for each zoom level. 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 */ @@ -6061,6 +6073,17 @@ olx.tilegrid.TileGridOptions.prototype.origins; olx.tilegrid.TileGridOptions.prototype.resolutions; +/** + * Number of tile rows and columns of the grid for each zoom level. This setting + * is only needed for tile coordinate transforms that need to work with origins + * other than the bottom-left corner of the grid. No tiles outside this range + * will be requested by sources. If an `extent` is also configured, it takes + * precedence. + * @type {Array.|undefined} + */ +olx.tilegrid.TileGridOptions.prototype.sizes; + + /** * Tile size. Default is `[256, 256]`. * @type {number|ol.Size|undefined} @@ -6079,32 +6102,32 @@ olx.tilegrid.TileGridOptions.prototype.tileSizes; /** - * Number of tile columns that cover the grid's extent for each zoom level. Only - * required when used with a source that has `wrapX` set to `true`, and only - * when the grid's origin differs from the one of the projection's extent. The - * array length has to match the length of the `resolutions` array, i.e. each - * resolution will have a matching entry here. - * @type {Array.|undefined} - * @api - */ -olx.tilegrid.TileGridOptions.prototype.widths; - - -/** - * @typedef {{origin: (ol.Coordinate|undefined), + * @typedef {{extent: (ol.Extent|undefined), + * origin: (ol.Coordinate|undefined), * origins: (Array.|undefined), * resolutions: !Array., * matrixIds: !Array., + * sizes: (Array.|undefined), * tileSize: (number|ol.Size|undefined), - * tileSizes: (Array.|undefined), - * widths: (Array.|undefined)}} + * tileSizes: (Array.|undefined)}} * @api */ olx.tilegrid.WMTSOptions; /** - * Origin. + * Extent for the tile grid. No tiles outside this extent will be requested by + * {@link ol.source.WMTS} sources. When no `origin` or `origins` are + * configured, the `origin` will be calculated from the extent. + * When no `sizes` are configured, they will be calculated from the extent. + * @type {ol.Extent|undefined} + * @api + */ +olx.tilegrid.WMTSOptions.prototype.extent; + + +/** + * Origin, i.e. the top-left corner of the grid. * @type {ol.Coordinate|undefined} * @api */ @@ -6112,7 +6135,8 @@ olx.tilegrid.WMTSOptions.prototype.origin; /** - * Origins. The length of this array needs to match the length of the + * Origins, i.e. the top-left corners of the grid for each zoom level. The + * length of this array needs to match the length of the * `resolutions` array. * @type {Array.|undefined} * @api @@ -6139,6 +6163,18 @@ olx.tilegrid.WMTSOptions.prototype.resolutions; olx.tilegrid.WMTSOptions.prototype.matrixIds; +/** + * Number of tile rows and columns of the grid for each zoom level. The values + * here are the `TileMatrixWidth` and `TileMatrixHeight` advertised in the + * GetCapabilities response of the WMTS, and define the grid's extent together + * with the `origin`. An `extent` can be configured in addition, and will + * further limit the extent for which tile requests are made by sources. + * @type {Array.|undefined} + * @api + */ +olx.tilegrid.WMTSOptions.prototype.sizes; + + /** * Tile size. * @type {number|ol.Size|undefined} diff --git a/src/ol/attribution.js b/src/ol/attribution.js index 4963fa08d6..9f069828c6 100644 --- a/src/ol/attribution.js +++ b/src/ol/attribution.js @@ -78,8 +78,8 @@ ol.Attribution.prototype.intersectsAnyTileRange = if (testTileRange.intersects(tileRange)) { return true; } - var extentTileRange = tileGrid.getTileRange( - parseInt(zKey, 10), projection); + var extentTileRange = tileGrid.getTileRangeForExtentAndZ( + projection.getExtent(), parseInt(zKey, 10)); var width = extentTileRange.getWidth(); if (tileRange.minX < extentTileRange.minX || tileRange.maxX > extentTileRange.maxX) { diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js index 76656e9fec..e756fb8b11 100644 --- a/src/ol/source/bingmapssource.js +++ b/src/ol/source/bingmapssource.js @@ -12,7 +12,6 @@ goog.require('ol.proj'); goog.require('ol.source.State'); goog.require('ol.source.TileImage'); goog.require('ol.tilecoord'); -goog.require('ol.tilegrid.XYZ'); @@ -103,18 +102,20 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse = var maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_; var sourceProjection = this.getProjection(); - var tileGrid = new ol.tilegrid.XYZ({ - extent: ol.tilegrid.extentFromProjection(sourceProjection), + var extent = ol.tilegrid.extentFromProjection(sourceProjection); + var tileSize = resource.imageWidth == resource.imageHeight ? + resource.imageWidth : [resource.imageWidth, resource.imageHeight]; + var tileGrid = ol.tilegrid.createXYZ({ + extent: extent, minZoom: resource.zoomMin, maxZoom: maxZoom, - tileSize: resource.imageWidth == resource.imageHeight ? - resource.imageWidth : [resource.imageWidth, resource.imageHeight] + tileSize: tileSize }); this.tileGrid = tileGrid; var culture = this.culture_; this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( - tileGrid.createTileCoordTransform(), + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid), ol.TileUrlFunction.createFromTileUrlFunctions( goog.array.map( resource.imageUrlSubdomains, diff --git a/src/ol/source/source.js b/src/ol/source/source.js index e215cb40e9..a6aeab177c 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -75,9 +75,9 @@ ol.source.Source = function(options) { /** * @private - * @type {boolean|undefined} + * @type {boolean} */ - this.wrapX_ = options.wrapX; + this.wrapX_ = goog.isDef(options.wrapX) ? options.wrapX : false; }; goog.inherits(ol.source.Source, ol.Object); diff --git a/src/ol/source/tileimagesource.js b/src/ol/source/tileimagesource.js index 0b549a1f9c..1701f19e86 100644 --- a/src/ol/source/tileimagesource.js +++ b/src/ol/source/tileimagesource.js @@ -93,7 +93,8 @@ ol.source.TileImage.prototype.getTile = } else { goog.asserts.assert(projection, 'argument projection is truthy'); var tileCoord = [z, x, y]; - var urlTileCoord = this.getWrapXTileCoord(tileCoord, projection); + var urlTileCoord = this.getTileCoordForTileUrlFunction( + tileCoord, projection); var tileUrl = goog.isNull(urlTileCoord) ? undefined : this.tileUrlFunction(urlTileCoord, pixelRatio, projection); var tile = new this.tileClass( diff --git a/src/ol/source/tilejsonsource.js b/src/ol/source/tilejsonsource.js index 609ce6267f..37dc0f63a0 100644 --- a/src/ol/source/tilejsonsource.js +++ b/src/ol/source/tilejsonsource.js @@ -17,7 +17,6 @@ goog.require('ol.extent'); goog.require('ol.proj'); goog.require('ol.source.State'); goog.require('ol.source.TileImage'); -goog.require('ol.tilegrid.XYZ'); @@ -69,7 +68,7 @@ ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) { } var minZoom = tileJSON.minzoom || 0; var maxZoom = tileJSON.maxzoom || 22; - var tileGrid = new ol.tilegrid.XYZ({ + var tileGrid = ol.tilegrid.createXYZ({ extent: ol.tilegrid.extentFromProjection(sourceProjection), maxZoom: maxZoom, minZoom: minZoom diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index e81a4261b3..a0179d5ad6 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -2,6 +2,7 @@ goog.provide('ol.source.Tile'); goog.provide('ol.source.TileEvent'); goog.provide('ol.source.TileOptions'); +goog.require('goog.asserts'); goog.require('goog.events.Event'); goog.require('ol.Attribution'); goog.require('ol.Extent'); @@ -216,28 +217,23 @@ ol.source.Tile.prototype.getTilePixelSize = /** - * Handles x-axis wrapping. When `wrapX` is `undefined` or the projection is not - * a global projection, `tileCoord` will be returned unaltered. When `wrapX` is - * `true`, the tile coordinate will be wrapped horizontally. - * When `wrapX` is `false`, `null` will be returned for tiles that are - * outside the projection extent. + * Handles x-axis wrapping and returns a tile coordinate when it is within + * the resolution and extent range. * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.proj.Projection=} opt_projection Projection. - * @return {ol.TileCoord} Tile coordinate. + * @return {ol.TileCoord} Tile coordinate to be passed to the tileUrlFunction or + * null if no tile URL should be created for the passed `tileCoord`. */ -ol.source.Tile.prototype.getWrapXTileCoord = +ol.source.Tile.prototype.getTileCoordForTileUrlFunction = function(tileCoord, opt_projection) { var projection = goog.isDef(opt_projection) ? opt_projection : this.getProjection(); var tileGrid = this.getTileGridForProjection(projection); - var wrapX = this.getWrapX(); - if (goog.isDef(wrapX) && tileGrid.isGlobal(tileCoord[0], projection)) { - return wrapX ? - ol.tilecoord.wrapX(tileCoord, tileGrid, projection) : - ol.tilecoord.clipX(tileCoord, tileGrid, projection); - } else { - return tileCoord; + goog.asserts.assert(!goog.isNull(tileGrid), 'tile grid needed'); + if (this.getWrapX()) { + tileCoord = ol.tilecoord.wrapX(tileCoord, tileGrid, projection); } + return ol.tilecoord.restrictByExtentAndZ(tileCoord, tileGrid); }; diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 9e3bcee79f..549ad82d5c 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -13,7 +13,6 @@ goog.require('ol.extent'); goog.require('ol.proj'); goog.require('ol.source.State'); goog.require('ol.source.Tile'); -goog.require('ol.tilegrid.XYZ'); @@ -122,7 +121,7 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { } var minZoom = tileJSON.minzoom || 0; var maxZoom = tileJSON.maxzoom || 22; - var tileGrid = new ol.tilegrid.XYZ({ + var tileGrid = ol.tilegrid.createXYZ({ extent: ol.tilegrid.extentFromProjection(sourceProjection), maxZoom: maxZoom, minZoom: minZoom diff --git a/src/ol/source/tilewmssource.js b/src/ol/source/tilewmssource.js index d655a2e179..b2fa897e0a 100644 --- a/src/ol/source/tilewmssource.js +++ b/src/ol/source/tilewmssource.js @@ -49,7 +49,7 @@ ol.source.TileWMS = function(opt_options) { tileGrid: options.tileGrid, tileLoadFunction: options.tileLoadFunction, tileUrlFunction: goog.bind(this.tileUrlFunction_, this), - wrapX: options.wrapX + wrapX: goog.isDef(options.wrapX) ? options.wrapX : true }); var urls = options.urls; diff --git a/src/ol/source/wmtssource.js b/src/ol/source/wmtssource.js index 53eb1f3bcb..d815e849b1 100644 --- a/src/ol/source/wmtssource.js +++ b/src/ol/source/wmtssource.js @@ -11,7 +11,6 @@ goog.require('ol.TileUrlFunctionType'); goog.require('ol.extent'); goog.require('ol.proj'); goog.require('ol.source.TileImage'); -goog.require('ol.tilecoord'); goog.require('ol.tilegrid.WMTS'); @@ -181,31 +180,8 @@ ol.source.WMTS = function(options) { goog.array.map(this.urls_, createFromWMTSTemplate)) : ol.TileUrlFunction.nullTileUrlFunction; - var tmpExtent = ol.extent.createEmpty(); tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( - /** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.proj.Projection} projection Projection. - * @param {ol.TileCoord=} opt_tileCoord Tile coordinate. - * @return {ol.TileCoord} Tile coordinate. - */ - function(tileCoord, projection, opt_tileCoord) { - goog.asserts.assert(!goog.isNull(tileGrid), - 'tileGrid must not be null'); - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return null; - } - var x = tileCoord[1]; - var y = -tileCoord[2] - 1; - var tileExtent = tileGrid.getTileCoordExtent(tileCoord, tmpExtent); - var extent = projection.getExtent(); - - if (!ol.extent.intersects(tileExtent, extent) || - ol.extent.touches(tileExtent, extent)) { - return null; - } - return ol.tilecoord.createOrUpdate(tileCoord[0], x, y, opt_tileCoord); - }, + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid), tileUrlFunction); goog.base(this, { @@ -379,7 +355,7 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { goog.asserts.assert(l['TileMatrixSetLink'].length > 0, 'layer has TileMatrixSetLink'); - var idx, matrixSet, wrapX; + var idx, matrixSet; if (l['TileMatrixSetLink'].length > 1) { idx = goog.array.findIndex(l['TileMatrixSetLink'], function(elt, index, array) { @@ -404,13 +380,6 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { goog.asserts.assert(!goog.isNull(matrixSet), 'TileMatrixSet must not be null'); - 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']; @@ -450,9 +419,6 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { goog.asserts.assert(!goog.isNull(matrixSetObj), 'found matrixSet in Contents/TileMatrixSet'); - var tileGrid = ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet( - matrixSetObj); - var projection; if (goog.isDef(config['projection'])) { projection = ol.proj.get(config['projection']); @@ -461,6 +427,27 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')); } + var wgs84BoundingBox = l['WGS84BoundingBox']; + var extent, wrapX; + if (goog.isDef(wgs84BoundingBox)) { + var wgs84ProjectionExtent = ol.proj.get('EPSG:4326').getExtent(); + wrapX = (wgs84BoundingBox[0] == wgs84ProjectionExtent[0] && + wgs84BoundingBox[2] == wgs84ProjectionExtent[2]); + extent = ol.proj.transformExtent( + wgs84BoundingBox, 'EPSG:4326', projection); + var projectionExtent = projection.getExtent(); + if (!goog.isNull(projectionExtent)) { + // If possible, do a sanity check on the extent - it should never be + // bigger than the validity extent of the projection of a matrix set. + if (!ol.extent.containsExtent(projectionExtent, extent)) { + extent = undefined; + } + } + } + + var tileGrid = ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet( + matrixSetObj, extent); + /** @type {!Array.} */ var urls = []; var requestEncoding = config['requestEncoding']; diff --git a/src/ol/source/xyzsource.js b/src/ol/source/xyzsource.js index afdeaeb7eb..2c60a6178e 100644 --- a/src/ol/source/xyzsource.js +++ b/src/ol/source/xyzsource.js @@ -3,7 +3,6 @@ goog.provide('ol.source.XYZ'); goog.require('ol.Attribution'); goog.require('ol.TileUrlFunction'); goog.require('ol.source.TileImage'); -goog.require('ol.tilegrid.XYZ'); @@ -20,7 +19,7 @@ ol.source.XYZ = function(options) { var projection = goog.isDef(options.projection) ? options.projection : 'EPSG:3857'; - var tileGrid = new ol.tilegrid.XYZ({ + var tileGrid = ol.tilegrid.createXYZ({ extent: ol.tilegrid.extentFromProjection(projection), maxZoom: options.maxZoom, tileSize: options.tileSize @@ -42,7 +41,8 @@ ol.source.XYZ = function(options) { * @private * @type {ol.TileCoordTransformType} */ - this.tileCoordTransform_ = tileGrid.createTileCoordTransform(); + this.tileCoordTransform_ = + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid); if (goog.isDef(options.tileUrlFunction)) { this.setTileUrlFunction(options.tileUrlFunction); diff --git a/src/ol/tilecoord.js b/src/ol/tilecoord.js index 09f7da088f..ebc1044add 100644 --- a/src/ol/tilecoord.js +++ b/src/ol/tilecoord.js @@ -3,7 +3,7 @@ goog.provide('ol.tilecoord'); goog.require('goog.array'); goog.require('goog.asserts'); -goog.require('goog.math'); +goog.require('ol.extent'); /** @@ -149,25 +149,42 @@ ol.tilecoord.toString = function(tileCoord) { */ ol.tilecoord.wrapX = function(tileCoord, tileGrid, projection) { var z = tileCoord[0]; - var x = tileCoord[1]; - var tileRange = tileGrid.getTileRange(z, projection); - if (x < tileRange.minX || x > tileRange.maxX) { - x = goog.math.modulo(x, tileRange.getWidth()); - return [z, x, tileCoord[2]]; + var center = tileGrid.getTileCoordCenter(tileCoord); + var projectionExtent = ol.tilegrid.extentFromProjection(projection); + if (!ol.extent.containsCoordinate(projectionExtent, center)) { + var worldWidth = ol.extent.getWidth(projectionExtent); + var worldsAway = Math.ceil((projectionExtent[0] - center[0]) / worldWidth); + center[0] += worldWidth * worldsAway; + return tileGrid.getTileCoordForCoordAndZ(center, z); + } else { + return tileCoord; } - return tileCoord; }; /** * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @param {ol.proj.Projection} projection Projection. + * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid. * @return {ol.TileCoord} Tile coordinate. */ -ol.tilecoord.clipX = function(tileCoord, tileGrid, projection) { +ol.tilecoord.restrictByExtentAndZ = function(tileCoord, tileGrid) { var z = tileCoord[0]; var x = tileCoord[1]; - var tileRange = tileGrid.getTileRange(z, projection); - return (x < tileRange.minX || x > tileRange.maxX) ? null : tileCoord; + var y = tileCoord[2]; + + if (tileGrid.getMinZoom() > z || z > tileGrid.getMaxZoom()) { + return null; + } + var extent = tileGrid.getExtent(); + var tileRange; + if (goog.isNull(extent)) { + tileRange = tileGrid.getFullTileRange(z); + } else { + tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + } + if (goog.isNull(tileRange)) { + return tileCoord; + } else { + return tileRange.containsXY(x, y) ? tileCoord : null; + } }; diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index 738b8cbd90..0dcbafc964 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -4,6 +4,7 @@ goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.functions'); goog.require('goog.math'); +goog.require('goog.object'); goog.require('ol'); goog.require('ol.Coordinate'); goog.require('ol.TileCoord'); @@ -69,6 +70,10 @@ ol.tilegrid.TileGrid = function(options) { goog.asserts.assert(this.origins_.length == this.resolutions_.length, 'number of origins and resolutions must be equal'); } + if (goog.isNull(this.origins_) && goog.isNull(this.origin_) && + goog.isDef(options.extent)) { + this.origin_ = ol.extent.getBottomLeft(options.extent); + } goog.asserts.assert( (goog.isNull(this.origin_) && !goog.isNull(this.origins_)) || (!goog.isNull(this.origin_) && goog.isNull(this.origins_)), @@ -97,6 +102,51 @@ ol.tilegrid.TileGrid = function(options) { (!goog.isNull(this.tileSize_) && goog.isNull(this.tileSizes_)), 'either tileSize or tileSizes must be configured, never both'); + var extent = options.extent; + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = goog.isDef(extent) ? extent : null; + + /** + * @private + * @type {Array.} + */ + this.fullTileRanges_ = null; + + if (goog.isDef(options.sizes)) { + goog.asserts.assert(options.sizes.length == this.resolutions_.length, + 'number of sizes and resolutions must be equal'); + this.fullTileRanges_ = goog.array.map(options.sizes, function(size, z) { + goog.asserts.assert(size[0] > 0, 'width must be > 0'); + goog.asserts.assert(size[1] !== 0, 'height must not be 0'); + var tileRange = new ol.TileRange(0, size[0] - 1, 0, size[1] - 1); + if (tileRange.maxY < tileRange.minY) { + tileRange.minY = size[1]; + tileRange.maxY = -1; + } + if (this.minZoom <= z && z <= this.maxZoom && goog.isDef(extent)) { + goog.asserts.assert(tileRange.containsTileRange( + this.getTileRangeForExtentAndZ(extent, z)), + 'extent tile range must not exceed tilegrid width and height'); + } + return tileRange; + }, this); + } else if (goog.isDef(extent)) { + var extentWidth = ol.extent.getWidth(extent); + var extentHeight = ol.extent.getHeight(extent); + var fullTileRanges = new Array(this.resolutions_.length); + var tileSize; + for (var z = 0, zz = fullTileRanges.length; z < zz; ++z) { + tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_); + fullTileRanges[z] = new ol.TileRange( + 0, Math.ceil(extentWidth / tileSize[0] / this.resolutions_[z]) - 1, + 0, Math.ceil(extentHeight / tileSize[1] / this.resolutions_[z]) - 1); + } + this.fullTileRanges_ = fullTileRanges; + } /** * @private @@ -104,17 +154,6 @@ ol.tilegrid.TileGrid = function(options) { */ this.tmpSize_ = [0, 0]; - /** - * @private - * @type {Array.} - */ - this.widths_ = null; - if (goog.isDef(options.widths)) { - this.widths_ = options.widths; - goog.asserts.assert(this.widths_.length == this.resolutions_.length, - 'number of widths and resolutions must be equal'); - } - }; @@ -161,6 +200,15 @@ ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange = }; +/** + * Get the extent for this tile grid, if it was configured. + * @return {ol.Extent} Extent. + */ +ol.tilegrid.TileGrid.prototype.getExtent = function() { + return this.extent_; +}; + + /** * Get the maximum zoom level for the grid. * @return {number} Max zoom. @@ -365,8 +413,11 @@ ol.tilegrid.TileGrid.prototype.getTileCoordForXYAndResolution_ = function( var origin = this.getOrigin(z); var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_); - var tileCoordX = scale * (x - origin[0]) / (resolution * tileSize[0]); - var tileCoordY = scale * (y - origin[1]) / (resolution * tileSize[1]); + var adjust = reverseIntersectionPolicy ? 0.5 : 0; + var xFromOrigin = ((x - origin[0]) / resolution + adjust) | 0; + var yFromOrigin = ((y - origin[1]) / resolution + adjust) | 0; + var tileCoordX = scale * xFromOrigin / tileSize[0]; + var tileCoordY = scale * yFromOrigin / tileSize[1]; if (reverseIntersectionPolicy) { tileCoordX = Math.ceil(tileCoordX) - 1; @@ -409,25 +460,6 @@ 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); -}; - - /** * Get the tile size for a zoom level. The type of the return value matches the * `tileSize` or `tileSizes` that the tile grid was configured with. To always @@ -452,15 +484,16 @@ 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. + * @return {ol.TileRange} Extent tile range for the specified zoom level. */ -ol.tilegrid.TileGrid.prototype.getWidth = function(z) { - if (!goog.isNull(this.widths_)) { +ol.tilegrid.TileGrid.prototype.getFullTileRange = function(z) { + if (goog.isNull(this.fullTileRanges_)) { + return null; + } else { goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom, 'z is not in allowed range (%s <= %s <= %s', this.minZoom, z, this.maxZoom); - return this.widths_[z]; + return this.fullTileRanges_[z]; } }; @@ -475,26 +508,6 @@ 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 ol.size.toSize(this.getTileSize(z), this.tmpSize_)[0] * 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. @@ -521,36 +534,48 @@ ol.tilegrid.getForProjection = function(projection) { */ ol.tilegrid.createForExtent = function(extent, opt_maxZoom, opt_tileSize, opt_corner) { - var tileSize = goog.isDef(opt_tileSize) ? - ol.size.toSize(opt_tileSize) : ol.size.toSize(ol.DEFAULT_TILE_SIZE); - var corner = goog.isDef(opt_corner) ? opt_corner : ol.extent.Corner.BOTTOM_LEFT; var resolutions = ol.tilegrid.resolutionsFromExtent( - extent, opt_maxZoom, ol.size.toSize(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[0] / resolutions[z]; - } + extent, opt_maxZoom, opt_tileSize); return new ol.tilegrid.TileGrid({ + extent: extent, origin: ol.extent.getCorner(extent, corner), resolutions: resolutions, - tileSize: goog.isDef(opt_tileSize) ? opt_tileSize : ol.DEFAULT_TILE_SIZE, - widths: widths + tileSize: opt_tileSize }); }; +/** + * Creates a tile grid with a standard XYZ tiling scheme. + * @param {olx.tilegrid.XYZOptions=} opt_options Tile grid options. + * @return {ol.tilegrid.TileGrid} Tile grid instance. + * @api + */ +ol.tilegrid.createXYZ = function(opt_options) { + var options = /** @type {olx.tilegrid.TileGridOptions} */ ({}); + goog.object.extend(options, goog.isDef(opt_options) ? + opt_options : /** @type {olx.tilegrid.XYZOptions} */ ({})); + if (!goog.isDef(options.extent)) { + options.extent = ol.proj.get('EPSG:3857').getExtent(); + } + options.resolutions = ol.tilegrid.resolutionsFromExtent( + options.extent, options.maxZoom, options.tileSize); + delete options.maxZoom; + return new ol.tilegrid.TileGrid(options); +}; + + /** * Create a resolutions array from an extent. A zoom factor of 2 is assumed. * @param {ol.Extent} extent Extent. * @param {number=} opt_maxZoom Maximum zoom level (default is * ol.DEFAULT_MAX_ZOOM). - * @param {ol.Size=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE). + * @param {number|ol.Size=} opt_tileSize Tile size (default uses + * ol.DEFAULT_TILE_SIZE). * @return {!Array.} Resolutions array. */ ol.tilegrid.resolutionsFromExtent = @@ -561,8 +586,8 @@ ol.tilegrid.resolutionsFromExtent = var height = ol.extent.getHeight(extent); var width = ol.extent.getWidth(extent); - var tileSize = goog.isDef(opt_tileSize) ? - opt_tileSize : ol.size.toSize(ol.DEFAULT_TILE_SIZE); + var tileSize = ol.size.toSize(goog.isDef(opt_tileSize) ? + opt_tileSize : ol.DEFAULT_TILE_SIZE); var maxResolution = Math.max( width / tileSize[0], height / tileSize[1]); @@ -608,3 +633,32 @@ ol.tilegrid.extentFromProjection = function(projection) { } return extent; }; + + +/** + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @return {function(ol.TileCoord, ol.proj.Projection, ol.TileCoord=): + * ol.TileCoord} Tile coordinate transform. + */ +ol.tilegrid.createOriginTopLeftTileCoordTransform = function(tileGrid) { + goog.asserts.assert(!goog.isNull(tileGrid), 'tileGrid required'); + return ( + /** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.proj.Projection} projection Projection. + * @param {ol.TileCoord=} opt_tileCoord Destination tile coordinate. + * @return {ol.TileCoord} Tile coordinate. + */ + function(tileCoord, projection, opt_tileCoord) { + if (goog.isNull(tileCoord)) { + return null; + } + var z = tileCoord[0]; + var fullTileRange = tileGrid.getFullTileRange(z); + var height = (goog.isNull(fullTileRange) || fullTileRange.minY < 0) ? + 0 : fullTileRange.getHeight(); + return ol.tilecoord.createOrUpdate( + z, tileCoord[1], height - tileCoord[2] - 1, opt_tileCoord); + } + ); +}; diff --git a/src/ol/tilegrid/wmtstilegrid.js b/src/ol/tilegrid/wmtstilegrid.js index 6770358784..74838aa2e9 100644 --- a/src/ol/tilegrid/wmtstilegrid.js +++ b/src/ol/tilegrid/wmtstilegrid.js @@ -32,12 +32,13 @@ ol.tilegrid.WMTS = function(options) { // FIXME: should the matrixIds become optionnal? goog.base(this, { + extent: options.extent, origin: options.origin, origins: options.origins, resolutions: options.resolutions, tileSize: options.tileSize, tileSizes: options.tileSizes, - widths: options.widths + sizes: options.sizes }); }; @@ -69,11 +70,13 @@ ol.tilegrid.WMTS.prototype.getMatrixIds = function() { * Create a tile grid from a WMTS capabilities matrix set. * @param {Object} matrixSet An object representing a matrixSet in the * capabilities document. + * @param {ol.Extent=} opt_extent An optional extent to restrict the tile + * ranges the server provides. * @return {ol.tilegrid.WMTS} WMTS tileGrid instance. * @api */ ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = - function(matrixSet) { + function(matrixSet, opt_extent) { /** @type {!Array.} */ var resolutions = []; @@ -83,8 +86,8 @@ ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = var origins = []; /** @type {!Array.} */ var tileSizes = []; - /** @type {!Array.} */ - var widths = []; + /** @type {!Array.} */ + var sizes = []; var supportedCRSPropName = 'SupportedCRS'; var matrixIdsPropName = 'TileMatrix'; @@ -108,26 +111,30 @@ ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = goog.array.forEach(matrixSet[matrixIdsPropName], function(elt, index, array) { matrixIds.push(elt[identifierPropName]); + var resolution = elt[scaleDenominatorPropName] * 0.28E-3 / + metersPerUnit; + var tileWidth = elt[tileWidthPropName]; + var tileHeight = elt[tileHeightPropName]; + var matrixHeight = elt['MatrixHeight']; if (switchOriginXY) { origins.push([elt[topLeftCornerPropName][1], elt[topLeftCornerPropName][0]]); } else { origins.push(elt[topLeftCornerPropName]); } - resolutions.push(elt[scaleDenominatorPropName] * 0.28E-3 / - metersPerUnit); - var tileWidth = elt[tileWidthPropName]; - var tileHeight = elt[tileHeightPropName]; + resolutions.push(resolution); tileSizes.push(tileWidth == tileHeight ? tileWidth : [tileWidth, tileHeight]); - widths.push(elt['MatrixWidth']); + // top-left origin, so height is negative + sizes.push([elt['MatrixWidth'], -matrixHeight]); }); return new ol.tilegrid.WMTS({ + extent: opt_extent, origins: origins, resolutions: resolutions, matrixIds: matrixIds, tileSizes: tileSizes, - widths: widths + sizes: sizes }); }; diff --git a/src/ol/tilegrid/xyztilegrid.js b/src/ol/tilegrid/xyztilegrid.js deleted file mode 100644 index b10a398c67..0000000000 --- a/src/ol/tilegrid/xyztilegrid.js +++ /dev/null @@ -1,130 +0,0 @@ -goog.provide('ol.tilegrid.XYZ'); - -goog.require('goog.math'); -goog.require('ol'); -goog.require('ol.TileCoord'); -goog.require('ol.TileRange'); -goog.require('ol.extent'); -goog.require('ol.extent.Corner'); -goog.require('ol.proj'); -goog.require('ol.proj.EPSG3857'); -goog.require('ol.size'); -goog.require('ol.tilecoord'); -goog.require('ol.tilegrid.TileGrid'); - - - -/** - * @classdesc - * Set the grid pattern for sources accessing XYZ tiled-image servers. - * - * @constructor - * @extends {ol.tilegrid.TileGrid} - * @param {olx.tilegrid.XYZOptions} options XYZ options. - * @struct - * @api - */ -ol.tilegrid.XYZ = function(options) { - var extent = goog.isDef(options.extent) ? - options.extent : ol.proj.EPSG3857.EXTENT; - var tileSize; - if (goog.isDef(options.tileSize)) { - tileSize = ol.size.toSize(options.tileSize); - } - var resolutions = ol.tilegrid.resolutionsFromExtent( - extent, options.maxZoom, tileSize); - - goog.base(this, { - minZoom: options.minZoom, - origin: ol.extent.getCorner(extent, ol.extent.Corner.TOP_LEFT), - resolutions: resolutions, - tileSize: options.tileSize - }); - -}; -goog.inherits(ol.tilegrid.XYZ, ol.tilegrid.TileGrid); - - -/** - * @inheritDoc - */ -ol.tilegrid.XYZ.prototype.createTileCoordTransform = function(opt_options) { - var options = goog.isDef(opt_options) ? opt_options : {}; - var minZ = this.minZoom; - var maxZ = this.maxZoom; - /** @type {Array.} */ - var tileRangeByZ = null; - if (goog.isDef(options.extent)) { - tileRangeByZ = new Array(maxZ + 1); - var z; - for (z = 0; z <= maxZ; ++z) { - if (z < minZ) { - tileRangeByZ[z] = null; - } else { - tileRangeByZ[z] = this.getTileRangeForExtentAndZ(options.extent, z); - } - } - } - return ( - /** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.proj.Projection} projection Projection. - * @param {ol.TileCoord=} opt_tileCoord Destination tile coordinate. - * @return {ol.TileCoord} Tile coordinate. - */ - function(tileCoord, projection, opt_tileCoord) { - var z = tileCoord[0]; - if (z < minZ || maxZ < z) { - return null; - } - var n = Math.pow(2, z); - var x = tileCoord[1]; - var y = tileCoord[2]; - if (y < -n || -1 < y) { - return null; - } - if (!goog.isNull(tileRangeByZ)) { - if (!tileRangeByZ[z].containsXY(x, y)) { - return null; - } - } - return ol.tilecoord.createOrUpdate(z, x, -y - 1, opt_tileCoord); - }); -}; - - -/** - * @inheritDoc - */ -ol.tilegrid.XYZ.prototype.getTileCoordChildTileRange = - function(tileCoord, opt_tileRange) { - if (tileCoord[0] < this.maxZoom) { - var doubleX = 2 * tileCoord[1]; - var doubleY = 2 * tileCoord[2]; - return ol.TileRange.createOrUpdate( - doubleX, doubleX + 1, - doubleY, doubleY + 1, - opt_tileRange); - } else { - return null; - } -}; - - -/** - * @inheritDoc - */ -ol.tilegrid.XYZ.prototype.forEachTileCoordParentTileRange = - function(tileCoord, callback, opt_this, opt_tileRange) { - var tileRange = ol.TileRange.createOrUpdate( - 0, tileCoord[1], 0, tileCoord[2], opt_tileRange); - var z; - for (z = tileCoord[0] - 1; z >= this.minZoom; --z) { - tileRange.minX = tileRange.maxX >>= 1; - tileRange.minY = tileRange.maxY >>= 1; - if (callback.call(opt_this, z, tileRange)) { - return true; - } - } - return false; -}; diff --git a/test/spec/ol/source/tilesource.test.js b/test/spec/ol/source/tilesource.test.js index ddd2b610b1..b6c2a99030 100644 --- a/test/spec/ol/source/tilesource.test.js +++ b/test/spec/ol/source/tilesource.test.js @@ -115,22 +115,7 @@ describe('ol.source.Tile', function() { }); - describe('#getWrapXTileCoord()', function() { - - it('returns the expected tile coordinate - {wrapX: undefined}', function() { - var tileSource = new ol.source.Tile({ - projection: 'EPSG:3857' - }); - - var tileCoord = tileSource.getWrapXTileCoord([6, -31, 22]); - expect(tileCoord).to.eql([6, -31, 22]); - - tileCoord = tileSource.getWrapXTileCoord([6, 33, 22]); - expect(tileCoord).to.eql([6, 33, 22]); - - tileCoord = tileSource.getWrapXTileCoord([6, 97, 22]); - expect(tileCoord).to.eql([6, 97, 22]); - }); + describe('#getTileCoordForTileUrlFunction()', function() { it('returns the expected tile coordinate - {wrapX: true}', function() { var tileSource = new ol.source.Tile({ @@ -138,13 +123,13 @@ describe('ol.source.Tile', function() { wrapX: true }); - var tileCoord = tileSource.getWrapXTileCoord([6, -31, 22]); + var tileCoord = tileSource.getTileCoordForTileUrlFunction([6, -31, 22]); expect(tileCoord).to.eql([6, 33, 22]); - tileCoord = tileSource.getWrapXTileCoord([6, 33, 22]); + tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 33, 22]); expect(tileCoord).to.eql([6, 33, 22]); - tileCoord = tileSource.getWrapXTileCoord([6, 97, 22]); + tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 97, 22]); expect(tileCoord).to.eql([6, 33, 22]); }); @@ -154,13 +139,13 @@ describe('ol.source.Tile', function() { wrapX: false }); - var tileCoord = tileSource.getWrapXTileCoord([6, -31, 22]); + var tileCoord = tileSource.getTileCoordForTileUrlFunction([6, -31, 22]); expect(tileCoord).to.eql(null); - tileCoord = tileSource.getWrapXTileCoord([6, 33, 22]); + tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 33, 22]); expect(tileCoord).to.eql([6, 33, 22]); - tileCoord = tileSource.getWrapXTileCoord([6, 97, 22]); + tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 97, 22]); expect(tileCoord).to.eql(null); }); }); diff --git a/test/spec/ol/source/xyzsource.test.js b/test/spec/ol/source/xyzsource.test.js index e5d7cab36c..866f3c20b5 100644 --- a/test/spec/ol/source/xyzsource.test.js +++ b/test/spec/ol/source/xyzsource.test.js @@ -66,15 +66,18 @@ describe('ol.source.XYZ', function() { it('returns the expected URL', function() { var projection = xyzTileSource.getProjection(); var tileUrl = xyzTileSource.tileUrlFunction( - xyzTileSource.getWrapXTileCoord([6, -31, -23], projection)); + xyzTileSource.getTileCoordForTileUrlFunction( + [6, -31, 41], projection)); expect(tileUrl).to.eql('6/33/22'); tileUrl = xyzTileSource.tileUrlFunction( - xyzTileSource.getWrapXTileCoord([6, 33, -23], projection)); + xyzTileSource.getTileCoordForTileUrlFunction( + [6, 33, 41], projection)); expect(tileUrl).to.eql('6/33/22'); tileUrl = xyzTileSource.tileUrlFunction( - xyzTileSource.getWrapXTileCoord([6, 97, -23], projection)); + xyzTileSource.getTileCoordForTileUrlFunction( + [6, 97, 41], projection)); expect(tileUrl).to.eql('6/33/22'); }); @@ -83,14 +86,20 @@ describe('ol.source.XYZ', function() { describe('crop y', function() { it('returns the expected URL', function() { + var projection = xyzTileSource.getProjection(); var tileUrl = xyzTileSource.tileUrlFunction( - [6, 33, -87]); + xyzTileSource.getTileCoordForTileUrlFunction( + [6, 33, 150], projection)); expect(tileUrl).to.be(undefined); - tileUrl = xyzTileSource.tileUrlFunction([6, 33, -23]); + tileUrl = xyzTileSource.tileUrlFunction( + xyzTileSource.getTileCoordForTileUrlFunction( + [6, 33, 41], projection)); expect(tileUrl).to.eql('6/33/22'); - tileUrl = xyzTileSource.tileUrlFunction([6, 33, 41]); + tileUrl = xyzTileSource.tileUrlFunction( + xyzTileSource.getTileCoordForTileUrlFunction( + [6, 33, -23], projection)); expect(tileUrl).to.be(undefined); }); diff --git a/test/spec/ol/tilecoord.test.js b/test/spec/ol/tilecoord.test.js index d46c3483c8..f2bd42411d 100644 --- a/test/spec/ol/tilecoord.test.js +++ b/test/spec/ol/tilecoord.test.js @@ -46,7 +46,79 @@ describe('ol.TileCoord', function() { ol.tilecoord.hash(tileCoord2)); }); }); + + describe('restrictByExtentAndZ', function() { + + it('restricts by z', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + tileSize: 10, + resolutions: [2, 1], + minZoom: 1 + }); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, 0], tileGrid)) + .to.equal(null); + expect(ol.tilecoord.restrictByExtentAndZ([1, 0, 0], tileGrid)) + .to.eql([1, 0, 0]); + expect(ol.tilecoord.restrictByExtentAndZ([2, 0, 0], tileGrid)) + .to.equal(null); + }); + + it('restricts by extent when extent defines tile ranges', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + sizes: [[3, 3]], + tileSize: 10, + resolutions: [1] + }); + expect(ol.tilecoord.restrictByExtentAndZ([0, 1, 1], tileGrid)) + .to.eql([0, 1, 1]); + expect(ol.tilecoord.restrictByExtentAndZ([0, 2, 0], tileGrid)) + .to.equal(null); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, 2], tileGrid)) + .to.equal(null); + }); + + it('restricts by extent when sizes define tile ranges', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + origin: [10, 20], + sizes: [[3, 3]], + tileSize: 10, + resolutions: [1] + }); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, 0], tileGrid)) + .to.eql([0, 0, 0]); + expect(ol.tilecoord.restrictByExtentAndZ([0, -1, 0], tileGrid)) + .to.equal(null); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, -1], tileGrid)) + .to.equal(null); + expect(ol.tilecoord.restrictByExtentAndZ([0, 2, 2], tileGrid)) + .to.eql([0, 2, 2]); + expect(ol.tilecoord.restrictByExtentAndZ([0, 3, 0], tileGrid)) + .to.equal(null); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, 3], tileGrid)) + .to.equal(null); + }); + + it('does not restrict by extent with no extent or sizes', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + origin: [10, 20], + tileSize: 10, + resolutions: [1] + }); + expect(ol.tilecoord.restrictByExtentAndZ([0, Infinity, 0], tileGrid)) + .to.eql([0, Infinity, 0]); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, Infinity], tileGrid)) + .to.eql([0, 0, Infinity]); + expect(ol.tilecoord.restrictByExtentAndZ([0, -Infinity, 0], tileGrid)) + .to.eql([0, -Infinity, 0]); + expect(ol.tilecoord.restrictByExtentAndZ([0, 0, Infinity], tileGrid)) + .to.eql([0, 0, Infinity]); + }); + }); + }); goog.require('ol.TileCoord'); goog.require('ol.tilecoord'); +goog.require('ol.tilegrid.TileGrid'); diff --git a/test/spec/ol/tilegrid/tilegrid.test.js b/test/spec/ol/tilegrid/tilegrid.test.js index a8b53c8860..757959a982 100644 --- a/test/spec/ol/tilegrid/tilegrid.test.js +++ b/test/spec/ol/tilegrid/tilegrid.test.js @@ -150,6 +150,129 @@ describe('ol.tilegrid.TileGrid', function() { }); }); + describe('create with extent exceeding tile ranges', function() { + it('throws an exception', function() { + expect(function() { + return new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + sizes: [[1, 1]], + tileSize: 10, + resolutions: [1] + }); + }).to.throwException(); + expect(function() { + return new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + origin: [10, 40], // top-left origin + sizes: [[3, 3]], // would have to be [[3, -3]] for this to not throw + tileSize: 10, + resolutions: [1] + }); + }).to.throwException(); + }); + }); + + describe('create with origin', function() { + var tileGrid; + beforeEach(function() { + tileGrid = new ol.tilegrid.TileGrid({ + origin: [10, 20], + tileSize: 10, + resolutions: [1] + }); + }); + + it('returns the configured origin', function() { + expect(tileGrid.getOrigin()).to.eql([10, 20]); + }); + + it('returns null for an unknown extent', function() { + expect(tileGrid.getExtent()).to.equal(null); + }); + + it('returns null for an unknown full tile range', function() { + expect(tileGrid.getFullTileRange(0)).to.equal(null); + }); + }); + + describe('create with extent', function() { + var tileGrid; + beforeEach(function() { + tileGrid = new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + tileSize: 10, + resolutions: [1] + }); + }); + + it('assumes bottom left corner of extent as origin', function() { + expect(tileGrid.getOrigin()).to.eql([10, 20]); + }); + + it('calculates full tile ranges from extent', function() { + var fullTileRange = tileGrid.getFullTileRange(0); + expect(fullTileRange.minX).to.equal(0); + expect(fullTileRange.maxX).to.equal(1); + expect(fullTileRange.minY).to.equal(0); + expect(fullTileRange.maxY).to.equal(1); + }); + }); + + describe('create with extent and sizes', function() { + var tileGrid; + beforeEach(function() { + tileGrid = new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + sizes: [[3, 3]], + tileSize: 10, + resolutions: [1] + }); + }); + + it('returns the configured extent', function() { + expect(tileGrid.getExtent()).to.eql([10, 20, 30, 40]); + }); + + it('calculates full tile ranges from sizes', function() { + var fullTileRange = tileGrid.getFullTileRange(0); + expect(fullTileRange.minX).to.equal(0); + expect(fullTileRange.maxX).to.equal(2); + expect(fullTileRange.minY).to.equal(0); + expect(fullTileRange.maxY).to.equal(2); + }); + }); + + describe('create with top-left origin and sizes', function() { + var tileGrid; + beforeEach(function() { + tileGrid = new ol.tilegrid.TileGrid({ + origin: [10, 40], + sizes: [[3, -3]], + tileSize: 10, + resolutions: [1] + }); + }); + + it('calculates correct minX and maxX for negative heights', function() { + var fullTileRange = tileGrid.getFullTileRange(0); + expect(fullTileRange.minY).to.equal(-3); + expect(fullTileRange.maxY).to.equal(-1); + }); + }); + + describe('create with extent and origin', function() { + it('uses both origin and extent', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + origin: [0, 0], + extent: [10, 20, 30, 40], + tileSize: 10, + resolutions: [1] + }); + expect(tileGrid.getOrigin()).to.eql([0, 0]); + expect(tileGrid.getExtent()).to.eql([10, 20, 30, 40]); + }); + }); + describe('createForExtent', function() { it('allows creation of tile grid from extent', function() { var extent = ol.extent.createOrUpdate(-100, -100, 100, 100); @@ -247,6 +370,104 @@ describe('ol.tilegrid.TileGrid', function() { }); + describe('createOriginTopLeftTileCoordTransform', function() { + + it('transforms y to -y-1 for top-left origins', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + origin: [10, 40], + sizes: [[2, -2], [4, -4]], + resolutions: [1, 0.5], + tileSize: 10 + }); + var transformFn = + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid); + expect(transformFn([0, 0, -2])).to.eql([0, 0, 1]); + expect(transformFn([0, 0, -1])).to.eql([0, 0, 0]); + expect(transformFn([1, 0, -4])).to.eql([1, 0, 3]); + expect(transformFn([1, 0, -1])).to.eql([1, 0, 0]); + }); + + it('transforms y to -y-1 when origin corner is not specified', function() { + var tileGrid1 = new ol.tilegrid.TileGrid({ + origin: [10, 20], + resolutions: [1, 0.5], + tileSize: 10 + }); + var tileGrid = new ol.tilegrid.TileGrid({ + origin: [10, 40], + resolutions: [1, 0.5], + tileSize: 10 + }); + var transformFn1 = + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid); + var transformFn2 = + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid1); + expect(transformFn1([0, 0, -2])).to.eql([0, 0, 1]); + expect(transformFn2([0, 0, -2])).to.eql([0, 0, 1]); + expect(transformFn1([0, 0, -1])).to.eql([0, 0, 0]); + expect(transformFn2([0, 0, -1])).to.eql([0, 0, 0]); + expect(transformFn1([1, 0, -4])).to.eql([1, 0, 3]); + expect(transformFn2([1, 0, -4])).to.eql([1, 0, 3]); + expect(transformFn1([1, 0, -1])).to.eql([1, 0, 0]); + expect(transformFn2([1, 0, -1])).to.eql([1, 0, 0]); + }); + + it('transforms y to height-y-1 for bottom-left origins', function() { + var tileGrid1 = new ol.tilegrid.TileGrid({ + extent: [10, 20, 30, 40], + resolutions: [1, 0.5], + tileSize: 10 + }); + var tileGrid2 = new ol.tilegrid.TileGrid({ + origin: [10, 20], + sizes: [[2, 2], [4, 4]], + resolutions: [1, 0.5], + tileSize: 10 + }); + var transformFn1 = + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid1); + var transformFn2 = + ol.tilegrid.createOriginTopLeftTileCoordTransform(tileGrid2); + expect(tileGrid1.getFullTileRange(0).getHeight()).to.equal(2); + expect(transformFn1([0, 0, 0])).to.eql([0, 0, 1]); + expect(transformFn2([0, 0, 0])).to.eql([0, 0, 1]); + expect(transformFn1([0, 0, 1])).to.eql([0, 0, 0]); + expect(transformFn2([0, 0, 1])).to.eql([0, 0, 0]); + expect(tileGrid1.getFullTileRange(1).getHeight()).to.equal(4); + expect(transformFn1([1, 0, 0])).to.eql([1, 0, 3]); + expect(transformFn2([1, 0, 0])).to.eql([1, 0, 3]); + expect(transformFn1([1, 0, 3])).to.eql([1, 0, 0]); + expect(transformFn2([1, 0, 3])).to.eql([1, 0, 0]); + }); + + }); + + describe('createXYZ()', function() { + + it('uses defaults', function() { + var tileGrid = ol.tilegrid.createXYZ(); + expect(tileGrid.getExtent()).to.eql( + ol.proj.get('EPSG:3857').getExtent()); + expect(tileGrid.getMinZoom()).to.equal(0); + expect(tileGrid.getMaxZoom()).to.equal(ol.DEFAULT_MAX_ZOOM); + expect(tileGrid.getTileSize()).to.equal(ol.DEFAULT_TILE_SIZE); + }); + + it('respects configuration options', function() { + var tileGrid = ol.tilegrid.createXYZ({ + extent: [10, 20, 30, 40], + minZoom: 1, + maxZoom: 2, + tileSize: 128 + }); + expect(tileGrid.getExtent()).to.eql([10, 20, 30, 40]); + expect(tileGrid.getMinZoom()).to.equal(1); + expect(tileGrid.getMaxZoom()).to.equal(2); + expect(tileGrid.getTileSize()).to.equal(128); + }); + + }); + describe('getForProjection', function() { it('gets the default tile grid for a projection', function() { @@ -269,7 +490,150 @@ describe('ol.tilegrid.TileGrid', function() { }); - describe('getTileCoordFromCoordAndZ', function() { + describe('#getTileCoordChildTileRange()', function() { + + var tileGrid; + beforeEach(function() { + tileGrid = ol.tilegrid.createForExtent( + ol.proj.get('EPSG:3857').getExtent(), 22); + }); + + it('returns the tile range for one zoom level deeper', function() { + var range; + + range = tileGrid.getTileCoordChildTileRange([0, 0, 0]); + expect(range.minX).to.be(0); + expect(range.maxX).to.be(1); + expect(range.minY).to.be(0); + expect(range.maxY).to.be(1); + + range = tileGrid.getTileCoordChildTileRange([0, 1, 0]); + expect(range.minX).to.be(2); + expect(range.maxX).to.be(3); + expect(range.minY).to.be(0); + expect(range.maxY).to.be(1); + + range = tileGrid.getTileCoordChildTileRange([0, 0, 1]); + expect(range.minX).to.be(0); + expect(range.maxX).to.be(1); + expect(range.minY).to.be(2); + expect(range.maxY).to.be(3); + + range = tileGrid.getTileCoordChildTileRange([0, -1, 0]); + expect(range.minX).to.be(-2); + expect(range.maxX).to.be(-1); + expect(range.minY).to.be(0); + expect(range.maxY).to.be(1); + + range = tileGrid.getTileCoordChildTileRange([0, 0, -1]); + expect(range.minX).to.be(0); + expect(range.maxX).to.be(1); + expect(range.minY).to.be(-2); + expect(range.maxY).to.be(-1); + }); + + it('returns null for z > maxZoom', function() { + var max = tileGrid.maxZoom; + var range = tileGrid.getTileCoordChildTileRange([max + 1, 0, 0]); + expect(range).to.be(null); + }); + + }); + + describe('#forEachTileCoordParentTileRange()', function() { + + var tileGrid; + beforeEach(function() { + tileGrid = ol.tilegrid.createForExtent( + ol.proj.get('EPSG:3857').getExtent(), 22); + }); + + it('iterates as expected', function() { + + var tileCoord = [5, 11, 21]; + var zs = [], tileRanges = []; + tileGrid.forEachTileCoordParentTileRange( + tileCoord, + function(z, tileRange) { + zs.push(z); + tileRanges.push(new ol.TileRange( + tileRange.minX, tileRange.maxX, + tileRange.minY, tileRange.maxY)); + return false; + }); + + expect(zs.length).to.eql(5); + expect(tileRanges.length).to.eql(5); + + expect(zs[0]).to.eql(4); + expect(tileRanges[0].minX).to.eql(5); + expect(tileRanges[0].maxX).to.eql(5); + expect(tileRanges[0].minY).to.eql(10); + expect(tileRanges[0].maxY).to.eql(10); + + expect(zs[1]).to.eql(3); + expect(tileRanges[1].minX).to.eql(2); + expect(tileRanges[1].maxX).to.eql(2); + expect(tileRanges[1].minY).to.eql(5); + expect(tileRanges[1].maxY).to.eql(5); + + expect(zs[2]).to.eql(2); + expect(tileRanges[2].minX).to.eql(1); + expect(tileRanges[2].maxX).to.eql(1); + expect(tileRanges[2].minY).to.eql(2); + expect(tileRanges[2].maxY).to.eql(2); + + expect(zs[3]).to.eql(1); + expect(tileRanges[3].minX).to.eql(0); + expect(tileRanges[3].maxX).to.eql(0); + expect(tileRanges[3].minY).to.eql(1); + expect(tileRanges[3].maxY).to.eql(1); + + expect(zs[4]).to.eql(0); + expect(tileRanges[4].minX).to.eql(0); + expect(tileRanges[4].maxX).to.eql(0); + expect(tileRanges[4].minY).to.eql(0); + expect(tileRanges[4].maxY).to.eql(0); + + }); + + }); + + describe('getResolution', function() { + + var tileGrid; + beforeEach(function() { + tileGrid = ol.tilegrid.createForExtent( + ol.proj.get('EPSG:3857').getExtent(), 22); + }); + + it('returns the correct resolution at the equator', function() { + // @see http://msdn.microsoft.com/en-us/library/aa940990.aspx + expect(tileGrid.getResolution(0)).to.roughlyEqual(156543.04, 1e-2); + expect(tileGrid.getResolution(1)).to.roughlyEqual(78271.52, 1e-2); + expect(tileGrid.getResolution(2)).to.roughlyEqual(39135.76, 1e-2); + expect(tileGrid.getResolution(3)).to.roughlyEqual(19567.88, 1e-2); + expect(tileGrid.getResolution(4)).to.roughlyEqual(9783.94, 1e-2); + expect(tileGrid.getResolution(5)).to.roughlyEqual(4891.97, 1e-2); + expect(tileGrid.getResolution(6)).to.roughlyEqual(2445.98, 1e-2); + expect(tileGrid.getResolution(7)).to.roughlyEqual(1222.99, 1e-2); + expect(tileGrid.getResolution(8)).to.roughlyEqual(611.50, 1e-2); + expect(tileGrid.getResolution(9)).to.roughlyEqual(305.75, 1e-2); + expect(tileGrid.getResolution(10)).to.roughlyEqual(152.87, 1e-2); + expect(tileGrid.getResolution(11)).to.roughlyEqual(76.44, 1e-2); + expect(tileGrid.getResolution(12)).to.roughlyEqual(38.22, 1e-2); + expect(tileGrid.getResolution(13)).to.roughlyEqual(19.11, 1e-2); + expect(tileGrid.getResolution(14)).to.roughlyEqual(9.55, 1e-2); + expect(tileGrid.getResolution(15)).to.roughlyEqual(4.78, 1e-2); + expect(tileGrid.getResolution(16)).to.roughlyEqual(2.39, 1e-2); + expect(tileGrid.getResolution(17)).to.roughlyEqual(1.19, 1e-2); + expect(tileGrid.getResolution(18)).to.roughlyEqual(0.60, 1e-2); + expect(tileGrid.getResolution(19)).to.roughlyEqual(0.30, 1e-2); + }); + + }); + + describe('#getTileCoordFromCoordAndZ()', function() { describe('Y North, X East', function() { it('returns the expected TileCoord', function() { @@ -696,7 +1060,7 @@ describe('ol.tilegrid.TileGrid', function() { }); }); - describe('getZForResolution (approcimate)', function() { + describe('getZForResolution (approximate)', function() { it('returns the expected z value', function() { var tileGrid = new ol.tilegrid.TileGrid({ resolutions: resolutions, @@ -731,4 +1095,5 @@ goog.require('ol.proj'); goog.require('ol.proj.EPSG3857'); goog.require('ol.proj.Projection'); goog.require('ol.proj.Units'); +goog.require('ol.TileRange'); goog.require('ol.tilegrid.TileGrid'); diff --git a/test/spec/ol/tilegrid/wmtstilegrid.test.js b/test/spec/ol/tilegrid/wmtstilegrid.test.js index 1e03436c23..966dacb59e 100644 --- a/test/spec/ol/tilegrid/wmtstilegrid.test.js +++ b/test/spec/ol/tilegrid/wmtstilegrid.test.js @@ -42,27 +42,17 @@ describe('ol.tilegrid.WMTS', function() { expect(tileGrid.origins_).to.be.an('array'); expect(tileGrid.origins_).to.have.length(20); expect(tileGrid.origins_).to.eql( - [[-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428], - [-20037508.3428, 20037508.3428], [-20037508.3428, 20037508.3428] - ]); + goog.array.repeat([-20037508.3428, 20037508.3428], 20)); expect(tileGrid.tileSizes_).to.be.an('array'); expect(tileGrid.tileSizes_).to.have.length(20); expect(tileGrid.tileSizes_).to.eql( - [256, 256, 256, 256, 256, 256, 256, 256, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, 256, 256]); + goog.array.repeat(256, 20)); }); }); }); +goog.require('goog.array'); goog.require('ol.format.WMTSCapabilities'); goog.require('ol.tilegrid.WMTS'); diff --git a/test/spec/ol/tilegrid/xyztilegrid.test.js b/test/spec/ol/tilegrid/xyztilegrid.test.js deleted file mode 100644 index b82079d354..0000000000 --- a/test/spec/ol/tilegrid/xyztilegrid.test.js +++ /dev/null @@ -1,168 +0,0 @@ -goog.provide('ol.test.tilegrid.XYZ'); - - -describe('ol.tilegrid.XYZ', function() { - - var xyzTileGrid; - beforeEach(function() { - xyzTileGrid = new ol.tilegrid.XYZ({ - maxZoom: 22 - }); - }); - - describe('#getTileCoordChildTileRange()', function() { - - it('returns the tile range for one zoom level deeper', function() { - var range; - - range = xyzTileGrid.getTileCoordChildTileRange([0, 0, 0]); - expect(range.minX).to.be(0); - expect(range.maxX).to.be(1); - expect(range.minY).to.be(0); - expect(range.maxY).to.be(1); - - range = xyzTileGrid.getTileCoordChildTileRange([0, 1, 0]); - expect(range.minX).to.be(2); - expect(range.maxX).to.be(3); - expect(range.minY).to.be(0); - expect(range.maxY).to.be(1); - - range = xyzTileGrid.getTileCoordChildTileRange([0, 0, 1]); - expect(range.minX).to.be(0); - expect(range.maxX).to.be(1); - expect(range.minY).to.be(2); - expect(range.maxY).to.be(3); - - range = xyzTileGrid.getTileCoordChildTileRange([0, -1, 0]); - expect(range.minX).to.be(-2); - expect(range.maxX).to.be(-1); - expect(range.minY).to.be(0); - expect(range.maxY).to.be(1); - - range = xyzTileGrid.getTileCoordChildTileRange([0, 0, -1]); - expect(range.minX).to.be(0); - expect(range.maxX).to.be(1); - expect(range.minY).to.be(-2); - expect(range.maxY).to.be(-1); - }); - - it('returns null for z > maxZoom', function() { - var max = xyzTileGrid.maxZoom; - var range = xyzTileGrid.getTileCoordChildTileRange([max + 1, 0, 0]); - expect(range).to.be(null); - }); - - it('is like ol.tilegrid.TileGrid#getTileCoordChildTileRange()', function() { - var superMethod = ol.tilegrid.TileGrid.prototype - .getTileCoordChildTileRange.bind(xyzTileGrid); - - var coord, selfRange, superRange; - - coord = [0, 0, 0]; - selfRange = xyzTileGrid.getTileCoordChildTileRange(coord); - superRange = superMethod(coord); - expect(selfRange.minX).to.be(superRange.minX); - expect(selfRange.maxX).to.be(superRange.maxX); - expect(selfRange.minY).to.be(superRange.minY); - expect(selfRange.maxY).to.be(superRange.maxY); - - coord = [1, 2, 3]; - selfRange = xyzTileGrid.getTileCoordChildTileRange(coord); - superRange = superMethod(coord); - expect(selfRange.minX).to.be(superRange.minX); - expect(selfRange.maxX).to.be(superRange.maxX); - expect(selfRange.minY).to.be(superRange.minY); - expect(selfRange.maxY).to.be(superRange.maxY); - - }); - - }); - - describe('forEachTileCoordParentTileRange', function() { - - it('iterates as expected', function() { - - var tileCoord = [5, 11, 21]; - var zs = [], tileRanges = []; - xyzTileGrid.forEachTileCoordParentTileRange( - tileCoord, - function(z, tileRange) { - zs.push(z); - tileRanges.push(new ol.TileRange( - tileRange.minX, tileRange.maxX, - tileRange.minY, tileRange.maxY)); - return false; - }); - - expect(zs.length).to.eql(5); - expect(tileRanges.length).to.eql(5); - - expect(zs[0]).to.eql(4); - expect(tileRanges[0].minX).to.eql(5); - expect(tileRanges[0].maxX).to.eql(5); - expect(tileRanges[0].minY).to.eql(10); - expect(tileRanges[0].maxY).to.eql(10); - - expect(zs[1]).to.eql(3); - expect(tileRanges[1].minX).to.eql(2); - expect(tileRanges[1].maxX).to.eql(2); - expect(tileRanges[1].minY).to.eql(5); - expect(tileRanges[1].maxY).to.eql(5); - - expect(zs[2]).to.eql(2); - expect(tileRanges[2].minX).to.eql(1); - expect(tileRanges[2].maxX).to.eql(1); - expect(tileRanges[2].minY).to.eql(2); - expect(tileRanges[2].maxY).to.eql(2); - - expect(zs[3]).to.eql(1); - expect(tileRanges[3].minX).to.eql(0); - expect(tileRanges[3].maxX).to.eql(0); - expect(tileRanges[3].minY).to.eql(1); - expect(tileRanges[3].maxY).to.eql(1); - - expect(zs[4]).to.eql(0); - expect(tileRanges[4].minX).to.eql(0); - expect(tileRanges[4].maxX).to.eql(0); - expect(tileRanges[4].minY).to.eql(0); - expect(tileRanges[4].maxY).to.eql(0); - - }); - - }); - - describe('getResolution', function() { - - it('returns the correct resolution at the equator', function() { - // @see http://msdn.microsoft.com/en-us/library/aa940990.aspx - expect(xyzTileGrid.getResolution(0)).to.roughlyEqual(156543.04, 1e-2); - expect(xyzTileGrid.getResolution(1)).to.roughlyEqual(78271.52, 1e-2); - expect(xyzTileGrid.getResolution(2)).to.roughlyEqual(39135.76, 1e-2); - expect(xyzTileGrid.getResolution(3)).to.roughlyEqual(19567.88, 1e-2); - expect(xyzTileGrid.getResolution(4)).to.roughlyEqual(9783.94, 1e-2); - expect(xyzTileGrid.getResolution(5)).to.roughlyEqual(4891.97, 1e-2); - expect(xyzTileGrid.getResolution(6)).to.roughlyEqual(2445.98, 1e-2); - expect(xyzTileGrid.getResolution(7)).to.roughlyEqual(1222.99, 1e-2); - expect(xyzTileGrid.getResolution(8)).to.roughlyEqual(611.50, 1e-2); - expect(xyzTileGrid.getResolution(9)).to.roughlyEqual(305.75, 1e-2); - expect(xyzTileGrid.getResolution(10)).to.roughlyEqual(152.87, 1e-2); - expect(xyzTileGrid.getResolution(11)).to.roughlyEqual(76.44, 1e-2); - expect(xyzTileGrid.getResolution(12)).to.roughlyEqual(38.22, 1e-2); - expect(xyzTileGrid.getResolution(13)).to.roughlyEqual(19.11, 1e-2); - expect(xyzTileGrid.getResolution(14)).to.roughlyEqual(9.55, 1e-2); - expect(xyzTileGrid.getResolution(15)).to.roughlyEqual(4.78, 1e-2); - expect(xyzTileGrid.getResolution(16)).to.roughlyEqual(2.39, 1e-2); - expect(xyzTileGrid.getResolution(17)).to.roughlyEqual(1.19, 1e-2); - expect(xyzTileGrid.getResolution(18)).to.roughlyEqual(0.60, 1e-2); - expect(xyzTileGrid.getResolution(19)).to.roughlyEqual(0.30, 1e-2); - }); - - }); - -}); - - -goog.require('ol.TileCoord'); -goog.require('ol.TileRange'); -goog.require('ol.tilegrid.TileGrid'); -goog.require('ol.tilegrid.XYZ'); diff --git a/test_rendering/spec/ol/layer/image.test.js b/test_rendering/spec/ol/layer/image.test.js index 692090e015..4ba4c44eb0 100644 --- a/test_rendering/spec/ol/layer/image.test.js +++ b/test_rendering/spec/ol/layer/image.test.js @@ -55,8 +55,8 @@ describe('ol.rendering.layer.Image', function() { beforeEach(function() { source = new ol.source.ImageStatic({ url: 'spec/ol/data/tiles/osm/5/5/12.png', - imageExtent: new ol.tilegrid.XYZ({}).getTileCoordExtent( - [5, 5, -12 - 1]), + imageExtent: ol.tilegrid.createXYZ().getTileCoordExtent( + [5, 5, 32 - 12 - 1]), projection: ol.proj.get('EPSG:3857') }); }); @@ -91,4 +91,3 @@ goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.layer.Image'); goog.require('ol.source.ImageStatic'); -goog.require('ol.tilegrid.XYZ');