diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 6ef234f60d..38b7367a22 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,7 +1,9 @@ goog.provide('ol.layer.Vector'); +goog.require('ol.Feature'); goog.require('ol.layer.Layer'); goog.require('ol.source.Vector'); +goog.require('ol.style.Style'); @@ -12,6 +14,13 @@ goog.require('ol.source.Vector'); */ ol.layer.Vector = function(layerOptions) { goog.base(this, layerOptions); + + /** + * @private + * @type {ol.style.Style} + */ + this.style_ = goog.isDef(layerOptions.style) ? layerOptions.style : null; + }; goog.inherits(ol.layer.Vector, ol.layer.Layer); @@ -22,3 +31,38 @@ goog.inherits(ol.layer.Vector, ol.layer.Layer); ol.layer.Vector.prototype.getVectorSource = function() { return /** @type {ol.source.Vector} */ (this.getSource()); }; + + +/** + * @param {Array.} features Features. + * @return {Array.} symbolizers for features. + */ +ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = + function(features) { + var uniqueLiterals = {}, + featuresBySymbolizer = [], + style = this.style_, + feature, literals, literal, key; + for (var i = 0, ii = features.length; i < ii; ++i) { + feature = features[i]; + literals = goog.isNull(style) ? + ol.style.Style.applyDefaultStyle(feature) : + style.apply(feature); + for (var j = 0, jj = literals.length; j < jj; ++j) { + literal = literals[j]; + for (var l in uniqueLiterals) { + if (literal.equals(uniqueLiterals[l])) { + literal = uniqueLiterals[l]; + break; + } + } + key = goog.getUid(literal); + if (!goog.object.containsKey(uniqueLiterals, key)) { + uniqueLiterals[key] = featuresBySymbolizer.length; + featuresBySymbolizer.push([[], literal]); + } + featuresBySymbolizer[uniqueLiterals[key]][0].push(feature); + } + } + return featuresBySymbolizer; +}; diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 5efe58ade3..38a82edfb7 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -14,10 +14,6 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.Renderer'); -goog.require('ol.style.LineLiteral'); -goog.require('ol.style.PolygonLiteral'); -goog.require('ol.style.ShapeLiteral'); -goog.require('ol.style.ShapeType'); goog.require('ol.tilegrid.TileGrid'); @@ -115,31 +111,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { this.dirty_ = false; - // TODO: implement layer.setStyle(style) where style is a set of rules - // and a rule has a filter and array of symbolizers - var symbolizers = {}; - symbolizers[ol.geom.GeometryType.POINT] = new ol.style.ShapeLiteral({ - type: ol.style.ShapeType.CIRCLE, - size: 10, - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.75 - }); - symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LineLiteral({ - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 1 - }); - symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.PolygonLiteral({ - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.5 - }); - // TODO: remove this - this.symbolizers_ = symbolizers; - /** * @private * @type {boolean} @@ -195,7 +166,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var view2DState = frameState.view2DState, resolution = view2DState.resolution, extent = frameState.extent, - source = this.getVectorLayer().getVectorSource(), + layer = this.getVectorLayer(), + source = layer.getVectorSource(), tileGrid = this.tileGrid_; if (goog.isNull(tileGrid)) { @@ -295,7 +267,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, - i, geomFilter, extentFilter, type, features, symbolizer; + i, geomFilter, extentFilter, type, features, + groups, group, j, numGroups; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tileCoord = new ol.TileCoord(z, x, y); @@ -312,10 +285,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = features = source.getFeatures(new ol.filter.Logical( [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { - // TODO: layer.getSymbolizerLiterals(features) or similar - symbolizer = this.symbolizers_[type]; - sketchCanvasRenderer.renderFeaturesByGeometryType( - type, features, symbolizer); + groups = layer.groupFeaturesBySymbolizerLiteral(features); + numGroups = groups.length; + for (j = 0; j < numGroups; ++j) { + group = groups[j]; + sketchCanvasRenderer.renderFeaturesByGeometryType(type, + group[0], group[1]); + } } } } diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 3b0c6cd1cf..7d8519067f 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -37,6 +37,16 @@ ol.style.LineLiteral = function(config) { goog.inherits(ol.style.LineLiteral, ol.style.SymbolizerLiteral); +/** + * @inheritDoc + */ +ol.style.LineLiteral.prototype.equals = function(lineLiteral) { + return this.strokeStyle == lineLiteral.strokeStyle && + this.strokeWidth == lineLiteral.strokeWidth && + this.opacity == lineLiteral.opacity; +}; + + /** * @typedef {{strokeStyle: (string|ol.Expression), * strokeWidth: (number|ol.Expression), diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index fe67bf677c..5886ccf474 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -41,6 +41,17 @@ ol.style.PolygonLiteral = function(config) { goog.inherits(ol.style.PolygonLiteral, ol.style.SymbolizerLiteral); +/** + * @inheritDoc + */ +ol.style.PolygonLiteral.prototype.equals = function(polygonLiteral) { + return this.fillStyle == polygonLiteral.fillStyle && + this.strokeStyle == polygonLiteral.strokeStyle && + this.strokeWidth == polygonLiteral.strokeWidth && + this.opacity == polygonLiteral.opacity; +}; + + /** * @typedef {{fillStyle: (string|ol.Expression), * strokeStyle: (string|ol.Expression), diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index 90e7ae8b00..14b054a3d3 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -1,5 +1,6 @@ goog.provide('ol.style.Rule'); +goog.require('ol.Feature'); goog.require('ol.filter.Filter'); goog.require('ol.style.Symbolizer'); @@ -32,3 +33,21 @@ ol.style.Rule = function(options) { options.symbolizers : []; }; + + +/** + * @param {ol.Feature} feature Feature. + * @return {boolean} Does the rule apply to the feature? + */ +ol.style.Rule.prototype.applies = function(feature) { + return goog.isNull(this.filter_) ? true : this.filter_.applies(feature); +}; + + +/** + * @return {Array.} Symbolizers. + */ +ol.style.Rule.prototype.getSymbolizers = function() { + return this.symbolizers_; +}; + diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index e3007f5205..92c407c452 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -57,6 +57,19 @@ ol.style.ShapeLiteral = function(config) { goog.inherits(ol.style.ShapeLiteral, ol.style.PointLiteral); +/** + * @inheritDoc + */ +ol.style.ShapeLiteral.prototype.equals = function(shapeLiteral) { + return this.type == shapeLiteral.type && + this.size == shapeLiteral.size && + this.fillStyle == shapeLiteral.fillStyle && + this.strokeStyle == shapeLiteral.strokeStyle && + this.strokeWidth == shapeLiteral.strokeWidth && + this.opacity == shapeLiteral.opacity; +}; + + /** * @typedef {{type: (ol.style.ShapeType), * size: (number|ol.Expression), diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 536c8dac6c..86271dcaf9 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -1,6 +1,9 @@ goog.provide('ol.style.Style'); +goog.require('ol.Feature'); +goog.require('ol.geom.GeometryType'); goog.require('ol.style.Rule'); +goog.require('ol.style.SymbolizerLiteral'); /** @@ -23,3 +26,46 @@ ol.style.Style = function(options) { this.rules_ = goog.isDef(options.rules) ? options.rules : []; }; + + +/** + * @param {ol.Feature} feature Feature. + * @return {Array.} Symbolizer literals for the + * feature. + */ +ol.style.Style.prototype.apply = function(feature) { + var rules = this.rules_, + literals = [], + rule, symbolizers; + for (var i = 0, ii = rules.length; i < ii; ++i) { + rule = rules[i]; + if (rule.applies(feature)) { + symbolizers = rule.getSymbolizers(); + for (var j = 0, jj = symbolizers.length; j < jj; ++j) { + literals.push(symbolizers[j].createLiteral(feature)); + } + } + } + return literals; +}; + + +/** + * @param {ol.Feature} feature Feature. + * @return {Array.} Default symbolizer literals for + * the feature. + */ +ol.style.Style.applyDefaultStyle = function(feature) { + var type = feature.getGeometry().getType(); + if (type === ol.geom.GeometryType.POINT || + type === ol.geom.GeometryType.MULTIPOINT) { + return [ol.style.ShapeDefaults]; + } else if (type === ol.geom.GeometryType.LINESTRING || + type === ol.geom.GeometryType.MULTILINESTRING) { + return [ol.style.LineDefaults]; + } else if (type === ol.geom.GeometryType.LINEARRING || + type === ol.geom.GeometryType.POLYGON || + type === ol.geom.GeometryType.MULTIPOLYGON) { + return [ol.style.PolygonDefaults]; + } +}; diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js index 72bda71e16..36bec5fbdc 100644 --- a/src/ol/style/symbolizer.js +++ b/src/ol/style/symbolizer.js @@ -11,6 +11,14 @@ goog.require('ol.Feature'); ol.style.SymbolizerLiteral = function() {}; +/** + * @param {ol.style.SymbolizerLiteral} symbolizerLiteral Symbolizer literal to + * compare to. + * @return {boolean} Is the passed symbolizer literal equal to this instance? + */ +ol.style.SymbolizerLiteral.prototype.equals = goog.abstractMethod; + + /** * @constructor