From ac67c10acdf3c32379cbbe2edfe1fe1c2389cb04 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 2 Jul 2013 14:51:55 +0200 Subject: [PATCH 1/5] Add and remove features with meaningful events This adds a removeFeatures method, and makes the CHANGE event more useful by adding information about changed features and extent. --- src/ol/layer/vectorlayer.js | 68 +++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index fdef16b75a..83d4249a3d 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -10,6 +10,7 @@ goog.require('ol.expr.Literal'); goog.require('ol.expr.Logical'); goog.require('ol.expr.LogicalOp'); goog.require('ol.expr.functions'); +goog.require('ol.extent'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.SharedVertices'); goog.require('ol.layer.Layer'); @@ -200,6 +201,34 @@ ol.layer.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { }; +/** + * Remove a feature from the cache. + * @param {ol.Feature} feature Feature. + */ +ol.layer.FeatureCache.prototype.remove = function(feature) { + var id = goog.getUid(feature).toString(), + geometry = feature.getGeometry(); + + delete this.idLookup_[id]; + + // index by geometry type and bounding box + if (!goog.isNull(geometry)) { + var geometryType = geometry.getType(); + delete this.geometryTypeIndex_[geometryType][id]; + this.rTree_.remove(geometry.getBounds(), feature); + } +}; + + +/** + * TODO: Create a VectorLayerEvent with ADD and REMOVE event types + * @typedef {{extent: (ol.Extent|undefined), + * features: (Array.|undefined), + * type: goog.events.EventType}} + */ +ol.layer.VectorLayerEventObject; + + /** * @constructor @@ -262,11 +291,21 @@ goog.inherits(ol.layer.Vector, ol.layer.Layer); * @param {Array.} features Array of features. */ ol.layer.Vector.prototype.addFeatures = function(features) { + var extent = ol.extent.createEmpty(), + feature, geometry; for (var i = 0, ii = features.length; i < ii; ++i) { - this.featureCache_.add(features[i]); + feature = features[i]; + this.featureCache_.add(feature); + geometry = feature.getGeometry(); + if (!goog.isNull(geometry)) { + ol.extent.extend(extent, geometry.getBounds()); + } } - // TODO: events for real - listeners want features and extent here - this.dispatchEvent(goog.events.EventType.CHANGE); + this.dispatchEvent(/** @type {ol.layer.VectorLayerEventObject} */ ({ + extent: extent, + features: features, + type: goog.events.EventType.CHANGE + })); }; @@ -456,6 +495,29 @@ ol.layer.Vector.prototype.getTransformFeatureInfo = function() { }; +/** + * Remove features from the layer. + * @param {Array.} features Features to remove. + */ +ol.layer.Vector.prototype.removeFeatures = function(features) { + var extent = ol.extent.createEmpty(), + feature, geometry; + for (var i = 0, ii = features.length; i < ii; ++i) { + feature = features[i]; + this.featureCache_.remove(feature); + geometry = feature.getGeometry(); + if (!goog.isNull(geometry)) { + ol.extent.extend(extent, geometry.getBounds()); + } + } + this.dispatchEvent(/** @type {ol.layer.VectorLayerEventObject} */ ({ + extent: extent, + features: features, + type: goog.events.EventType.CHANGE + })); +}; + + /** * @param {Array.} features Features. * @return {string} Feature info. From d04c6f4c017bc18ec336abaffbca5a34cddb687e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 2 Jul 2013 14:54:59 +0200 Subject: [PATCH 2/5] New pruneTileRange method This also adds an assert that cache values are an instance of ol.Tile in expireCache, instead of doing a type cast. Later we will want to enforce the ol.Tile type by overriding the set method. --- src/ol/tilecache.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js index 09cf94c81d..61d2a940ac 100644 --- a/src/ol/tilecache.js +++ b/src/ol/tilecache.js @@ -1,6 +1,8 @@ goog.provide('ol.TileCache'); +goog.require('goog.asserts'); goog.require('ol.Tile'); +goog.require('ol.TileCoord'); goog.require('ol.TileRange'); goog.require('ol.structs.LRUCache'); @@ -46,7 +48,10 @@ ol.TileCache.prototype.canExpireCache = function() { ol.TileCache.prototype.expireCache = function(usedTiles) { var tile, zKey; while (this.canExpireCache()) { - tile = /** @type {ol.Tile} */ (this.peekLast()); + tile = (this.peekLast()); + // TODO: Enforce ol.Tile in ol.TileCache#set + goog.asserts.assert(tile instanceof ol.Tile, + 'ol.TileCache#expireCache only works with ol.Tile values.'); zKey = tile.tileCoord.z.toString(); if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) { break; @@ -55,3 +60,21 @@ ol.TileCache.prototype.expireCache = function(usedTiles) { } } }; + + +/** + * Remove a tile range from the cache, e.g. to invalidate tiles. + * @param {ol.TileRange} tileRange The tile range to prune. + */ +ol.TileCache.prototype.pruneTileRange = function(tileRange) { + var i = this.getCount(), + key; + while (i--) { + key = this.peekLastKey(); + if (tileRange.contains(ol.TileCoord.createFromString(key))) { + this.pop(); + } else { + this.get(key); + } + } +}; From 781caff9e529f5fa06509df7555c3c7ab3d46689 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 2 Jul 2013 14:56:46 +0200 Subject: [PATCH 3/5] Expire tile by range and listen to changes Instead of just listening for the first change, we now listen for all changes, and expire tiles by tile range. The outcome is that the vector layer's addFeatures and removeFeatures methods now show instant results on the rendered map. --- .../renderer/canvas/canvasvectorlayerrenderer.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index bc378f465c..94260aac9f 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -86,8 +86,7 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileCache_ = new ol.TileCache( ol.renderer.canvas.VectorLayer.TILECACHE_SIZE); - // TODO: this is far too coarse, we want extent of added features - goog.events.listenOnce(layer, goog.events.EventType.CHANGE, + goog.events.listen(layer, goog.events.EventType.CHANGE, this.handleLayerChange_, false, this); /** @@ -177,10 +176,13 @@ goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); * @private */ ol.renderer.canvas.VectorLayer.prototype.expireTiles_ = function(opt_extent) { + var tileCache = this.tileCache_; if (goog.isDef(opt_extent)) { - // TODO: implement this + var tileRange = this.tileGrid_.getTileRangeForExtentAndZ(opt_extent, 0); + tileCache.pruneTileRange(tileRange); + } else { + tileCache.clear(); } - this.tileCache_.clear(); }; @@ -305,12 +307,11 @@ ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel = /** - * @param {goog.events.Event} event Layer change event. + * @param {ol.layer.VectorLayerEventObject} event Layer change event. * @private */ ol.renderer.canvas.VectorLayer.prototype.handleLayerChange_ = function(event) { - // TODO: get rid of this in favor of vector specific events - this.expireTiles_(); + this.expireTiles_(event.extent); this.requestMapRenderFrame_(); }; @@ -526,6 +527,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tile.getContext('2d').drawImage(sketchCanvas, (tileRange.minX - tileCoord.x) * tileSize[0], (tileCoord.y - tileRange.maxY) * tileSize[1]); + // TODO: Create an ol.VectorTile subclass of ol.Tile this.tileCache_.set(key, [tile, symbolSizes, maxSymbolSize]); } finalContext.drawImage(tile, From b43625820e6116585d92796f84c423b94809eabc Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 2 Jul 2013 14:57:48 +0200 Subject: [PATCH 4/5] Removing addFeatures from the API --- src/ol/layer/vectorlayer.exports | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index fa79c6b730..111527584d 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1,3 +1 @@ @exportClass ol.layer.Vector ol.layer.VectorLayerOptions - -@exportProperty ol.layer.Vector.prototype.addFeatures From ca9f9b756316bcd16263a5b2dfe7cb7438c9d23c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 19 Jul 2013 13:52:38 +0200 Subject: [PATCH 5/5] Re-adding type cast, as suggested by @elemoine --- src/ol/tilecache.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js index 61d2a940ac..366a8ce364 100644 --- a/src/ol/tilecache.js +++ b/src/ol/tilecache.js @@ -48,7 +48,7 @@ ol.TileCache.prototype.canExpireCache = function() { ol.TileCache.prototype.expireCache = function(usedTiles) { var tile, zKey; while (this.canExpireCache()) { - tile = (this.peekLast()); + tile = /** @type {ol.Tile} */ (this.peekLast()); // TODO: Enforce ol.Tile in ol.TileCache#set goog.asserts.assert(tile instanceof ol.Tile, 'ol.TileCache#expireCache only works with ol.Tile values.');