From 49a1215f7566515b8a27fe3b3f35b37732a530a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sat, 14 Jun 2014 14:21:34 +0200 Subject: [PATCH 01/13] Pass a replay group to ol.render.Event --- src/ol/render/renderevent.js | 11 +++++++-- src/ol/renderer/canvas/canvaslayerrenderer.js | 4 ++-- src/ol/renderer/canvas/canvasmaprenderer.js | 23 ++++++++++++++----- src/ol/renderer/webgl/webgllayerrenderer.js | 2 +- src/ol/renderer/webgl/webglmaprenderer.js | 2 +- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/ol/render/renderevent.js b/src/ol/render/renderevent.js index dcb2597ef5..5000d4c56e 100644 --- a/src/ol/render/renderevent.js +++ b/src/ol/render/renderevent.js @@ -35,13 +35,14 @@ ol.render.EventType = { * @param {ol.render.EventType} type Type. * @param {Object=} opt_target Target. * @param {ol.render.IVectorContext=} opt_vectorContext Vector context. + * @param {ol.render.IReplayGroup=} opt_replayGroup Replay group. * @param {olx.FrameState=} opt_frameState Frame state. * @param {?CanvasRenderingContext2D=} opt_context Context. * @param {?ol.webgl.Context=} opt_glContext WebGL Context. */ ol.render.Event = function( - type, opt_target, opt_vectorContext, opt_frameState, opt_context, - opt_glContext) { + type, opt_target, opt_vectorContext, opt_replayGroup, opt_frameState, + opt_context, opt_glContext) { goog.base(this, type, opt_target); @@ -52,6 +53,12 @@ ol.render.Event = function( */ this.vectorContext = opt_vectorContext; + /** + * @type {ol.render.IReplayGroup|undefined} + * @todo api + */ + this.replayGroup = opt_replayGroup; + /** * @type {olx.FrameState|undefined} * @todo api diff --git a/src/ol/renderer/canvas/canvaslayerrenderer.js b/src/ol/renderer/canvas/canvaslayerrenderer.js index 54375fc932..2732f5a1a0 100644 --- a/src/ol/renderer/canvas/canvaslayerrenderer.js +++ b/src/ol/renderer/canvas/canvaslayerrenderer.js @@ -90,8 +90,8 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = var render = new ol.render.canvas.Immediate( context, frameState.pixelRatio, frameState.extent, transform, frameState.view2DState.rotation); - var composeEvent = new ol.render.Event(type, layer, render, frameState, - context, null); + var composeEvent = new ol.render.Event(type, layer, render, null, + frameState, context, null); layer.dispatchEvent(composeEvent); render.flush(); } diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index c262d394e9..18d1f3dd47 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -17,6 +17,7 @@ goog.require('ol.layer.Vector'); goog.require('ol.render.Event'); goog.require('ol.render.EventType'); goog.require('ol.render.canvas.Immediate'); +goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.Layer'); @@ -97,8 +98,11 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = var map = this.getMap(); var context = this.context_; if (map.hasListener(type)) { - var view2DState = frameState.view2DState; + var extent = frameState.extent; var pixelRatio = frameState.pixelRatio; + var view2DState = frameState.view2DState; + var resolution = view2DState.resolution; + var rotation = view2DState.rotation; ol.vec.Mat4.makeTransform2D(this.transform_, this.canvas_.width / 2, this.canvas_.height / 2, @@ -106,12 +110,19 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = -pixelRatio / view2DState.resolution, -view2DState.rotation, -view2DState.center[0], -view2DState.center[1]); - var render = new ol.render.canvas.Immediate(context, pixelRatio, - frameState.extent, this.transform_, view2DState.rotation); - var composeEvent = new ol.render.Event(type, map, render, frameState, - context, null); + var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio, + extent, this.transform_, rotation); + var tolerance = resolution / (2 * pixelRatio); + var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent, + resolution); + var composeEvent = new ol.render.Event(type, map, vectorContext, + replayGroup, frameState, context, null); map.dispatchEvent(composeEvent); - render.flush(); + vectorContext.flush(); + if (!replayGroup.isEmpty()) { + replayGroup.replay(context, extent, pixelRatio, this.transform_, + rotation, {}); + } } }; diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 707a661abd..823ffcc184 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -239,7 +239,7 @@ ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ = if (layer.hasListener(type)) { var render = new ol.render.webgl.Immediate(context, frameState.pixelRatio); var composeEvent = new ol.render.Event( - type, layer, render, frameState, null, context); + type, layer, render, null, frameState, null, context); layer.dispatchEvent(composeEvent); } }; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 2b8074ee29..ecb2c222fe 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -269,7 +269,7 @@ ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ = var context = this.getContext(); var render = new ol.render.webgl.Immediate(context, frameState.pixelRatio); var composeEvent = new ol.render.Event( - type, map, render, frameState, null, context); + type, map, render, null, frameState, null, context); map.dispatchEvent(composeEvent); } }; From 4f23ef66d082e48f2b65ad4a96d447430d5934a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sat, 14 Jun 2014 14:22:10 +0200 Subject: [PATCH 02/13] Make ol.FeatureOverlay use replay group --- src/ol/featureoverlay.js | 46 +++++-------------- test/spec/ol/featureoverlay.test.js | 70 ----------------------------- 2 files changed, 11 insertions(+), 105 deletions(-) delete mode 100644 test/spec/ol/featureoverlay.test.js diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 3aa6b1e045..39644d15f8 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -10,7 +10,7 @@ goog.require('ol.CollectionEventType'); goog.require('ol.Feature'); goog.require('ol.feature'); goog.require('ol.render.EventType'); -goog.require('ol.style.ImageState'); +goog.require('ol.renderer.vector'); @@ -101,37 +101,6 @@ ol.FeatureOverlay.prototype.addFeature = function(feature) { }; -/** - * @param {ol.render.IVectorContext|undefined} vectorContext Vector context. - * @param {ol.Feature} feature Feature. - * @param {ol.style.Style} style Style. - * @private - */ -ol.FeatureOverlay.prototype.drawFeature_ = function(vectorContext, feature, - style) { - var imageStyle = style.getImage(); - if (!goog.isNull(imageStyle)) { - var imageState = imageStyle.getImageState(); - if (imageState == ol.style.ImageState.LOADED || - imageState == ol.style.ImageState.ERROR) { - imageStyle.unlistenImageChange(this.handleImageChange_, this); - if (imageState == ol.style.ImageState.LOADED) { - vectorContext.drawFeature(feature, style); - } - } else { - if (imageState == ol.style.ImageState.IDLE) { - imageStyle.load(); - } - imageState = imageStyle.getImageState(); - goog.asserts.assert(imageState == ol.style.ImageState.LOADING); - imageStyle.listenImageChange(this.handleImageChange_, this); - } - } else { - vectorContext.drawFeature(feature, style); - } -}; - - /** * @return {ol.Collection} Features collection. * @todo api @@ -199,8 +168,14 @@ ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) { if (!goog.isDef(styleFunction)) { styleFunction = ol.feature.defaultStyleFunction; } - var resolution = event.frameState.view2DState.resolution; - var vectorContext = event.vectorContext; + var replayGroup = /** @type {ol.render.IReplayGroup} */ + (event.replayGroup); + goog.asserts.assert(goog.isDef(replayGroup)); + var frameState = event.frameState; + var pixelRatio = frameState.pixelRatio; + var resolution = frameState.view2DState.resolution; + var squaredTolerance = + resolution * resolution / (4 * pixelRatio * pixelRatio); var i, ii, styles; this.features_.forEach(function(feature) { styles = styleFunction(feature, resolution); @@ -209,7 +184,8 @@ ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) { } ii = styles.length; for (i = 0; i < ii; ++i) { - this.drawFeature_(vectorContext, feature, styles[i]); + ol.renderer.vector.renderFeature(replayGroup, feature, styles[i], + squaredTolerance, feature, this.handleImageChange_, this); } }, this); }; diff --git a/test/spec/ol/featureoverlay.test.js b/test/spec/ol/featureoverlay.test.js deleted file mode 100644 index fe46d74f4f..0000000000 --- a/test/spec/ol/featureoverlay.test.js +++ /dev/null @@ -1,70 +0,0 @@ -goog.provide('ol.test.FeatureOverlay'); - -describe('ol.Feature', function() { - var featureOverlay; - - beforeEach(function() { - featureOverlay = new ol.FeatureOverlay(); - }); - - afterEach(function() { - ol.style.IconImageCache.getInstance().clear(); - }); - - describe('#drawFeature_ style with no image', function() { - it('calls vectorContext.drawFeature', function() { - var vectorContext = new ol.render.canvas.Immediate( - null, // context - 1, // pixelRatio - [], // extent - goog.vec.Mat4.createNumberIdentity(), // transform - 0 // viewRotation - ); - var feature = new ol.Feature(); - var style = new ol.style.Style({ - fill: new ol.style.Fill({ - color: '#ffffff' - }) - }); - - var spy = sinon.spy(vectorContext, 'drawFeature'); - featureOverlay.drawFeature_(vectorContext, feature, style); - expect(spy.calledOnce).to.be.ok(); - }); - }); - - describe('#drawFeature_ style with unloaded image', function() { - it('calls image.load', function() { - var vectorContext = new ol.render.canvas.Immediate( - null, // context - 1, // pixelRatio - [], // extent - goog.vec.Mat4.createNumberIdentity(), // transform - 0 // viewRotation - ); - var feature = new ol.Feature(); - var style = new ol.style.Style({ - image: new ol.style.Icon({ - src: 'http://example.com/icon.png' - }) - }); - - var stub = sinon.stub(style.getImage(), 'load', function() { - style.getImage().iconImage_.imageState_ = - ol.style.ImageState.LOADING; - }); - featureOverlay.drawFeature_(vectorContext, feature, style); - expect(stub.calledOnce).to.be.ok(); - }); - }); -}); - -goog.require('goog.vec.Mat4'); -goog.require('ol.Feature'); -goog.require('ol.FeatureOverlay'); -goog.require('ol.style.ImageState'); -goog.require('ol.render.canvas.Immediate'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Icon'); -goog.require('ol.style.IconImageCache'); -goog.require('ol.style.Style'); From 18d117dff624c19c1b7275cac498eafe2561af23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sat, 14 Jun 2014 15:13:00 +0200 Subject: [PATCH 03/13] Hit detection for features drawn at pre/postcompose --- src/ol/renderer/canvas/canvasmaprenderer.js | 1 + src/ol/renderer/maprenderer.js | 37 +++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 18d1f3dd47..9ce9c2fa7b 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -123,6 +123,7 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = replayGroup.replay(context, extent, pixelRatio, this.transform_, rotation, {}); } + this.replayGroup = replayGroup; } }; diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 9308533ff1..d8cd7cf3a8 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -43,6 +43,12 @@ ol.renderer.Map = function(container, map) { */ this.map_ = map; + /** + * @protected + * @type {ol.render.IReplayGroup} + */ + this.replayGroup = null; + /** * @private * @type {Object.} @@ -110,9 +116,36 @@ ol.renderer.Map.prototype.disposeInternal = function() { ol.renderer.Map.prototype.forEachFeatureAtPixel = function(coordinate, frameState, callback, thisArg, layerFilter, thisArg2) { + var result; + var extent = frameState.extent; + var view2DState = frameState.view2DState; + var viewResolution = view2DState.resolution; + var viewRotation = view2DState.rotation; + if (!goog.isNull(this.replayGroup)) { + /** @type {Object.} */ + var features = {}; + result = this.replayGroup.forEachGeometryAtPixel(extent, viewResolution, + viewRotation, coordinate, {}, + /** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {Object} data Data. + * @return {?} Callback result. + */ + function(geometry, data) { + var feature = /** @type {ol.Feature} */ (data); + goog.asserts.assert(goog.isDef(feature)); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, null); + } + }); + if (result) { + return result; + } + } var layerStates = this.map_.getLayerGroup().getLayerStatesArray(); var numLayers = layerStates.length; - var viewResolution = frameState.view2DState.resolution; var i; for (i = numLayers - 1; i >= 0; --i) { var layerState = layerStates[i]; @@ -120,7 +153,7 @@ ol.renderer.Map.prototype.forEachFeatureAtPixel = if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { var layerRenderer = this.getLayerRenderer(layer); - var result = layerRenderer.forEachFeatureAtPixel( + result = layerRenderer.forEachFeatureAtPixel( coordinate, frameState, callback, thisArg); if (result) { return result; From bcdce20ee3a82c201a031f31735b4df66d5641c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sat, 14 Jun 2014 15:13:21 +0200 Subject: [PATCH 04/13] Clone skippedFeatureUids_ --- src/ol/map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/map.js b/src/ol/map.js index 6412a15574..a1e2d0fb72 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -1202,7 +1202,7 @@ ol.Map.prototype.renderFrame_ = function(time) { pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_, postRenderFunctions: [], size: size, - skippedFeatureUids_: this.skippedFeatureUids_, + skippedFeatureUids_: goog.object.clone(this.skippedFeatureUids_), tileQueue: this.tileQueue_, time: time, usedTiles: {}, From 0762b8c0230107853143305dfd4db133f295b60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 26 Jun 2014 10:03:23 +0200 Subject: [PATCH 05/13] Defer selection/deselection --- src/ol/interaction/selectinteraction.js | 11 +++++++++-- src/ol/map.js | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ol/interaction/selectinteraction.js b/src/ol/interaction/selectinteraction.js index a926065dee..8d3f727ce1 100644 --- a/src/ol/interaction/selectinteraction.js +++ b/src/ol/interaction/selectinteraction.js @@ -150,6 +150,8 @@ ol.interaction.Select.prototype.handleMapBrowserEvent = } } else { // Modify the currently selected feature(s). + var /** @type {Array.} */ deselected = []; + var /** @type {Array.} */ selected = []; map.forEachFeatureAtPixel(mapBrowserEvent.pixel, /** * @param {ol.Feature} feature Feature. @@ -159,14 +161,19 @@ ol.interaction.Select.prototype.handleMapBrowserEvent = var index = goog.array.indexOf(features.getArray(), feature); if (index == -1) { if (add || toggle) { - features.push(feature); + selected.push(feature); } } else { if (remove || toggle) { - features.removeAt(index); + deselected.push(index); } } }, undefined, this.layerFilter_); + var i; + for (i = deselected.length - 1; i >= 0; --i) { + features.removeAt(deselected[i]); + } + features.extend(selected); } return false; }; diff --git a/src/ol/map.js b/src/ol/map.js index a1e2d0fb72..6412a15574 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -1202,7 +1202,7 @@ ol.Map.prototype.renderFrame_ = function(time) { pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_, postRenderFunctions: [], size: size, - skippedFeatureUids_: goog.object.clone(this.skippedFeatureUids_), + skippedFeatureUids_: this.skippedFeatureUids_, tileQueue: this.tileQueue_, time: time, usedTiles: {}, From ba2d49d715272d7c20053d5286578d8e86f56b47 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 26 Jun 2014 13:16:34 +0200 Subject: [PATCH 06/13] Finish the replay group after dispatching the compose event --- src/ol/renderer/canvas/canvasmaprenderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 9ce9c2fa7b..3be96c664b 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -118,6 +118,7 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = var composeEvent = new ol.render.Event(type, map, vectorContext, replayGroup, frameState, context, null); map.dispatchEvent(composeEvent); + replayGroup.finish(); vectorContext.flush(); if (!replayGroup.isEmpty()) { replayGroup.replay(context, extent, pixelRatio, this.transform_, From 6cf4c957621abe7db2570135a47154c5ba96a3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 26 Jun 2014 14:06:56 +0200 Subject: [PATCH 07/13] Flush the vector context after replaying the batch --- src/ol/renderer/canvas/canvasmaprenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 3be96c664b..1211d408ba 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -119,11 +119,11 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = replayGroup, frameState, context, null); map.dispatchEvent(composeEvent); replayGroup.finish(); - vectorContext.flush(); if (!replayGroup.isEmpty()) { replayGroup.replay(context, extent, pixelRatio, this.transform_, rotation, {}); } + vectorContext.flush(); this.replayGroup = replayGroup; } }; From b25e5778651767c02df4191ab020caedc32bc7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 26 Jun 2014 14:11:43 +0200 Subject: [PATCH 08/13] Do not "export" ol.render.Event#replayGroup --- src/ol/render/renderevent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ol/render/renderevent.js b/src/ol/render/renderevent.js index 5000d4c56e..df6fe50664 100644 --- a/src/ol/render/renderevent.js +++ b/src/ol/render/renderevent.js @@ -55,7 +55,6 @@ ol.render.Event = function( /** * @type {ol.render.IReplayGroup|undefined} - * @todo api */ this.replayGroup = opt_replayGroup; From 1551e6e7ffbc2dbaff4b57d89df26c9c4a1374d6 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 26 Jun 2014 15:17:29 +0200 Subject: [PATCH 09/13] Improved forEatchFeatureAtPixel docs --- src/ol/map.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 6412a15574..421e8d900b 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -535,16 +535,24 @@ ol.Map.prototype.disposeInternal = function() { /** + * Detect features that intersect a pixel on the viewport, and execute a + * callback with each intersecting feature. Layers included in the detection can + * be configured through `opt_layerFilter`. Feature overlays will always be + * included in the detection. * @param {ol.Pixel} pixel Pixel. * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature - * callback. + * callback. If the detected feature is not on a layer, but on a + * {@link ol.FeatureOverlay}, then the 2nd argument to this function will + * be `null`. To stop detection, callback functions can return a truthy + * value. * @param {S=} opt_this Value to use as `this` when executing `callback`. * @param {function(this: U, ol.layer.Layer): boolean=} opt_layerFilter Layer * filter function, only layers which are visible and for which this - * function returns `true` will be tested for features. By default, all - * visible layers will be tested. + * function returns `true` will be tested for features. By default, all + * visible layers will be tested. Feature overlays will always be tested. * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`. - * @return {T|undefined} Callback result. + * @return {T|undefined} Callback result, i.e. the return value of last + * callback execution, or the first truthy callback return value. * @template S,T,U * @todo api */ From 2171c92b5c9ac5d07ab1f2691db6eac5da8829cd Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 27 Jun 2014 11:46:47 +0200 Subject: [PATCH 10/13] Add new tests for FeatureOverlay In the process of creating these tests, a bug with setting this._style was also fixed. --- src/ol/featureoverlay.js | 2 +- test/spec/ol/featureoverlay.test.js | 34 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/spec/ol/featureoverlay.test.js diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 39644d15f8..5419977f32 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -65,7 +65,7 @@ ol.FeatureOverlay = function(opt_options) { * @private * @type {ol.style.Style|Array.|ol.feature.StyleFunction} */ - this.style_ = null; + this.style_ = goog.isDef(options.style) ? options.style : null; /** * @private diff --git a/test/spec/ol/featureoverlay.test.js b/test/spec/ol/featureoverlay.test.js new file mode 100644 index 0000000000..47aa06189a --- /dev/null +++ b/test/spec/ol/featureoverlay.test.js @@ -0,0 +1,34 @@ +goog.provide('ol.test.FeatureOverlay'); + +describe('ol.FeatureOverlay', function() { + + describe('constructor', function() { + + it('creates an new feature overlay', function() { + var featureOverlay = new ol.FeatureOverlay(); + expect(featureOverlay).to.be.a(ol.FeatureOverlay); + }); + + it('takes features', function() { + var featureOverlay = new ol.FeatureOverlay({ + features: [new ol.Feature(new ol.geom.Point([0, 0]))] + }); + expect(featureOverlay.getFeatures().getLength()).to.be(1); + }); + + it('takes a style', function() { + var style = [new ol.style.Style()]; + var featureOverlay = new ol.FeatureOverlay({ + style: [new ol.style.Style()] + }); + expect(featureOverlay.getStyle()).to.eql(style); + expect(featureOverlay.getStyleFunction()()).to.eql(style); + }); + + }); +}); + +goog.require('ol.Feature'); +goog.require('ol.FeatureOverlay'); +goog.require('ol.geom.Point'); +goog.require('ol.style.Style'); From f67767aaf0fd5caa6cb8d221d2e19ff51c381be7 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 27 Jun 2014 12:10:46 +0200 Subject: [PATCH 11/13] Add ol.renderer.vector.getSquaredTolerance and ol.SIMPLIFY_TOLERANCE --- src/ol/featureoverlay.js | 5 ++--- src/ol/geom/flat/simplifyflatgeom.js | 4 ++-- src/ol/ol.js | 6 ++++++ src/ol/render/vector.js | 21 +++++++++++++++++++ src/ol/renderer/canvas/canvasmaprenderer.js | 5 +++-- .../canvas/canvasvectorlayerrenderer.js | 13 ++++++------ src/ol/source/imagevectorsource.js | 12 +++++------ 7 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 5419977f32..77e3055274 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -174,8 +174,6 @@ ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) { var frameState = event.frameState; var pixelRatio = frameState.pixelRatio; var resolution = frameState.view2DState.resolution; - var squaredTolerance = - resolution * resolution / (4 * pixelRatio * pixelRatio); var i, ii, styles; this.features_.forEach(function(feature) { styles = styleFunction(feature, resolution); @@ -185,7 +183,8 @@ ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) { ii = styles.length; for (i = 0; i < ii; ++i) { ol.renderer.vector.renderFeature(replayGroup, feature, styles[i], - squaredTolerance, feature, this.handleImageChange_, this); + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + feature, this.handleImageChange_, this); } }, this); }; diff --git a/src/ol/geom/flat/simplifyflatgeom.js b/src/ol/geom/flat/simplifyflatgeom.js index b5f2032040..e1f992fc1c 100644 --- a/src/ol/geom/flat/simplifyflatgeom.js +++ b/src/ol/geom/flat/simplifyflatgeom.js @@ -238,7 +238,7 @@ ol.geom.flat.simplify.radialDistance = function(flatCoordinates, offset, end, /** * @param {number} value Value. - * @param {number} tolerance Squared tolerance. + * @param {number} tolerance Tolerance. * @return {number} Rounded value. */ ol.geom.flat.simplify.snap = function(value, tolerance) { @@ -259,7 +259,7 @@ ol.geom.flat.simplify.snap = function(value, tolerance) { * @param {number} offset Offset. * @param {number} end End. * @param {number} stride Stride. - * @param {number} tolerance Squared tolerance. + * @param {number} tolerance Tolerance. * @param {Array.} simplifiedFlatCoordinates Simplified flat * coordinates. * @param {number} simplifiedOffset Simplified offset. diff --git a/src/ol/ol.js b/src/ol/ol.js index 56c0874dc6..c2057ed4c4 100644 --- a/src/ol/ol.js +++ b/src/ol/ol.js @@ -173,6 +173,12 @@ ol.MOUSEWHEELZOOM_TIMEOUT_DURATION = 80; ol.ROTATE_ANIMATION_DURATION = 250; +/** + * @define {number} Tolerance for geometry simplification in device pixels. + */ +ol.SIMPLIFY_TOLERANCE = 0.5; + + /** * @define {number} Texture cache high water mark. */ diff --git a/src/ol/render/vector.js b/src/ol/render/vector.js index 93271dd843..39b259670a 100644 --- a/src/ol/render/vector.js +++ b/src/ol/render/vector.js @@ -24,6 +24,27 @@ ol.renderer.vector.defaultOrder = function(feature1, feature2) { }; +/** + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @return {number} Squared pixel tolerance. + */ +ol.renderer.vector.getSquaredTolerance = function(resolution, pixelRatio) { + var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio); + return tolerance * tolerance; +}; + + +/** + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @return {number} Pixel tolerance. + */ +ol.renderer.vector.getTolerance = function(resolution, pixelRatio) { + return ol.SIMPLIFY_TOLERANCE * resolution / pixelRatio; +}; + + /** * @param {ol.render.IReplayGroup} replayGroup Replay group. * @param {ol.geom.Geometry} geometry Geometry. diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 1211d408ba..7177e5c824 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -23,6 +23,7 @@ goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); +goog.require('ol.renderer.vector'); goog.require('ol.source.State'); goog.require('ol.vec.Mat4'); @@ -112,8 +113,8 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = -view2DState.center[0], -view2DState.center[1]); var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio, extent, this.transform_, rotation); - var tolerance = resolution / (2 * pixelRatio); - var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent, + var replayGroup = new ol.render.canvas.ReplayGroup( + ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution); var composeEvent = new ol.render.Event(type, map, vectorContext, replayGroup, frameState, context, null); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index aa148b4d77..19970bdc7f 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -206,9 +206,10 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = if (!goog.isDef(styleFunction)) { styleFunction = ol.feature.defaultStyleFunction; } - var tolerance = resolution / (2 * pixelRatio); var replayGroup = - new ol.render.canvas.ReplayGroup(tolerance, extent, resolution); + new ol.render.canvas.ReplayGroup( + ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, + resolution); vectorSource.loadFeatures(extent, resolution, projection); var renderFeature = /** @@ -261,14 +262,12 @@ ol.renderer.canvas.VectorLayer.prototype.renderFeature = if (!goog.isDefAndNotNull(styles)) { return false; } - // simplify to a tolerance of half a device pixel - var squaredTolerance = - resolution * resolution / (4 * pixelRatio * pixelRatio); var i, ii, loading = false; for (i = 0, ii = styles.length; i < ii; ++i) { loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles[i], squaredTolerance, feature, - this.handleImageChange_, this) || loading; + replayGroup, feature, styles[i], + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + feature, this.handleImageChange_, this) || loading; } return loading; }; diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index c26b1a9d87..992ac56c7c 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -102,8 +102,8 @@ goog.inherits(ol.source.ImageVector, ol.source.ImageCanvas); ol.source.ImageVector.prototype.canvasFunctionInternal_ = function(extent, resolution, pixelRatio, size, projection) { - var tolerance = resolution / (2 * pixelRatio); - var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent, + var replayGroup = new ol.render.canvas.ReplayGroup( + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), extent, resolution); var loading = false; @@ -226,14 +226,12 @@ ol.source.ImageVector.prototype.renderFeature_ = if (!goog.isDefAndNotNull(styles)) { return false; } - // simplify to a tolerance of half a device pixel - var squaredTolerance = - resolution * resolution / (4 * pixelRatio * pixelRatio); var i, ii, loading = false; for (i = 0, ii = styles.length; i < ii; ++i) { loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles[i], squaredTolerance, feature, - this.handleImageChange_, this) || loading; + replayGroup, feature, styles[i], + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + feature, this.handleImageChange_, this) || loading; } return loading; }; From 80fa0be42f53da5227df18e0070613ef28e29416 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 27 Jun 2014 12:14:22 +0200 Subject: [PATCH 12/13] Use setStyle in the constructor --- src/ol/featureoverlay.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 77e3055274..b9db01bb52 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -65,14 +65,17 @@ ol.FeatureOverlay = function(opt_options) { * @private * @type {ol.style.Style|Array.|ol.feature.StyleFunction} */ - this.style_ = goog.isDef(options.style) ? options.style : null; + this.style_ = null; /** * @private * @type {ol.feature.StyleFunction|undefined} */ - this.styleFunction_ = goog.isDef(options.style) ? - ol.feature.createStyleFunction(options.style) : undefined; + this.styleFunction_ = undefined; + + if (goog.isDef(options.style)) { + this.setStyle(options.style); + } if (goog.isDef(options.features)) { if (goog.isArray(options.features)) { From cf13cb5e3248cbff455677e994d67728ca40e465 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 27 Jun 2014 12:20:16 +0200 Subject: [PATCH 13/13] Fix remaining API doc issues with squared tolerance vs. tolerance --- src/ol/geom/flat/simplifyflatgeom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/geom/flat/simplifyflatgeom.js b/src/ol/geom/flat/simplifyflatgeom.js index e1f992fc1c..7b747c6280 100644 --- a/src/ol/geom/flat/simplifyflatgeom.js +++ b/src/ol/geom/flat/simplifyflatgeom.js @@ -344,7 +344,7 @@ ol.geom.flat.simplify.quantize = function(flatCoordinates, offset, end, stride, * @param {number} offset Offset. * @param {Array.} ends Ends. * @param {number} stride Stride. - * @param {number} tolerance Squared tolerance. + * @param {number} tolerance Tolerance. * @param {Array.} simplifiedFlatCoordinates Simplified flat * coordinates. * @param {number} simplifiedOffset Simplified offset. @@ -374,7 +374,7 @@ ol.geom.flat.simplify.quantizes = function( * @param {number} offset Offset. * @param {Array.>} endss Endss. * @param {number} stride Stride. - * @param {number} tolerance Squared tolerance. + * @param {number} tolerance Tolerance. * @param {Array.} simplifiedFlatCoordinates Simplified flat * coordinates. * @param {number} simplifiedOffset Simplified offset.