From 2bcb10c9735f9993b6df1f426e31fc0ce6eb5932 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 15 Jun 2015 11:15:25 +0200 Subject: [PATCH] Respect the tile grid's extent By adding a getTileCoordForTileUrlFuction method like for ol.source.Tile, we can now properly handle extent and resolution restrictions, and reuse tiles on wrapped worlds. Also adds the missing wrapX option to ol.source.TileVector. --- externs/olx.js | 13 ++++- src/ol/source/tilevectorsource.js | 33 ++++++++++-- test/spec/ol/source/tilevectorsource.test.js | 54 +++++++++++++++++++- 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index 08cd8d98be..7947e81244 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4045,7 +4045,8 @@ olx.source.TileImageOptions.prototype.wrapX; * tileGrid: ol.tilegrid.TileGrid, * tileUrlFunction: (ol.TileUrlFunctionType|undefined), * url: (string|undefined), - * urls: (Array.|undefined)}} + * urls: (Array.|undefined), + * wrapX: (boolean|undefined)}} * @api */ olx.source.TileVectorOptions; @@ -4108,6 +4109,16 @@ olx.source.TileVectorOptions.prototype.url; olx.source.TileVectorOptions.prototype.urls; +/** + * Wrap the world horizontally. Default is `true`. For vector editing across the + * -180° and 180° meridians to work properly, this should be set to `false`. The + * resulting geometry coordinates will then exceed the world bounds. + * @type {boolean|undefined} + * @api + */ +olx.source.TileVectorOptions.prototype.wrapX; + + /** * @typedef {{url: (string|undefined), * displayDpi: (number|undefined), diff --git a/src/ol/source/tilevectorsource.js b/src/ol/source/tilevectorsource.js index bfb56a798d..870eb6cfae 100644 --- a/src/ol/source/tilevectorsource.js +++ b/src/ol/source/tilevectorsource.js @@ -7,6 +7,7 @@ goog.require('ol.TileUrlFunction'); goog.require('ol.featureloader'); goog.require('ol.source.State'); goog.require('ol.source.Vector'); +goog.require('ol.tilecoord'); goog.require('ol.tilegrid.TileGrid'); @@ -27,7 +28,8 @@ ol.source.TileVector = function(options) { attributions: options.attributions, logo: options.logo, projection: undefined, - state: ol.source.State.READY + state: ol.source.State.READY, + wrapX: options.wrapX }); /** @@ -230,6 +232,28 @@ ol.source.TileVector.prototype.getFeaturesAtCoordinateAndResolution = ol.source.TileVector.prototype.getFeaturesInExtent = goog.abstractMethod; +/** + * Handles x-axis wrapping and returns a tile coordinate transformed from the + * internal tile scheme to the tile grid's tile scheme. When the tile coordinate + * is outside the resolution and extent range of the tile grid, `null` will be + * returned. + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.proj.Projection} projection Projection. + * @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.TileVector.prototype.getTileCoordForTileUrlFunction = + function(tileCoord, projection) { + var tileGrid = this.tileGrid_; + goog.asserts.assert(!goog.isNull(tileGrid), 'tile grid needed'); + if (this.getWrapX()) { + tileCoord = ol.tilecoord.wrapX(tileCoord, tileGrid, projection); + } + return ol.tilecoord.withinExtentAndZ(tileCoord, tileGrid) ? + tileGrid.transformTileCoord(tileCoord) : null; +}; + + /** * @param {number} z Z. * @param {number} x X. @@ -267,11 +291,12 @@ ol.source.TileVector.prototype.loadFeatures = for (y = tileRange.minY; y <= tileRange.maxY; ++y) { var tileKey = this.getTileKeyZXY_(z, x, y); if (!(tileKey in tiles)) { - tileCoord[0] = z; tileCoord[1] = x; tileCoord[2] = y; - tileGrid.transformTileCoord(tileCoord, tileCoord); - var url = tileUrlFunction(tileCoord, 1, projection); + var urlTileCoord = this.getTileCoordForTileUrlFunction( + tileCoord, projection); + var url = goog.isNull(urlTileCoord) ? undefined : + tileUrlFunction(urlTileCoord, 1, projection); if (goog.isDef(url)) { tiles[tileKey] = []; var loader = ol.featureloader.loadFeaturesXhr(url, this.format_, diff --git a/test/spec/ol/source/tilevectorsource.test.js b/test/spec/ol/source/tilevectorsource.test.js index 32c83c346d..87a34db4de 100644 --- a/test/spec/ol/source/tilevectorsource.test.js +++ b/test/spec/ol/source/tilevectorsource.test.js @@ -9,7 +9,6 @@ describe('ol.source.TileVector', function() { var tileCoords = []; var source = new ol.source.TileVector({ format: new ol.format.TopoJSON(), - projection: 'EPSG:3857', tileGrid: ol.tilegrid.createXYZ({ maxZoom: 19 }), @@ -18,8 +17,9 @@ describe('ol.source.TileVector', function() { return null; } }); + var projection = ol.proj.get('EPSG:3857'); source.loadFeatures( - [-8238854, 4969777, -8237854, 4970777], 4.8, source.getProjection()); + [-8238854, 4969777, -8237854, 4970777], 4.8, projection); expect(tileCoords[0]).to.eql([15, 9647, 12320]); expect(tileCoords[1]).to.eql([15, 9647, 12319]); expect(tileCoords[2]).to.eql([15, 9648, 12320]); @@ -28,8 +28,58 @@ describe('ol.source.TileVector', function() { }); + describe('#getTileCoordForTileUrlFunction()', function() { + + it('returns the expected tile coordinate - {wrapX: true}', function() { + var tileSource = new ol.source.TileVector({ + format: new ol.format.TopoJSON(), + tileGrid: ol.tilegrid.createXYZ({ + maxZoom: 19 + }), + wrapX: true + }); + var projection = ol.proj.get('EPSG:3857'); + + var tileCoord = tileSource.getTileCoordForTileUrlFunction( + [6, -31, 41], projection); + expect(tileCoord).to.eql([6, 33, 22]); + + tileCoord = tileSource.getTileCoordForTileUrlFunction( + [6, 33, 41], projection); + expect(tileCoord).to.eql([6, 33, 22]); + + tileCoord = tileSource.getTileCoordForTileUrlFunction( + [6, 97, 41], projection); + expect(tileCoord).to.eql([6, 33, 22]); + }); + + it('returns the expected tile coordinate - {wrapX: false}', function() { + var tileSource = new ol.source.TileVector({ + format: new ol.format.TopoJSON(), + tileGrid: ol.tilegrid.createXYZ({ + maxZoom: 19 + }), + wrapX: false + }); + var projection = ol.proj.get('EPSG:3857'); + + var tileCoord = tileSource.getTileCoordForTileUrlFunction( + [6, -31, 41], projection); + expect(tileCoord).to.eql(null); + + tileCoord = tileSource.getTileCoordForTileUrlFunction( + [6, 33, 41], projection); + expect(tileCoord).to.eql([6, 33, 22]); + + tileCoord = tileSource.getTileCoordForTileUrlFunction( + [6, 97, 41], projection); + expect(tileCoord).to.eql(null); + }); + }); + }); goog.require('ol.format.TopoJSON'); +goog.require('ol.proj'); goog.require('ol.source.TileVector');