diff --git a/examples/draw-features.js b/examples/draw-features.js index 1a25d6874e..dcab9c70db 100644 --- a/examples/draw-features.js +++ b/examples/draw-features.js @@ -14,7 +14,7 @@ var raster = new ol.layer.Tile({ source: new ol.source.MapQuest({layer: 'sat'}) }); -var source = new ol.source.Vector(); +var source = new ol.source.Vector({wrapX: false}); var vector = new ol.layer.Vector({ source: source, diff --git a/examples/modify-features.js b/examples/modify-features.js index 90709d6a8b..25c6ee18dc 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -19,7 +19,8 @@ var raster = new ol.layer.Tile({ var vector = new ol.layer.Vector({ source: new ol.source.Vector({ url: 'data/geojson/countries.geojson', - format: new ol.format.GeoJSON() + format: new ol.format.GeoJSON(), + wrapX: false }) }); diff --git a/externs/olx.js b/externs/olx.js index 5c7f87024e..25c3589c3c 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4946,7 +4946,8 @@ olx.source.TileWMSOptions.prototype.wrapX; * loader: (ol.FeatureLoader|undefined), * logo: (string|olx.LogoOptions|undefined), * strategy: (ol.LoadingStrategy|undefined), - * url: (string|undefined)}} + * url: (string|undefined), + * wrapX: (boolean|undefined)}} * @api */ olx.source.VectorOptions; @@ -5015,6 +5016,16 @@ olx.source.VectorOptions.prototype.strategy; olx.source.VectorOptions.prototype.url; +/** + * 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.VectorOptions.prototype.wrapX; + + /** * @typedef {{attributions: (Array.|undefined), * crossOrigin: (string|null|undefined), diff --git a/src/ol/proj/proj.js b/src/ol/proj/proj.js index 6ec9c030f8..ddcc57484f 100644 --- a/src/ol/proj/proj.js +++ b/src/ol/proj/proj.js @@ -121,6 +121,13 @@ ol.proj.Projection = function(options) { */ this.global_ = goog.isDef(options.global) ? options.global : false; + + /** + * @private + * @type {boolean} + */ + this.canWrapX_ = this.global_ && !goog.isNull(this.extent_); + /** * @private * @type {function(number, ol.Coordinate):number} @@ -172,6 +179,14 @@ ol.proj.Projection = function(options) { }; +/** + * @return {boolean} The projection is suitable for wrapping the x-axis + */ +ol.proj.Projection.prototype.canWrapX = function() { + return this.canWrapX_; +}; + + /** * Get the code for this projection, e.g. 'EPSG:4326'. * @return {string} Code. @@ -255,6 +270,7 @@ ol.proj.Projection.prototype.isGlobal = function() { */ ol.proj.Projection.prototype.setGlobal = function(global) { this.global_ = global; + this.canWrapX_ = global && !goog.isNull(this.extent_); }; @@ -281,6 +297,7 @@ ol.proj.Projection.prototype.setDefaultTileGrid = function(tileGrid) { */ ol.proj.Projection.prototype.setExtent = function(extent) { this.extent_ = extent; + this.canWrapX_ = this.global_ && !goog.isNull(extent); }; diff --git a/src/ol/renderer/canvas/canvaslayerrenderer.js b/src/ol/renderer/canvas/canvaslayerrenderer.js index 137485307f..ac8b9a112a 100644 --- a/src/ol/renderer/canvas/canvaslayerrenderer.js +++ b/src/ol/renderer/canvas/canvaslayerrenderer.js @@ -127,7 +127,7 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = var layer = this.getLayer(); if (layer.hasListener(type)) { var transform = goog.isDef(opt_transform) ? - opt_transform : this.getTransform(frameState); + opt_transform : this.getTransform(frameState, 0); var render = new ol.render.canvas.Immediate( context, frameState.pixelRatio, frameState.extent, transform, frameState.viewState.rotation); @@ -192,10 +192,12 @@ ol.renderer.canvas.Layer.prototype.getImageTransform = goog.abstractMethod; /** * @param {olx.FrameState} frameState Frame state. + * @param {number} offsetX Offset on the x-axis in view coordinates. * @protected * @return {!goog.vec.Mat4.Number} Transform. */ -ol.renderer.canvas.Layer.prototype.getTransform = function(frameState) { +ol.renderer.canvas.Layer.prototype.getTransform = + function(frameState, offsetX) { var viewState = frameState.viewState; var pixelRatio = frameState.pixelRatio; return ol.vec.Mat4.makeTransform2D(this.transform_, @@ -204,7 +206,8 @@ ol.renderer.canvas.Layer.prototype.getTransform = function(frameState) { pixelRatio / viewState.resolution, -pixelRatio / viewState.resolution, -viewState.rotation, - -viewState.center[0], -viewState.center[1]); + -viewState.center[0] + offsetX, + -viewState.center[1]); }; diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 86b1c06512..6a1c38e691 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -10,6 +10,7 @@ goog.require('ol'); goog.require('ol.RendererType'); goog.require('ol.css'); goog.require('ol.dom'); +goog.require('ol.extent'); goog.require('ol.layer.Image'); goog.require('ol.layer.Layer'); goog.require('ol.layer.Tile'); @@ -102,29 +103,41 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = var extent = frameState.extent; var pixelRatio = frameState.pixelRatio; var viewState = frameState.viewState; + var projection = viewState.projection; var resolution = viewState.resolution; var rotation = viewState.rotation; - ol.vec.Mat4.makeTransform2D(this.transform_, - this.canvas_.width / 2, this.canvas_.height / 2, - pixelRatio / resolution, -pixelRatio / resolution, - -rotation, - -viewState.center[0], -viewState.center[1]); + var offsetX = 0; + if (projection.canWrapX()) { + var projectionExtent = projection.getExtent(); + var worldWidth = ol.extent.getWidth(projectionExtent); + var x = frameState.focus[0]; + if (x < projectionExtent[0] || x > projectionExtent[2]) { + var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); + offsetX = worldWidth * worldsAway; + extent = [ + extent[0] + offsetX, extent[1], + extent[2] + offsetX, extent[3] + ]; + } + } + + var transform = this.getTransform(frameState, offsetX); var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio); - var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent, - resolution); + var replayGroup = new ol.render.canvas.ReplayGroup( + tolerance, extent, resolution); var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio, - extent, this.transform_, rotation); + extent, transform, rotation); var composeEvent = new ol.render.Event(type, map, vectorContext, replayGroup, frameState, context, null); map.dispatchEvent(composeEvent); replayGroup.finish(); if (!replayGroup.isEmpty()) { - replayGroup.replay(context, pixelRatio, this.transform_, - rotation, {}); + replayGroup.replay(context, pixelRatio, transform, rotation, {}); + } vectorContext.flush(); this.replayGroup = replayGroup; @@ -133,14 +146,21 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = /** - * @param {ol.layer.Layer} layer Layer. - * @return {ol.renderer.canvas.Layer} Canvas layer renderer. + * @param {olx.FrameState} frameState Frame state. + * @param {number} offsetX Offset on the x-axis in view coordinates. + * @protected + * @return {!goog.vec.Mat4.Number} Transform. */ -ol.renderer.canvas.Map.prototype.getCanvasLayerRenderer = function(layer) { - var layerRenderer = this.getLayerRenderer(layer); - goog.asserts.assertInstanceof(layerRenderer, ol.renderer.canvas.Layer, - 'layerRenderer is an instance of ol.renderer.canvas.Layer'); - return /** @type {ol.renderer.canvas.Layer} */ (layerRenderer); +ol.renderer.canvas.Map.prototype.getTransform = function(frameState, offsetX) { + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var resolution = viewState.resolution; + return ol.vec.Mat4.makeTransform2D(this.transform_, + this.canvas_.width / 2, this.canvas_.height / 2, + pixelRatio / resolution, -pixelRatio / resolution, + -viewState.rotation, + -viewState.center[0] - offsetX, + -viewState.center[1]); }; @@ -197,7 +217,8 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { } } - this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState); + this.dispatchComposeEvent_( + ol.render.EventType.POSTCOMPOSE, frameState); if (!this.renderedVisible_) { goog.style.setElementShown(this.canvas_, true); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 6b913462da..45a83ea653 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -11,6 +11,7 @@ goog.require('ol.render.EventType'); goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.vector'); +goog.require('ol.source.Vector'); @@ -75,7 +76,18 @@ goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) { - var transform = this.getTransform(frameState); + var extent = frameState.extent; + var focus = frameState.focus; + var pixelRatio = frameState.pixelRatio; + var skippedFeatureUids = frameState.skippedFeatureUids; + var viewState = frameState.viewState; + var projection = viewState.projection; + var rotation = viewState.rotation; + var projectionExtent = projection.getExtent(); + var vectorSource = this.getLayer().getSource(); + goog.asserts.assertInstanceof(vectorSource, ol.source.Vector); + + var transform = this.getTransform(frameState, 0); this.dispatchPreComposeEvent(context, frameState, transform); @@ -96,9 +108,48 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = // see http://jsperf.com/context-save-restore-versus-variable var alpha = replayContext.globalAlpha; replayContext.globalAlpha = layerState.opacity; - replayGroup.replay( - replayContext, frameState.pixelRatio, transform, - frameState.viewState.rotation, frameState.skippedFeatureUids); + var noSkip = {}; + var focusX = focus[0]; + + if (vectorSource.getWrapX() && projection.canWrapX() && + !ol.extent.containsExtent(projectionExtent, extent)) { + var projLeft = projectionExtent[0]; + var projRight = projectionExtent[2]; + // A feature from skippedFeatureUids will only be skipped in the world + // that has the frameState's focus, because this is where a feature + // overlay for highlighting or selection would render the skipped + // feature. + replayGroup.replay(replayContext, pixelRatio, transform, rotation, + projLeft <= focusX && focusX <= projRight ? + skippedFeatureUids : noSkip); + var startX = extent[0]; + var worldWidth = ol.extent.getWidth(projectionExtent); + var world = 0; + var offsetX; + while (startX < projectionExtent[0]) { + --world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(replayContext, pixelRatio, transform, rotation, + projLeft + offsetX <= focusX && focusX <= projRight + offsetX ? + skippedFeatureUids : noSkip); + startX += worldWidth; + } + world = 0; + startX = extent[2]; + while (startX > projectionExtent[2]) { + ++world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(replayContext, pixelRatio, transform, rotation, + projLeft + offsetX <= focusX && focusX <= projRight + offsetX ? + skippedFeatureUids : noSkip); + startX -= worldWidth; + } + } else { + replayGroup.replay( + replayContext, pixelRatio, transform, rotation, skippedFeatureUids); + } if (replayContext != context) { this.dispatchRenderEvent(replayContext, frameState, transform); @@ -194,6 +245,14 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = var extent = ol.extent.buffer(frameStateExtent, vectorLayerRenderBuffer * resolution); + var projectionExtent = viewState.projection.getExtent(); + + if (vectorSource.getWrapX() && viewState.projection.canWrapX() && + !ol.extent.containsExtent(projectionExtent, frameState.extent)) { + // do not clip when the view crosses the -180° or 180° meridians + extent[0] = projectionExtent[0]; + extent[2] = projectionExtent[2]; + } if (!this.dirty_ && this.renderedResolution_ == resolution && diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 64e32edc75..666ae642b0 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -9,6 +9,7 @@ goog.require('goog.events.EventType'); goog.require('goog.functions'); goog.require('goog.object'); goog.require('goog.vec.Mat4'); +goog.require('ol.extent'); goog.require('ol.layer.Layer'); goog.require('ol.renderer.Layer'); goog.require('ol.style.IconImageCache'); @@ -40,6 +41,7 @@ ol.renderer.Map = function(container, map) { goog.base(this); + /** * @private * @type {ol.Map} @@ -136,23 +138,39 @@ ol.renderer.Map.prototype.forEachFeatureAtCoordinate = var viewState = frameState.viewState; var viewResolution = viewState.resolution; var viewRotation = viewState.rotation; + + /** @type {Object.} */ + var features = {}; + + /** + * @param {ol.Feature} feature Feature. + * @return {?} Callback result. + */ + function forEachFeatureAtCoordinate(feature) { + goog.asserts.assert(goog.isDef(feature), 'received a feature'); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, null); + } + } + + var projection = viewState.projection; + + var translatedCoordinate = coordinate; + if (projection.canWrapX()) { + var projectionExtent = projection.getExtent(); + var worldWidth = ol.extent.getWidth(projectionExtent); + var x = coordinate[0]; + if (x < projectionExtent[0] || x > projectionExtent[2]) { + var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); + translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]]; + } + } + if (!goog.isNull(this.replayGroup)) { - /** @type {Object.} */ - var features = {}; - result = this.replayGroup.forEachFeatureAtCoordinate(coordinate, - viewResolution, viewRotation, {}, - /** - * @param {ol.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - goog.asserts.assert(goog.isDef(feature), 'received a feature'); - var key = goog.getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, null); - } - }); + result = this.replayGroup.forEachFeatureAtCoordinate(translatedCoordinate, + viewResolution, viewRotation, {}, forEachFeatureAtCoordinate); if (result) { return result; } @@ -167,7 +185,8 @@ ol.renderer.Map.prototype.forEachFeatureAtCoordinate = layerFilter.call(thisArg2, layer)) { var layerRenderer = this.getLayerRenderer(layer); result = layerRenderer.forEachFeatureAtCoordinate( - coordinate, frameState, callback, thisArg); + layer.getSource().getWrapX() ? translatedCoordinate : coordinate, + frameState, callback, thisArg); if (result) { return result; } diff --git a/src/ol/source/source.js b/src/ol/source/source.js index 75a02f5cd3..e215cb40e9 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -24,7 +24,8 @@ ol.source.State = { * @typedef {{attributions: (Array.|undefined), * logo: (string|olx.LogoOptions|undefined), * projection: ol.proj.ProjectionLike, - * state: (ol.source.State|undefined)}} + * state: (ol.source.State|undefined), + * wrapX: (boolean|undefined)}} */ ol.source.SourceOptions; @@ -72,6 +73,12 @@ ol.source.Source = function(options) { this.state_ = goog.isDef(options.state) ? options.state : ol.source.State.READY; + /** + * @private + * @type {boolean|undefined} + */ + this.wrapX_ = options.wrapX; + }; goog.inherits(ol.source.Source, ol.Object); @@ -135,6 +142,14 @@ ol.source.Source.prototype.getState = function() { }; +/** + * @return {boolean|undefined} Wrap X. + */ +ol.source.Source.prototype.getWrapX = function() { + return this.wrapX_; +}; + + /** * Set the attributions of the source. * @param {Array.} attributions Attributions. diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index abb7ba7730..e81a4261b3 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -47,7 +47,8 @@ ol.source.Tile = function(options) { extent: options.extent, logo: options.logo, projection: options.projection, - state: options.state + state: options.state, + wrapX: options.wrapX }); /** @@ -81,12 +82,6 @@ ol.source.Tile = function(options) { */ this.tmpSize = [0, 0]; - /** - * @private - * @type {boolean|undefined} - */ - this.wrapX_ = options.wrapX; - }; goog.inherits(ol.source.Tile, ol.source.Source); @@ -221,10 +216,10 @@ ol.source.Tile.prototype.getTilePixelSize = /** - * Handles x-axis wrapping. When `this.wrapX_` is undefined or the projection - * is not a global projection, `tileCoord` will be returned unaltered. When - * `this.wrapX_` is true, the tile coordinate will be wrapped horizontally. - * When `this.wrapX_` is `false`, `null` will be returned for tiles that are + * 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. * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.proj.Projection=} opt_projection Projection. @@ -235,8 +230,9 @@ ol.source.Tile.prototype.getWrapXTileCoord = var projection = goog.isDef(opt_projection) ? opt_projection : this.getProjection(); var tileGrid = this.getTileGridForProjection(projection); - if (goog.isDef(this.wrapX_) && tileGrid.isGlobal(tileCoord[0], projection)) { - return this.wrapX_ ? + 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 { diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 53b3319f64..42f945f9d7 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -79,7 +79,8 @@ ol.source.Vector = function(opt_options) { attributions: options.attributions, logo: options.logo, projection: undefined, - state: ol.source.State.READY + state: ol.source.State.READY, + wrapX: goog.isDef(options.wrapX) ? options.wrapX : true }); /** diff --git a/test/spec/ol/proj/proj.test.js b/test/spec/ol/proj/proj.test.js index 1714236b0d..de7ba4935a 100644 --- a/test/spec/ol/proj/proj.test.js +++ b/test/spec/ol/proj/proj.test.js @@ -138,6 +138,46 @@ describe('ol.proj', function() { }); }); + describe('canWrapX()', function() { + + it('requires an extent for allowing wrapX', function() { + var proj = new ol.proj.Projection({ + code: 'foo', + global: true + }); + expect(proj.canWrapX()).to.be(false); + proj.setExtent([1, 2, 3, 4]); + expect(proj.canWrapX()).to.be(true); + proj = new ol.proj.Projection({ + code: 'foo', + global: true, + extent: [1, 2, 3, 4] + }); + expect(proj.canWrapX()).to.be(true); + proj.setExtent(null); + expect(proj.canWrapX()).to.be(false); + }); + + it('requires global to be true for allowing wrapX', function() { + var proj = new ol.proj.Projection({ + code: 'foo', + extent: [1, 2, 3, 4] + }); + expect(proj.canWrapX()).to.be(false); + proj.setGlobal(true); + expect(proj.canWrapX()).to.be(true); + proj = new ol.proj.Projection({ + code: 'foo', + global: true, + extent: [1, 2, 3, 4] + }); + expect(proj.canWrapX()).to.be(true); + proj.setGlobal(false); + expect(proj.canWrapX()).to.be(false); + }); + + }); + describe('transformExtent()', function() { it('transforms an extent given projection identifiers', function() { diff --git a/test/spec/ol/renderer/canvas/canvasmaprenderer.test.js b/test/spec/ol/renderer/canvas/canvasmaprenderer.test.js new file mode 100644 index 0000000000..f5209f1ce6 --- /dev/null +++ b/test/spec/ol/renderer/canvas/canvasmaprenderer.test.js @@ -0,0 +1,90 @@ +goog.provide('ol.test.renderer.canvas.Map'); + +describe('ol.renderer.canvas.Map', function() { + + describe('constructor', function() { + + it('creates a new instance', function() { + var map = new ol.Map({ + target: document.createElement('div') + }); + var renderer = new ol.renderer.canvas.Map(map.viewport_, map); + expect(renderer).to.be.a(ol.renderer.canvas.Map); + }); + + }); + + describe('#renderFrame()', function() { + var layer, map, renderer; + + beforeEach(function() { + map = new ol.Map({}); + map.on('postcompose', function() {}); + layer = new ol.layer.Vector({ + source: new ol.source.Vector({wrapX: true}) + }); + renderer = map.getRenderer(); + renderer.layerRenderers_ = {}; + var layerRenderer = new ol.renderer.canvas.Layer(layer); + layerRenderer.prepareFrame = function() { return true; }; + layerRenderer.getImage = function() { return null; }; + renderer.layerRenderers_[goog.getUid(layer)] = layerRenderer; + }); + + it('uses correct extent and offset on wrapped worlds', function() { + var spy = sinon.spy(renderer, 'getTransform'); + var proj = new ol.proj.Projection({ + code: 'foo', + extent: [-180, -90, 180, 90], + global: true + }); + var frameState = { + coordinateToPixelMatrix: map.coordinateToPixelMatrix_, + pixelToCoordinateMatrix: map.pixelToCoordinateMatrix_, + pixelRatio: 1, + size: [100, 100], + skippedFeatureUids: {}, + extent: proj.getExtent(), + viewState: { + center: [0, 0], + projection: proj, + resolution: 1, + rotation: 0 + }, + layerStates: {}, + layerStatesArray: [{ + layer: layer, + sourceState: 'ready', + visible: true, + minResolution: 1, + maxResolution: 2 + }], + postRenderFunctions: [] + }; + frameState.focus = [0, 0]; + // focus is on real world + renderer.renderFrame(frameState); + expect(spy.getCall(0).args[1]).to.be(0); + expect(renderer.replayGroup.maxExtent_).to.eql([-180, -90, 180, 90]); + frameState.focus = [-200, 0]; + // focus is one world left of the real world + renderer.renderFrame(frameState); + expect(spy.getCall(1).args[1]).to.be(360); + expect(renderer.replayGroup.maxExtent_).to.eql([180, -90, 540, 90]); + frameState.focus = [200, 0]; + // focus is one world right of the real world + renderer.renderFrame(frameState); + expect(spy.getCall(2).args[1]).to.be(-360); + expect(renderer.replayGroup.maxExtent_).to.eql([-540, -90, -180, 90]); + }); + }); + +}); + + +goog.require('ol.layer.Vector'); +goog.require('ol.Map'); +goog.require('ol.proj.Projection'); +goog.require('ol.renderer.canvas.Layer'); +goog.require('ol.renderer.canvas.Map'); +goog.require('ol.source.Vector'); diff --git a/test/spec/ol/renderer/canvas/canvasvectorlayerrenderer.test.js b/test/spec/ol/renderer/canvas/canvasvectorlayerrenderer.test.js index 2457dae729..82636f5730 100644 --- a/test/spec/ol/renderer/canvas/canvasvectorlayerrenderer.test.js +++ b/test/spec/ol/renderer/canvas/canvasvectorlayerrenderer.test.js @@ -5,14 +5,10 @@ describe('ol.renderer.canvas.VectorLayer', function() { describe('constructor', function() { it('creates a new instance', function() { - var map = new ol.Map({ - target: document.createElement('div') - }); var layer = new ol.layer.Vector({ source: new ol.source.Vector() }); - var renderer = new ol.renderer.canvas.VectorLayer(map.getRenderer(), - layer); + var renderer = new ol.renderer.canvas.VectorLayer(layer); expect(renderer).to.be.a(ol.renderer.canvas.VectorLayer); }); @@ -59,27 +55,24 @@ describe('ol.renderer.canvas.VectorLayer', function() { }); describe('#forEachFeatureAtCoordinate', function() { - var renderer; + var layer, renderer; beforeEach(function() { - var map = new ol.Map({}); - var layer = new ol.layer.Vector({ + layer = new ol.layer.Vector({ source: new ol.source.Vector() }); - renderer = new ol.renderer.canvas.VectorLayer( - map.getRenderer(), layer); + renderer = new ol.renderer.canvas.VectorLayer(layer); var replayGroup = {}; renderer.replayGroup_ = replayGroup; replayGroup.forEachFeatureAtCoordinate = function(coordinate, resolution, rotation, skippedFeaturesUids, callback) { - var geometry = new ol.geom.Point([0, 0]); var feature = new ol.Feature(); - callback(geometry, feature); - callback(geometry, feature); + callback(feature); + callback(feature); }; }); - it('calls callback once per feature', function() { + it('calls callback once per feature with a layer as 2nd arg', function() { var spy = sinon.spy(); var coordinate = [0, 0]; var frameState = { @@ -92,6 +85,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { renderer.forEachFeatureAtCoordinate( coordinate, frameState, spy, undefined); expect(spy.callCount).to.be(1); + expect(spy.getCall(0).args[1]).to.equal(layer); }); });