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 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. 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, diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js index 09cf94c81d..366a8ce364 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'); @@ -47,6 +49,9 @@ ol.TileCache.prototype.expireCache = function(usedTiles) { var tile, zKey; while (this.canExpireCache()) { 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.'); 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); + } + } +};