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);
});
});