From 483376deb24da4065f42ee0edd79571f0877fd0e Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Wed, 27 Jan 2016 00:42:51 +0100 Subject: [PATCH] Untangle vector tile feature reprojection --- src/ol/featureloader.js | 10 +-- src/ol/format/mvtformat.js | 2 +- .../canvas/canvasvectortilelayerrenderer.js | 24 +++++-- src/ol/source/vectortilesource.js | 6 +- src/ol/vectortile.js | 8 +-- .../canvasvectortilelayerrenderer.test.js | 65 ++++++++++++++----- 6 files changed, 76 insertions(+), 39 deletions(-) diff --git a/src/ol/featureloader.js b/src/ol/featureloader.js index b47b3eff5d..247bc5c5b7 100644 --- a/src/ol/featureloader.js +++ b/src/ol/featureloader.js @@ -12,7 +12,6 @@ goog.require('ol.VectorTile'); goog.require('ol.format.FormatType'); goog.require('ol.proj'); goog.require('ol.proj.Projection'); -goog.require('ol.proj.Units'); goog.require('ol.xml'); @@ -142,14 +141,7 @@ ol.featureloader.tile = function(url, format) { * @this {ol.VectorTile} */ function(features, dataProjection) { - var dataUnits = dataProjection.getUnits(); - if (dataUnits === ol.proj.Units.TILE_PIXELS) { - var projection = new ol.proj.Projection({ - code: this.getProjection().getCode(), - units: dataUnits - }); - this.setProjection(projection); - } + this.setProjection(dataProjection); this.setFeatures(features); }, /** diff --git a/src/ol/format/mvtformat.js b/src/ol/format/mvtformat.js index ccf08b7295..e92bdacee6 100644 --- a/src/ol/format/mvtformat.js +++ b/src/ol/format/mvtformat.js @@ -41,7 +41,7 @@ ol.format.MVT = function(opt_options) { * @type {ol.proj.Projection} */ this.defaultDataProjection = new ol.proj.Projection({ - code: 'EPSG:3857', + code: '', units: ol.proj.Units.TILE_PIXELS }); diff --git a/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js b/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js index 66ab25d9f4..f2c4ea6700 100644 --- a/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js @@ -13,6 +13,7 @@ goog.require('ol.dom'); goog.require('ol.extent'); goog.require('ol.geom.flat.transform'); goog.require('ol.layer.VectorTile'); +goog.require('ol.proj'); goog.require('ol.proj.Units'); goog.require('ol.render.EventType'); goog.require('ol.render.canvas.ReplayGroup'); @@ -205,9 +206,10 @@ ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(frameState, * @param {ol.VectorTile} tile Tile. * @param {ol.layer.VectorTile} layer Vector tile layer. * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. */ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, - layer, pixelRatio) { + layer, pixelRatio, projection) { var revision = layer.getRevision(); var renderOrder = layer.getRenderOrder() || null; @@ -227,14 +229,19 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, 'Source is an ol.source.VectorTile'); var tileGrid = source.getTileGrid(); var tileCoord = tile.getTileCoord(); - var pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS; - var extent; + var tileProjection = tile.getProjection(); + var pixelSpace = tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS; + var extent, reproject; if (pixelSpace) { var tilePixelSize = source.getTilePixelSize(tileCoord[0], pixelRatio, tile.getProjection()); extent = [0, 0, tilePixelSize[0], tilePixelSize[1]]; } else { extent = tileGrid.getTileCoordExtent(tileCoord); + if (!ol.proj.equivalent(projection, tileProjection)) { + reproject = true; + tile.setProjection(projection); + } } var resolution = tileGrid.getResolution(tileCoord[0]); var tileResolution = @@ -276,7 +283,14 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, if (renderOrder && renderOrder !== replayState.renderedRenderOrder) { features.sort(renderOrder); } - features.forEach(renderFeature, this); + var feature; + for (var i = 0, ii = features.length; i < ii; ++i) { + feature = features[i]; + if (reproject) { + feature.getGeometry().transform(tileProjection, projection); + } + renderFeature.call(this, feature); + } replayGroup.finish(); @@ -464,7 +478,7 @@ ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, tile = tilesToDraw[tileCoordKey]; if (tile.getState() == ol.TileState.LOADED) { replayables.push(tile); - this.createReplayGroup(tile, layer, pixelRatio); + this.createReplayGroup(tile, layer, pixelRatio, projection); } } } diff --git a/src/ol/source/vectortilesource.js b/src/ol/source/vectortilesource.js index 48224f30cd..3fe805b817 100644 --- a/src/ol/source/vectortilesource.js +++ b/src/ol/source/vectortilesource.js @@ -1,6 +1,5 @@ goog.provide('ol.source.VectorTile'); -goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('ol.TileState'); @@ -54,7 +53,7 @@ ol.source.VectorTile = function(options) { /** * @protected * @type {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string, - * ol.format.Feature, ol.TileLoadFunctionType, ol.proj.Projection)} + * ol.format.Feature, ol.TileLoadFunctionType)} */ this.tileClass = options.tileClass ? options.tileClass : ol.VectorTile; @@ -70,7 +69,6 @@ ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projectio if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); } else { - goog.asserts.assert(projection, 'argument projection is truthy'); var tileCoord = [z, x, y]; var urlTileCoord = this.getTileCoordForTileUrlFunction( tileCoord, projection); @@ -80,7 +78,7 @@ ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projectio tileCoord, tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, tileUrl !== undefined ? tileUrl : '', - this.format_, this.tileLoadFunction, projection); + this.format_, this.tileLoadFunction); goog.events.listen(tile, goog.events.EventType.CHANGE, this.handleTileChange, false, this); diff --git a/src/ol/vectortile.js b/src/ol/vectortile.js index ac1e28db34..ac2b2f2052 100644 --- a/src/ol/vectortile.js +++ b/src/ol/vectortile.js @@ -26,9 +26,8 @@ ol.TileReplayState; * @param {string} src Data source url. * @param {ol.format.Feature} format Feature format. * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. - * @param {ol.proj.Projection} projection Feature projection. */ -ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction, projection) { +ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) { goog.base(this, tileCoord, state); @@ -57,10 +56,11 @@ ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction, projec this.loader_; /** + * Data projection * @private * @type {ol.proj.Projection} */ - this.projection_ = projection; + this.projection_; /** * @private @@ -154,7 +154,7 @@ ol.VectorTile.prototype.load = function() { if (this.state == ol.TileState.IDLE) { this.setState(ol.TileState.LOADING); this.tileLoadFunction_(this, this.url_); - this.loader_(null, NaN, this.projection_); + this.loader_(null, NaN, null); } }; diff --git a/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js b/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js index 7fb60ef0c8..e29d0ac0d2 100644 --- a/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js +++ b/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js @@ -4,20 +4,15 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { describe('constructor', function() { - it('creates a new instance', function() { - var layer = new ol.layer.VectorTile({ - source: new ol.source.VectorTile({}) - }); - var renderer = new ol.renderer.canvas.VectorTileLayer(layer); - expect(renderer).to.be.a(ol.renderer.canvas.VectorTileLayer); - }); + var map, layer, feature1, feature2, target, tileCallback; - it('gives precedence to feature styles over layer styles', function() { - var target = document.createElement('div'); + beforeEach(function() { + tileCallback = function() {}; + target = document.createElement('div'); target.style.width = '256px'; target.style.height = '256px'; document.body.appendChild(target); - var map = new ol.Map({ + map = new ol.Map({ view: new ol.View({ center: [0, 0], zoom: 0 @@ -34,13 +29,15 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { text: 'feature' }) })]; - var feature1 = new ol.Feature(new ol.geom.Point([0, 0])); - var feature2 = new ol.Feature(new ol.geom.Point([0, 0])); + feature1 = new ol.Feature(new ol.geom.Point([1, -1])); + feature2 = new ol.Feature(new ol.geom.Point([0, 0])); feature2.setStyle(featureStyle); var TileClass = function() { ol.VectorTile.apply(this, arguments); this.setState('loaded'); this.setFeatures([feature1, feature2]); + this.setProjection(ol.proj.get('EPSG:4326')); + tileCallback(this); }; ol.inherits(TileClass, ol.VectorTile); var source = new ol.source.VectorTile({ @@ -48,17 +45,52 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { tileClass: TileClass, tileGrid: ol.tilegrid.createXYZ() }); - var layer = new ol.layer.VectorTile({ + layer = new ol.layer.VectorTile({ source: source, style: layerStyle }); map.addLayer(layer); + }); + + it('creates a new instance', function() { + var renderer = new ol.renderer.canvas.VectorTileLayer(layer); + expect(renderer).to.be.a(ol.renderer.canvas.VectorTileLayer); + }); + + afterEach(function() { + document.body.removeChild(target); + map.dispose(); + }); + + it('gives precedence to feature styles over layer styles', function() { var spy = sinon.spy(map.getRenderer().getLayerRenderer(layer), 'renderFeature'); map.renderSync(); - expect(spy.getCall(0).args[2]).to.be(layerStyle); - expect(spy.getCall(1).args[2]).to.be(featureStyle); - document.body.removeChild(target); + expect(spy.getCall(0).args[2]).to.be(layer.getStyle()); + expect(spy.getCall(1).args[2]).to.be(feature2.getStyle()); + }); + + it('transforms geometries when tile and view projection are different', function() { + var tile; + tileCallback = function(t) { + tile = t; + } + map.renderSync(); + expect(tile.getProjection()).to.equal(ol.proj.get('EPSG:3857')); + expect(feature1.getGeometry().getCoordinates()).to.eql( + ol.proj.fromLonLat([1, -1])); + }); + + it('leaves geometries untouched when units are tile-pixels', function() { + var proj = new ol.proj.Projection({code: '', units: 'tile-pixels'}); + var tile; + tileCallback = function(t) { + t.setProjection(proj); + tile = t; + } + map.renderSync(); + expect(tile.getProjection()).to.equal(proj); + expect(feature1.getGeometry().getCoordinates()).to.eql([1, -1]); }); }); @@ -121,6 +153,7 @@ goog.require('ol.format.MVT'); goog.require('ol.geom.Point'); goog.require('ol.layer.VectorTile'); goog.require('ol.proj'); +goog.require('ol.proj.Projection'); goog.require('ol.renderer.canvas.VectorTileLayer'); goog.require('ol.source.VectorTile'); goog.require('ol.style.Style');