From 499ba4ac8f23e49b00596c02b4fd813872f2400f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 09:17:21 -0700 Subject: [PATCH 01/16] Add a style option for vector layers This can be a single ol.style.Style, and array of styles, or a style function. --- src/objectliterals.jsdoc | 2 +- src/ol/layer/vectorlayer.js | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index e26100a3b2..27d384f189 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -532,7 +532,7 @@ * @property {number|undefined} opacity Opacity. 0-1. Default is `1`. * @property {number|undefined} saturation Saturation. * @property {ol.source.Vector} source Source. - * @property {ol.feature.StyleFunction|undefined} styleFunction Style function. + * @property {ol.style.Style|Array.|ol.feature.StyleFunction|undefined} style Layer style. * @property {boolean|undefined} visible Visibility. Default is `true` (visible). * @todo stability experimental */ diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index e9bd4110d3..39f5b0eb1e 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,7 +1,9 @@ goog.provide('ol.layer.Vector'); +goog.require('goog.asserts'); goog.require('ol.feature'); goog.require('ol.layer.Layer'); +goog.require('ol.style.Style'); /** @@ -27,9 +29,32 @@ ol.layer.Vector = function(opt_options) { goog.base(this, /** @type {olx.layer.LayerOptions} */ (options)); - // FIXME veryify this - if (goog.isDef(options.styleFunction)) { - this.setStyleFunction(options.styleFunction); + if (goog.isDef(options.style)) { + + /** + * @type {ol.feature.StyleFunction} + */ + var styleFunction; + + if (goog.isFunction(options.style)) { + styleFunction = /** @type {ol.feature.StyleFunction} */ (options.style); + } else { + /** + * @type {Array.} + */ + var styles; + if (goog.isArray(options.style)) { + styles = options.style; + } else { + goog.asserts.assertInstanceof(options.style, ol.style.Style); + styles = [options.style]; + } + styleFunction = function(feature, resolution) { + return styles; + }; + } + + this.setStyleFunction(styleFunction); } }; From 5c21f24df5fd25bee3c8325901a2c3b6c1f4489f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 09:33:29 -0700 Subject: [PATCH 02/16] Add tests for vector layer --- test/spec/ol/layer/vectorlayer.test.js | 54 ++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/spec/ol/layer/vectorlayer.test.js diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js new file mode 100644 index 0000000000..3d25c62ac6 --- /dev/null +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -0,0 +1,54 @@ +goog.provide('ol.test.layer.Vector'); + +describe('ol.layer.Vector', function() { + + describe('constructor', function() { + var source = new ol.source.Vector(); + var style = new ol.style.Style(); + + it('creates a new layer', function() { + var layer = new ol.layer.Vector({source: source}); + expect(layer).to.be.a(ol.layer.Vector); + expect(layer).to.be.a(ol.layer.Layer); + }); + + it('accepts a style option with a single style', function() { + var layer = new ol.layer.Vector({ + source: source, + style: style + }); + + var styleFunction = layer.getStyleFunction(); + expect(styleFunction()).to.eql([style]); + }); + + it('accepts a style option with an array of styles', function() { + var layer = new ol.layer.Vector({ + source: source, + style: [style] + }); + + var styleFunction = layer.getStyleFunction(); + expect(styleFunction()).to.eql([style]); + }); + + it('accepts a style option with a style function', function() { + var layer = new ol.layer.Vector({ + source: source, + style: function(feature, resolution) { + return [style]; + } + }); + + var styleFunction = layer.getStyleFunction(); + expect(styleFunction()).to.eql([style]); + }); + + }); + +}); + +goog.require('ol.layer.Layer'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Style'); From 1072f6dfa7efdcd8e1b8186eb792eb69ee6f43dc Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 10:43:19 -0700 Subject: [PATCH 03/16] Use style option in vector layer examples Providing a styleFunction option still works because the layer property is still named styleFunction. --- examples/drag-and-drop.js | 2 +- examples/draw-features.js | 34 +++++++++++++++------------------- examples/geojson.js | 2 +- examples/google-map.js | 23 +++++++++-------------- examples/gpx.js | 2 +- examples/igc.js | 2 +- examples/kml-earthquakes.html | 2 +- examples/kml-earthquakes.js | 2 +- examples/kml-timezones.js | 2 +- examples/modify-features.js | 2 +- examples/rtree.js | 6 ++---- examples/select-features.js | 4 +--- examples/synthetic-lines.js | 16 ++++++---------- examples/synthetic-points.js | 2 +- examples/topojson.js | 2 +- examples/vector-layer.js | 2 +- 16 files changed, 44 insertions(+), 61 deletions(-) diff --git a/examples/drag-and-drop.js b/examples/drag-and-drop.js index 4e39605838..aa3e6f2fa9 100644 --- a/examples/drag-and-drop.js +++ b/examples/drag-and-drop.js @@ -118,7 +118,7 @@ dragAndDropInteraction.on('addfeatures', function(event) { }); map.getLayers().push(new ol.layer.Vector({ source: vectorSource, - styleFunction: styleFunction + style: styleFunction })); var view2D = map.getView().getView2D(); view2D.fitExtent(vectorSource.getExtent(), map.getSize()); diff --git a/examples/draw-features.js b/examples/draw-features.js index 19415de98f..88a09399b5 100644 --- a/examples/draw-features.js +++ b/examples/draw-features.js @@ -15,29 +15,25 @@ var raster = new ol.layer.Tile({ source: new ol.source.MapQuest({layer: 'sat'}) }); -var styleArray = [new ol.style.Style({ - fill: new ol.style.Fill({ - color: 'rgba(255, 255, 255, 0.2)' - }), - stroke: new ol.style.Stroke({ - color: '#ffcc33', - width: 2 - }), - image: new ol.style.Circle({ - radius: 7, - fill: new ol.style.Fill({ - color: '#ffcc33' - }) - }) -})]; - var source = new ol.source.Vector(); var vector = new ol.layer.Vector({ source: source, - styleFunction: function(feature, resolution) { - return styleArray; - } + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }), + stroke: new ol.style.Stroke({ + color: '#ffcc33', + width: 2 + }), + image: new ol.style.Circle({ + radius: 7, + fill: new ol.style.Fill({ + color: '#ffcc33' + }) + }) + }) }); var map = new ol.Map({ diff --git a/examples/geojson.js b/examples/geojson.js index f8146d1000..9889d08d3e 100644 --- a/examples/geojson.js +++ b/examples/geojson.js @@ -177,7 +177,7 @@ vectorSource.addFeature(new ol.Feature(new ol.geom.Circle([5e6, 7e6], 1e6))); var vectorLayer = new ol.layer.Vector({ source: vectorSource, - styleFunction: styleFunction + style: styleFunction }); var map = new ol.Map({ diff --git a/examples/google-map.js b/examples/google-map.js index 83c18c7016..f6dd0a6994 100644 --- a/examples/google-map.js +++ b/examples/google-map.js @@ -34,20 +34,15 @@ google.maps.event.addListenerOnce(gmap, 'tilesloaded', function() { url: 'data/geojson/countries.geojson', projection: 'EPSG:3857' }), - styleFunction: (function() { - var styleArray = [new ol.style.Style({ - fill: new ol.style.Fill({ - color: 'rgba(255, 255, 255, 0.6)' - }), - stroke: new ol.style.Stroke({ - color: '#319FD3', - width: 1 - }) - })]; - return function(feature, resolution) { - return styleArray; - }; - }()) + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 255, 255, 0.6)' + }), + stroke: new ol.style.Stroke({ + color: '#319FD3', + width: 1 + }) + }) }); var center = gmap.getCenter(); diff --git a/examples/gpx.js b/examples/gpx.js index 438811d93c..b0f1c9294d 100644 --- a/examples/gpx.js +++ b/examples/gpx.js @@ -48,7 +48,7 @@ var vector = new ol.layer.Vector({ projection: 'EPSG:3857', url: 'data/gpx/fells_loop.gpx' }), - styleFunction: function(feature, resolution) { + style: function(feature, resolution) { return style[feature.getGeometry().getType()]; } }); diff --git a/examples/igc.js b/examples/igc.js index 7ab9d9fa49..a0fd613f60 100644 --- a/examples/igc.js +++ b/examples/igc.js @@ -63,7 +63,7 @@ var map = new ol.Map({ }), new ol.layer.Vector({ source: vectorSource, - styleFunction: styleFunction + style: styleFunction }) ], renderer: 'canvas', diff --git a/examples/kml-earthquakes.html b/examples/kml-earthquakes.html index 4d3f2386c9..73951c9226 100644 --- a/examples/kml-earthquakes.html +++ b/examples/kml-earthquakes.html @@ -55,7 +55,7 @@

Demonstrates the use of a Shape symbolizer to render earthquake locations.

- This example parses a KML file and renders the features as a vector layer. The layer is given a styleFunction that renders earthquake locations with a size relative to their magnitude. + This example parses a KML file and renders the features as a vector layer. The layer is given a style that renders earthquake locations with a size relative to their magnitude.

See the kml-earthquakes.js source to see how this is done.

diff --git a/examples/kml-earthquakes.js b/examples/kml-earthquakes.js index d4edebcc3b..4bbc80716f 100644 --- a/examples/kml-earthquakes.js +++ b/examples/kml-earthquakes.js @@ -42,7 +42,7 @@ var vector = new ol.layer.Vector({ projection: 'EPSG:3857', url: 'data/kml/2012_Earthquakes_Mag5.kml' }), - styleFunction: styleFunction + style: styleFunction }); var raster = new ol.layer.Tile({ diff --git a/examples/kml-timezones.js b/examples/kml-timezones.js index 6211b4957d..2973b637d6 100644 --- a/examples/kml-timezones.js +++ b/examples/kml-timezones.js @@ -49,7 +49,7 @@ var vector = new ol.layer.Vector({ projection: 'EPSG:3857', url: 'data/kml/timezones.kml' }), - styleFunction: styleFunction + style: styleFunction }); var raster = new ol.layer.Tile({ diff --git a/examples/modify-features.js b/examples/modify-features.js index 2ed0e6aa86..5d9fa52e32 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -161,7 +161,7 @@ var vectorSource = new ol.source.GeoJSON( var vectorLayer = new ol.layer.Vector({ source: vectorSource, - styleFunction: styleFunction + style: styleFunction }); var overlayStyle = (function() { diff --git a/examples/rtree.js b/examples/rtree.js index 7a9f184753..43e8a3d10c 100644 --- a/examples/rtree.js +++ b/examples/rtree.js @@ -83,16 +83,14 @@ for (i = 0, ii = extentsByDepth.length; i < ii; ++i) { var vector = new ol.layer.Vector({ source: vectorSource, - styleFunction: function(feature, resolution) { - return styleArray; - } + style: styleArray }); var rtree = new ol.layer.Vector({ source: new ol.source.Vector({ features: rtreeExtentFeatures }), - styleFunction: function(feature, resolution) { + style: function(feature, resolution) { return feature.get('styleArray'); } }); diff --git a/examples/select-features.js b/examples/select-features.js index d9d5a0ca37..27b76c63a4 100644 --- a/examples/select-features.js +++ b/examples/select-features.js @@ -35,9 +35,7 @@ var vector = new ol.layer.Vector({ projection: 'EPSG:3857', url: 'data/geojson/countries.geojson' }), - styleFunction: function(feature, resolution) { - return unselectedStyle; - } + style: unselectedStyle }); var select = new ol.interaction.Select({ diff --git a/examples/synthetic-lines.js b/examples/synthetic-lines.js index 9a5dd9e4ee..789a7c2571 100644 --- a/examples/synthetic-lines.js +++ b/examples/synthetic-lines.js @@ -36,20 +36,16 @@ for (i = 0; i < count; ++i) { startPoint = endPoint; } -var styleArray = [new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: '#666666', - width: 1 - }) -})]; - var vector = new ol.layer.Vector({ source: new ol.source.Vector({ features: features }), - styleFunction: function(feature, resolution) { - return styleArray; - } + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#666666', + width: 1 + }) + }) }); var view = new ol.View2D({ diff --git a/examples/synthetic-points.js b/examples/synthetic-points.js index 7ebd447c4b..7246fbfda1 100644 --- a/examples/synthetic-points.js +++ b/examples/synthetic-points.js @@ -46,7 +46,7 @@ var vectorSource = new ol.source.Vector({ }); var vector = new ol.layer.Vector({ source: vectorSource, - styleFunction: function(feature, resolution) { + style: function(feature, resolution) { return styles[feature.get('size')]; } }); diff --git a/examples/topojson.js b/examples/topojson.js index 0cdde09c0c..d9eb762642 100644 --- a/examples/topojson.js +++ b/examples/topojson.js @@ -30,7 +30,7 @@ var vector = new ol.layer.Vector({ projection: 'EPSG:3857', url: 'data/topojson/world-110m.json' }), - styleFunction: function(feature, resolution) { + style: function(feature, resolution) { // don't want to render the full world polygon, which repeats all countries return feature.getId() !== undefined ? styleArray : null; } diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 8b36ced3a3..979b8732fb 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -17,7 +17,7 @@ var vectorLayer = new ol.layer.Vector({ projection: 'EPSG:3857', url: 'data/geojson/countries.geojson' }), - styleFunction: function(feature, resolution) { + style: function(feature, resolution) { var text = resolution < 5000 ? feature.get('name') : ''; if (!styleCache[text]) { styleCache[text] = [new ol.style.Style({ From b309c44020f9d670177259fe2d56ef94863cfd7a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 12:32:42 -0700 Subject: [PATCH 04/16] Give vector image source a style option --- examples/drag-and-drop-image-vector.js | 2 +- examples/image-vector-layer.js | 22 ++++++++---------- src/objectliterals.jsdoc | 4 ++-- src/ol/source/imagevectorsource.js | 31 ++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/examples/drag-and-drop-image-vector.js b/examples/drag-and-drop-image-vector.js index 6c65270335..7aa470eea9 100644 --- a/examples/drag-and-drop-image-vector.js +++ b/examples/drag-and-drop-image-vector.js @@ -121,7 +121,7 @@ dragAndDropInteraction.on('addfeatures', function(event) { map.getLayers().push(new ol.layer.Image({ source: new ol.source.ImageVector({ source: vectorSource, - styleFunction: styleFunction + style: styleFunction }) })); var view2D = map.getView().getView2D(); diff --git a/examples/image-vector-layer.js b/examples/image-vector-layer.js index bc38cedc5e..342a5c9ac8 100644 --- a/examples/image-vector-layer.js +++ b/examples/image-vector-layer.js @@ -11,16 +11,6 @@ goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); -var styleArray = [new ol.style.Style({ - fill: new ol.style.Fill({ - color: 'rgba(255, 255, 255, 0.6)' - }), - stroke: new ol.style.Stroke({ - color: '#319FD3', - width: 1 - }) -})]; - var map = new ol.Map({ layers: [ new ol.layer.Tile({ @@ -32,9 +22,15 @@ var map = new ol.Map({ projection: 'EPSG:3857', url: 'data/geojson/countries.geojson' }), - styleFunction: function(feature, resolution) { - return styleArray; - } + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 255, 255, 0.6)' + }), + stroke: new ol.style.Stroke({ + color: '#319FD3', + width: 1 + }) + }) }) }) ], diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 27d384f189..0b5d8ac1ad 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -736,8 +736,8 @@ * new canvases will be created for these resolutions only. * @property {ol.source.Vector} source The vector source from which the vector * features drawn in canvas elements are read. - * @property {ol.feature.StyleFunction|undefined} styleFunction Style function - * providing the styles to use when rendering features to the canvas. + * @property {ol.style.Style|Array.|ol.feature.StyleFunction|undefined} style + * Style to use when rendering features to the canvas. */ /** diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index 97d84e32e6..d362fec8f4 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -12,6 +12,7 @@ goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.vector'); goog.require('ol.source.ImageCanvas'); goog.require('ol.source.Vector'); +goog.require('ol.style.Style'); goog.require('ol.vec.Mat4'); @@ -40,12 +41,38 @@ ol.source.ImageVector = function(options) { */ this.source_ = options.source; + + /** + * @type {ol.feature.StyleFunction} + */ + var styleFunction; + + if (goog.isDef(options.style)) { + if (goog.isFunction(options.style)) { + styleFunction = /** @type {ol.feature.StyleFunction} */ (options.style); + } else { + /** + * @type {Array.} + */ + var styles; + if (goog.isArray(options.style)) { + styles = options.style; + } else { + goog.asserts.assertInstanceof(options.style, ol.style.Style); + styles = [options.style]; + } + styleFunction = function(feature, resolution) { + return styles; + }; + } + } + /** * @private * @type {!ol.feature.StyleFunction} */ - this.styleFunction_ = goog.isDef(options.styleFunction) ? - options.styleFunction : ol.feature.defaultStyleFunction; + this.styleFunction_ = goog.isDef(styleFunction) ? + styleFunction : ol.feature.defaultStyleFunction; /** * @private From 6abb691224c20c85d5df3fd7a35ffbb8f12fdd81 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 12:52:41 -0700 Subject: [PATCH 05/16] Give feature overlays a style option --- examples/image-vector-layer.js | 22 +++++++++------------- examples/modify-features.js | 2 +- examples/select-features.js | 32 +++++++++++++------------------- examples/vector-layer.js | 2 +- src/objectliterals.jsdoc | 2 +- src/ol/featureoverlay.js | 31 ++++++++++++++++++++++++++----- 6 files changed, 51 insertions(+), 40 deletions(-) diff --git a/examples/image-vector-layer.js b/examples/image-vector-layer.js index 342a5c9ac8..83bf47477b 100644 --- a/examples/image-vector-layer.js +++ b/examples/image-vector-layer.js @@ -42,21 +42,17 @@ var map = new ol.Map({ }) }); -var highlightStyleArray = [new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: '#f00', - width: 1 - }), - fill: new ol.style.Fill({ - color: 'rgba(255,0,0,0.1)' - }) -})]; - var featureOverlay = new ol.FeatureOverlay({ map: map, - styleFunction: function(feature, resolution) { - return highlightStyleArray; - } + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#f00', + width: 1 + }), + fill: new ol.style.Fill({ + color: 'rgba(255,0,0,0.1)' + }) + }) }); var highlight; diff --git a/examples/modify-features.js b/examples/modify-features.js index 5d9fa52e32..ed8bb3714c 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -228,7 +228,7 @@ var overlayStyle = (function() { })(); var overlay = new ol.FeatureOverlay({ - styleFunction: overlayStyle + style: overlayStyle }); var modify = new ol.interaction.Modify({ featureOverlay: overlay }); diff --git a/examples/select-features.js b/examples/select-features.js index 27b76c63a4..def9194a29 100644 --- a/examples/select-features.js +++ b/examples/select-features.js @@ -15,34 +15,28 @@ var raster = new ol.layer.Tile({ source: new ol.source.MapQuest({layer: 'sat'}) }); -var unselectedStyle = [new ol.style.Style({ - fill: new ol.style.Fill({ - color: 'rgba(255,255,255,0.25)' - }), - stroke: new ol.style.Stroke({ - color: '#6666ff' - }) -})]; - -var selectedStyle = [new ol.style.Style({ - fill: new ol.style.Fill({ - color: 'rgba(255,255,255,0.5)' - }) -})]; - var vector = new ol.layer.Vector({ source: new ol.source.GeoJSON({ projection: 'EPSG:3857', url: 'data/geojson/countries.geojson' }), - style: unselectedStyle + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255,255,255,0.25)' + }), + stroke: new ol.style.Stroke({ + color: '#6666ff' + }) + }) }); var select = new ol.interaction.Select({ featureOverlay: new ol.FeatureOverlay({ - styleFunction: function(feature, resolution) { - return selectedStyle; - } + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255,255,255,0.5)' + }) + }) }) }); diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 979b8732fb..ca33ead397 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -64,7 +64,7 @@ var highlightStyleCache = {}; var featureOverlay = new ol.FeatureOverlay({ map: map, - styleFunction: function(feature, resolution) { + style: function(feature, resolution) { var text = resolution < 5000 ? feature.get('name') : ''; if (!highlightStyleCache[text]) { highlightStyleCache[text] = [new ol.style.Style({ diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 0b5d8ac1ad..7f1216abf4 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -566,7 +566,7 @@ * @typedef {Object} olx.FeatureOverlayOptions * @property {Array.|ol.Collection|undefined} features Features. * @property {ol.Map|undefined} map Map. - * @property {ol.feature.StyleFunction|undefined} styleFunction Style function. + * @property {ol.style.Style|Array.|ol.feature.StyleFunction|undefined} style Feature style. */ /** diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index ccc5f68b45..e5d2784af0 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -10,6 +10,7 @@ goog.require('ol.CollectionEventType'); goog.require('ol.Feature'); goog.require('ol.feature'); goog.require('ol.render.EventType'); +goog.require('ol.style.Style'); @@ -52,11 +53,35 @@ ol.FeatureOverlay = function(opt_options) { */ this.postComposeListenerKey_ = null; + /** + * @type {ol.feature.StyleFunction} + */ + var styleFunction; + if (goog.isDef(options.style)) { + if (goog.isFunction(options.style)) { + styleFunction = /** @type {ol.feature.StyleFunction} */ (options.style); + } else { + /** + * @type {Array.} + */ + var styles; + if (goog.isArray(options.style)) { + styles = options.style; + } else { + goog.asserts.assertInstanceof(options.style, ol.style.Style); + styles = [options.style]; + } + styleFunction = function(feature, resolution) { + return styles; + }; + } + } + /** * @private * @type {ol.feature.StyleFunction|undefined} */ - this.styleFunction_ = undefined; + this.styleFunction_ = styleFunction; if (goog.isDef(options.features)) { if (goog.isArray(options.features)) { @@ -69,10 +94,6 @@ ol.FeatureOverlay = function(opt_options) { this.setFeatures(new ol.Collection()); } - if (goog.isDef(options.styleFunction)) { - this.setStyleFunction(options.styleFunction); - } - if (goog.isDef(options.map)) { this.setMap(options.map); } From c64c24d3dc94f5d1ad093c26c891ed702ed8e707 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 13:25:21 -0700 Subject: [PATCH 06/16] Common code for creating a style function --- src/ol/feature.js | 35 ++++++++++++++++++++++++++++++ src/ol/featureoverlay.js | 28 ++---------------------- src/ol/layer/vectorlayer.js | 28 +----------------------- src/ol/source/imagevectorsource.js | 32 +++------------------------ test/spec/ol/feature.test.js | 31 ++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 82 deletions(-) diff --git a/src/ol/feature.js b/src/ol/feature.js index 54439b1b8c..77d35aa333 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -238,3 +238,38 @@ ol.feature.defaultStyleFunction = function(feature, resolution) { } return featureStyleFunction.call(feature, resolution); }; + + +/** + * Convert the provided object into a style function. Functions passed through + * unchanged. Arrays of ol.style.Style or single style objects wrapped in a + * new style function. + * @param {ol.feature.StyleFunction|Array.|ol.style.Style} obj + * A style function, a single style, or an array of styles. + * @return {ol.feature.StyleFunction} A style function. + */ +ol.feature.createStyleFunction = function(obj) { + /** + * @type {ol.feature.StyleFunction} + */ + var styleFunction; + + if (goog.isFunction(obj)) { + styleFunction = /** @type {ol.feature.StyleFunction} */ (obj); + } else { + /** + * @type {Array.} + */ + var styles; + if (goog.isArray(obj)) { + styles = obj; + } else { + goog.asserts.assertInstanceof(obj, ol.style.Style); + styles = [obj]; + } + styleFunction = function(feature, resolution) { + return styles; + }; + } + return styleFunction; +}; diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index e5d2784af0..50987b3a4b 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -10,7 +10,6 @@ goog.require('ol.CollectionEventType'); goog.require('ol.Feature'); goog.require('ol.feature'); goog.require('ol.render.EventType'); -goog.require('ol.style.Style'); @@ -53,35 +52,12 @@ ol.FeatureOverlay = function(opt_options) { */ this.postComposeListenerKey_ = null; - /** - * @type {ol.feature.StyleFunction} - */ - var styleFunction; - if (goog.isDef(options.style)) { - if (goog.isFunction(options.style)) { - styleFunction = /** @type {ol.feature.StyleFunction} */ (options.style); - } else { - /** - * @type {Array.} - */ - var styles; - if (goog.isArray(options.style)) { - styles = options.style; - } else { - goog.asserts.assertInstanceof(options.style, ol.style.Style); - styles = [options.style]; - } - styleFunction = function(feature, resolution) { - return styles; - }; - } - } - /** * @private * @type {ol.feature.StyleFunction|undefined} */ - this.styleFunction_ = styleFunction; + this.styleFunction_ = goog.isDef(options.style) ? + ol.feature.createStyleFunction(options.style) : undefined; if (goog.isDef(options.features)) { if (goog.isArray(options.features)) { diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 39f5b0eb1e..f27775b3a7 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,9 +1,7 @@ goog.provide('ol.layer.Vector'); -goog.require('goog.asserts'); goog.require('ol.feature'); goog.require('ol.layer.Layer'); -goog.require('ol.style.Style'); /** @@ -30,31 +28,7 @@ ol.layer.Vector = function(opt_options) { goog.base(this, /** @type {olx.layer.LayerOptions} */ (options)); if (goog.isDef(options.style)) { - - /** - * @type {ol.feature.StyleFunction} - */ - var styleFunction; - - if (goog.isFunction(options.style)) { - styleFunction = /** @type {ol.feature.StyleFunction} */ (options.style); - } else { - /** - * @type {Array.} - */ - var styles; - if (goog.isArray(options.style)) { - styles = options.style; - } else { - goog.asserts.assertInstanceof(options.style, ol.style.Style); - styles = [options.style]; - } - styleFunction = function(feature, resolution) { - return styles; - }; - } - - this.setStyleFunction(styleFunction); + this.setStyleFunction(ol.feature.createStyleFunction(options.style)); } }; diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index d362fec8f4..d6050ac8d4 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -12,7 +12,6 @@ goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.vector'); goog.require('ol.source.ImageCanvas'); goog.require('ol.source.Vector'); -goog.require('ol.style.Style'); goog.require('ol.vec.Mat4'); @@ -41,38 +40,13 @@ ol.source.ImageVector = function(options) { */ this.source_ = options.source; - - /** - * @type {ol.feature.StyleFunction} - */ - var styleFunction; - - if (goog.isDef(options.style)) { - if (goog.isFunction(options.style)) { - styleFunction = /** @type {ol.feature.StyleFunction} */ (options.style); - } else { - /** - * @type {Array.} - */ - var styles; - if (goog.isArray(options.style)) { - styles = options.style; - } else { - goog.asserts.assertInstanceof(options.style, ol.style.Style); - styles = [options.style]; - } - styleFunction = function(feature, resolution) { - return styles; - }; - } - } - /** * @private * @type {!ol.feature.StyleFunction} */ - this.styleFunction_ = goog.isDef(styleFunction) ? - styleFunction : ol.feature.defaultStyleFunction; + this.styleFunction_ = goog.isDef(options.style) ? + ol.feature.createStyleFunction(options.style) : + ol.feature.defaultStyleFunction; /** * @private diff --git a/test/spec/ol/feature.test.js b/test/spec/ol/feature.test.js index afbf896ed9..ece506aad4 100644 --- a/test/spec/ol/feature.test.js +++ b/test/spec/ol/feature.test.js @@ -209,8 +209,39 @@ describe('ol.Feature', function() { }); +describe('ol.feature.createStyleFunction()', function() { + var style = new ol.style.Style(); + + it('creates a style function from a single style', function() { + var styleFunction = ol.feature.createStyleFunction(style); + expect(styleFunction()).to.eql([style]); + }); + + it('creates a style function from an array of styles', function() { + var styleFunction = ol.feature.createStyleFunction([style]); + expect(styleFunction()).to.eql([style]); + }); + + it('passes through a function', function() { + var original = function() { + return [style]; + }; + var styleFunction = ol.feature.createStyleFunction(original); + expect(styleFunction).to.be(original); + }); + + it('throws on (some) unexpected input', function() { + expect(function() { + ol.feature.createStyleFunction({bogus: 'input'}); + }).to.throwException(); + }); + +}); + goog.require('goog.events'); goog.require('goog.object'); goog.require('ol.Feature'); +goog.require('ol.feature'); goog.require('ol.geom.Point'); +goog.require('ol.style.Style'); From 76f6961f83fd4f0d296749808ad94fc4561e58e4 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 13:28:46 -0700 Subject: [PATCH 07/16] Delete style option before passing to base --- src/ol/layer/vectorlayer.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index f27775b3a7..3746ee63fa 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,5 +1,6 @@ goog.provide('ol.layer.Vector'); +goog.require('goog.object'); goog.require('ol.feature'); goog.require('ol.layer.Layer'); @@ -25,7 +26,11 @@ ol.layer.Vector = function(opt_options) { var options = goog.isDef(opt_options) ? opt_options : /** @type {olx.layer.VectorOptions} */ ({}); - goog.base(this, /** @type {olx.layer.LayerOptions} */ (options)); + var baseOptions = /** @type {olx.layer.LayerOptions} */ + (goog.object.clone(options)); + + delete baseOptions.style; + goog.base(this, baseOptions); if (goog.isDef(options.style)) { this.setStyleFunction(ol.feature.createStyleFunction(options.style)); From 10d5073732bd4c349e6691e94ef6dacf5834da85 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 13:59:55 -0700 Subject: [PATCH 08/16] Give the draw interaction a style option --- src/objectliterals.jsdoc | 3 ++- src/ol/interaction/drawinteraction.js | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 7f1216abf4..a639989c1b 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -380,7 +380,8 @@ * drawing finish (default is 12). * @property {ol.geom.GeometryType} type Drawing type ('Point', 'LineString', * 'Polygon', 'MultiPoint', 'MultiLineString', or 'MultiPolygon'). - * @property {ol.feature.StyleFunction|undefined} styleFunction Style function. + * @property {ol.style.Style|Array.|ol.feature.StyleFunction|undefined} style + * Style for sketch features. * @todo stability experimental */ diff --git a/src/ol/interaction/drawinteraction.js b/src/ol/interaction/drawinteraction.js index c2d19aded1..9a263bc38b 100644 --- a/src/ol/interaction/drawinteraction.js +++ b/src/ol/interaction/drawinteraction.js @@ -146,9 +146,11 @@ ol.interaction.Draw = function(options) { * @type {ol.FeatureOverlay} * @private */ - this.overlay_ = new ol.FeatureOverlay(); - this.overlay_.setStyleFunction(goog.isDef(options.styleFunction) ? - options.styleFunction : ol.interaction.Draw.getDefaultStyleFunction()); + this.overlay_ = new ol.FeatureOverlay({ + style: goog.isDef(options.style) ? + options.style : ol.interaction.Draw.getDefaultStyleFunction() + }); + }; goog.inherits(ol.interaction.Draw, ol.interaction.Interaction); From 620a38d3e98ce1c5eade0b76958e4fa101c09e6d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 16:35:54 -0700 Subject: [PATCH 09/16] Avoid clashing with user property names Features are records with any number of user set values. Separate from this, we rely on feature properties like the feature identifier and feature style. The two (user properties and our internal properties) should not be mixed. --- src/ol/feature.js | 41 ++++++++++------------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/src/ol/feature.js b/src/ol/feature.js index 77d35aa333..c2154c6974 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -10,14 +10,6 @@ goog.require('ol.geom.Geometry'); goog.require('ol.style.Style'); -/** - * @enum {string} - */ -ol.FeatureProperty = { - STYLE_FUNCTION: 'styleFunction' -}; - - /** * @constructor @@ -42,6 +34,12 @@ ol.Feature = function(opt_geometryOrValues) { */ this.geometryName_ = 'geometry'; + /** + * @private + * @type {ol.feature.FeatureStyleFunction|undefined} + */ + this.styleFunction_; + /** * @private * @type {goog.events.Key} @@ -51,9 +49,6 @@ ol.Feature = function(opt_geometryOrValues) { goog.events.listen( this, ol.Object.getChangeEventType(this.geometryName_), this.handleGeometryChanged_, false, this); - goog.events.listen( - this, ol.Object.getChangeEventType(ol.FeatureProperty.STYLE_FUNCTION), - this.handleStyleFunctionChange_, false, this); if (goog.isDefAndNotNull(opt_geometryOrValues)) { if (opt_geometryOrValues instanceof ol.geom.Geometry) { @@ -108,13 +103,8 @@ ol.Feature.prototype.getGeometryName = function() { * @todo stability experimental */ ol.Feature.prototype.getStyleFunction = function() { - return /** @type {ol.feature.FeatureStyleFunction|undefined} */ ( - this.get(ol.FeatureProperty.STYLE_FUNCTION)); + return this.styleFunction_; }; -goog.exportProperty( - ol.Feature.prototype, - 'getStyleFunction', - ol.Feature.prototype.getStyleFunction); /** @@ -142,14 +132,6 @@ ol.Feature.prototype.handleGeometryChanged_ = function() { }; -/** - * @private - */ -ol.Feature.prototype.handleStyleFunctionChange_ = function() { - this.dispatchChangeEvent(); -}; - - /** * @param {ol.geom.Geometry|undefined} geometry Geometry. * @todo stability experimental @@ -165,16 +147,13 @@ goog.exportProperty( /** * @param {ol.feature.FeatureStyleFunction|undefined} styleFunction Style - * function + * function. * @todo stability experimental */ ol.Feature.prototype.setStyleFunction = function(styleFunction) { - this.set(ol.FeatureProperty.STYLE_FUNCTION, styleFunction); + this.styleFunction_ = styleFunction; + this.dispatchChangeEvent(); }; -goog.exportProperty( - ol.Feature.prototype, - 'setStyleFunction', - ol.Feature.prototype.setStyleFunction); /** From a185fc963d152a29a3ecd83cb9cd03dd9882c6f5 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 7 Feb 2014 17:03:39 -0700 Subject: [PATCH 10/16] Tests for get/setStyleFunction It would be nice to also test the following: it('does not return user set property with the same name', function() { var feature = new ol.Feature({ whatever: 'some value', styleFunction: 'another value' }); expect(feature.getStyleFunction()).to.be(undefined); }); Unfortunately, in uncompiled code (or if we export `setStyleFunction`) this does not work. Same goes for user set `id` properties (this will set our internal `id_` property). See #1672. --- test/spec/ol/feature.test.js | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/spec/ol/feature.test.js b/test/spec/ol/feature.test.js index ece506aad4..5a07558349 100644 --- a/test/spec/ol/feature.test.js +++ b/test/spec/ol/feature.test.js @@ -207,6 +207,59 @@ describe('ol.Feature', function() { }); + describe('#getStyleFunction()', function() { + + var styleFunction = function(feature, resolution) { + return null; + }; + + it('returns undefined after construction', function() { + var feature = new ol.Feature(); + expect(feature.getStyleFunction()).to.be(undefined); + }); + + it('returns the function passed to setStyleFunction', function() { + var feature = new ol.Feature(); + feature.setStyleFunction(styleFunction); + expect(feature.getStyleFunction()).to.be(styleFunction); + }); + + it('does not get confused with user "styleFunction" property', function() { + var feature = new ol.Feature(); + feature.set('styleFunction', 'foo'); + expect(feature.getStyleFunction()).to.be(undefined); + }); + + // TODO: also assert that 'styleFunction' passed to the constructor is + // not confused with the internal 'styleFunction_'. + // See https://github.com/openlayers/ol3/issues/1672 + + }); + + describe('#setStyleFunction()', function() { + + var styleFunction = function(feature, resolution) { + return null; + }; + + it('sets the style function', function() { + var feature = new ol.Feature(); + feature.setStyleFunction(styleFunction); + expect(feature.getStyleFunction()).to.be(styleFunction); + }); + + it('dispatches a change event', function(done) { + var feature = new ol.Feature(); + feature.on('change', function() { + done(); + }); + feature.setStyleFunction(styleFunction); + }); + + }); + + + }); describe('ol.feature.createStyleFunction()', function() { From a1f714f7dce00d225cf7338fb5b880ec93a90b04 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 12 Feb 2014 16:00:25 -0700 Subject: [PATCH 11/16] Add setStyle and getStyle methods to ol.layer.Vector The setStyle method accepts a single style, an array of styles, or a style function. The getStyle method returns what was set. Internally, we use the getStyleFunction method which always returns a function. When calling setStyle, a change event is dispatched (fixes #1671). --- src/ol/layer/vectorlayer.js | 51 ++++++++++++++------- test/spec/ol/layer/vectorlayer.test.js | 63 ++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 17 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 3746ee63fa..291c466656 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -9,8 +9,7 @@ goog.require('ol.layer.Layer'); * @enum {string} */ ol.layer.VectorProperty = { - RENDER_GEOMETRY_FUNCTIONS: 'renderGeometryFunctions', - STYLE_FUNCTION: 'styleFunction' + RENDER_GEOMETRY_FUNCTIONS: 'renderGeometryFunctions' }; @@ -32,8 +31,22 @@ ol.layer.Vector = function(opt_options) { delete baseOptions.style; goog.base(this, baseOptions); + /** + * User provided style. + * @type {ol.style.Style|Array.|ol.feature.StyleFunction} + * @private + */ + this.style_ = null; + + /** + * Style function for use within the library. + * @type {ol.feature.StyleFunction} + * @private + */ + this.styleFunction_; + if (goog.isDef(options.style)) { - this.setStyleFunction(ol.feature.createStyleFunction(options.style)); + this.setStyle(options.style); } }; @@ -55,17 +68,22 @@ goog.exportProperty( /** - * @return {ol.feature.StyleFunction|undefined} Style function. + * @return {ol.style.Style|Array.|ol.feature.StyleFunction} + * Layer style. + * @todo stability experimental + */ +ol.layer.Vector.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * @return {ol.feature.StyleFunction} Layer style function. * @todo stability experimental */ ol.layer.Vector.prototype.getStyleFunction = function() { - return /** @type {ol.feature.StyleFunction|undefined} */ ( - this.get(ol.layer.VectorProperty.STYLE_FUNCTION)); + return this.styleFunction_; }; -goog.exportProperty( - ol.layer.Vector.prototype, - 'getStyleFunction', - ol.layer.Vector.prototype.getStyleFunction); /** @@ -88,13 +106,12 @@ goog.exportProperty( * If the styles are changed by setting a new style function or by changing the * value returned by the style function then `dispatchChangeEvent` should be * called on the layer for the layer to be refreshed on the screen. - * @param {ol.feature.StyleFunction|undefined} styleFunction Style function. + * @param {ol.style.Style|Array.|ol.feature.StyleFunction} style + * Layer style. * @todo stability experimental */ -ol.layer.Vector.prototype.setStyleFunction = function(styleFunction) { - this.set(ol.layer.VectorProperty.STYLE_FUNCTION, styleFunction); +ol.layer.Vector.prototype.setStyle = function(style) { + this.style_ = style; + this.styleFunction_ = ol.feature.createStyleFunction(style); + this.dispatchChangeEvent(); }; -goog.exportProperty( - ol.layer.Vector.prototype, - 'setStyleFunction', - ol.layer.Vector.prototype.setStyleFunction); diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index 3d25c62ac6..f00c97ba41 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -46,6 +46,69 @@ describe('ol.layer.Vector', function() { }); + describe('#setStyle()', function() { + + var source = new ol.source.Vector(); + var style = new ol.style.Style(); + + it('allows the style to be set after construction', function() { + var layer = new ol.layer.Vector({ + source: source + }); + + layer.setStyle(style); + expect(layer.getStyle()).to.be(style); + }); + + it('dispatches the change event', function(done) { + var layer = new ol.layer.Vector({ + source: source + }); + layer.on('change', function() { + done(); + }); + layer.setStyle(style); + }); + + it('updates the internal style function', function() { + var layer = new ol.layer.Vector({ + source: source + }); + expect(layer.getStyleFunction()).to.be(undefined); + layer.setStyle(style); + expect(layer.getStyleFunction()).to.be.a('function'); + }); + + }); + + describe('#getStyle()', function() { + + var source = new ol.source.Vector(); + var style = new ol.style.Style(); + + it('returns what is provided to setStyle', function() { + var layer = new ol.layer.Vector({ + source: source + }); + + expect(layer.getStyle()).to.be(null); + + layer.setStyle(style); + expect(layer.getStyle()).to.be(style); + + layer.setStyle([style]); + expect(layer.getStyle()).to.eql([style]); + + var styleFunction = function(feature, resolution) { + return [style]; + }; + layer.setStyle(styleFunction); + expect(layer.getStyle()).to.be(styleFunction); + + }); + + }); + }); goog.require('ol.layer.Layer'); From 477c369f6ce911d51f2db1e0cd03b2aaa5f00cdc Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 12 Feb 2014 16:03:03 -0700 Subject: [PATCH 12/16] As with the vector layer, feature overlays have getStyle and setStyle --- src/ol/featureoverlay.exports | 2 +- src/ol/featureoverlay.js | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ol/featureoverlay.exports b/src/ol/featureoverlay.exports index 120ff391d2..cf40b7937a 100644 --- a/src/ol/featureoverlay.exports +++ b/src/ol/featureoverlay.exports @@ -3,5 +3,5 @@ @exportProperty ol.FeatureOverlay.prototype.getFeatures @exportProperty ol.FeatureOverlay.prototype.setFeatures @exportProperty ol.FeatureOverlay.prototype.setMap -@exportProperty ol.FeatureOverlay.prototype.setStyleFunction +@exportProperty ol.FeatureOverlay.prototype.setStyle @exportProperty ol.FeatureOverlay.prototype.removeFeature diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 50987b3a4b..6d9ac8863e 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -52,6 +52,12 @@ ol.FeatureOverlay = function(opt_options) { */ this.postComposeListenerKey_ = null; + /** + * @private + * @type {ol.style.Style|Array.|ol.feature.StyleFunction} + */ + this.style_ = null; + /** * @private * @type {ol.feature.StyleFunction|undefined} @@ -235,15 +241,26 @@ ol.FeatureOverlay.prototype.setMap = function(map) { /** - * @param {ol.feature.StyleFunction} styleFunction Style function. + * @param {ol.style.Style|Array.|ol.feature.StyleFunction} style + * Overlay style. * @todo stability experimental */ -ol.FeatureOverlay.prototype.setStyleFunction = function(styleFunction) { - this.styleFunction_ = styleFunction; +ol.FeatureOverlay.prototype.setStyle = function(style) { + this.style_ = style; + this.styleFunction_ = ol.feature.createStyleFunction(style); this.requestRenderFrame_(); }; +/** + * @return {ol.style.Style|Array.|ol.feature.StyleFunction} + * Overlay style. + */ +ol.FeatureOverlay.prototype.getStyle = function() { + return this.style_; +}; + + /** * @return {ol.feature.StyleFunction|undefined} Style function. */ From f4585331f202bf2187dff42c2b758c987d7e321b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 12 Feb 2014 16:49:53 -0700 Subject: [PATCH 13/16] Add setStyle and getStyle to ol.Feature As with vector layers and feature overlays, feature style can be set by calling setStyle. Calling getStyle returns what was passed to setStyle. Internally, we call getStyleFunction. --- examples/icon.js | 8 +-- src/ol/feature.exports | 2 + src/ol/feature.js | 63 +++++++++++++++++-- src/ol/format/kmlformat.js | 2 +- test/spec/ol/feature.test.js | 117 ++++++++++++++++++++++++++++++++--- 5 files changed, 172 insertions(+), 20 deletions(-) diff --git a/examples/icon.js b/examples/icon.js index 7b1f6af21b..bdb89645e5 100644 --- a/examples/icon.js +++ b/examples/icon.js @@ -18,19 +18,17 @@ var iconFeature = new ol.Feature({ rainfall: 500 }); -var styleArray = [new ol.style.Style({ +var iconStyle = new ol.style.Style({ image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({ anchor: [0.5, 46], anchorXUnits: 'fraction', anchorYUnits: 'pixels', src: 'data/icon.png' })) -})]; - -iconFeature.setStyleFunction(function(resolution) { - return styleArray; }); +iconFeature.setStyle(iconStyle); + var vectorSource = new ol.source.Vector({ features: [iconFeature] }); diff --git a/src/ol/feature.exports b/src/ol/feature.exports index 08e79a2c8c..fda1ed0384 100644 --- a/src/ol/feature.exports +++ b/src/ol/feature.exports @@ -3,3 +3,5 @@ @exportProperty ol.Feature.prototype.setGeometryName @exportProperty ol.Feature.prototype.getId @exportProperty ol.Feature.prototype.setId +@exportProperty ol.Feature.prototype.getStyle +@exportProperty ol.Feature.prototype.setStyle diff --git a/src/ol/feature.js b/src/ol/feature.js index c2154c6974..c9d1c09271 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -34,6 +34,14 @@ ol.Feature = function(opt_geometryOrValues) { */ this.geometryName_ = 'geometry'; + /** + * User provided style. + * @private + * @type {ol.style.Style|Array.| + * ol.feature.FeatureStyleFunction} + */ + this.style_ = null; + /** * @private * @type {ol.feature.FeatureStyleFunction|undefined} @@ -98,6 +106,16 @@ ol.Feature.prototype.getGeometryName = function() { }; +/** + * @return {ol.style.Style|Array.| + * ol.feature.FeatureStyleFunction} User provided style. + * @todo stability experimental + */ +ol.Feature.prototype.getStyle = function() { + return this.style_; +}; + + /** * @return {ol.feature.FeatureStyleFunction|undefined} Style function. * @todo stability experimental @@ -146,12 +164,13 @@ goog.exportProperty( /** - * @param {ol.feature.FeatureStyleFunction|undefined} styleFunction Style - * function. + * @param {ol.style.Style|Array.| + * ol.feature.FeatureStyleFunction} style Feature style. * @todo stability experimental */ -ol.Feature.prototype.setStyleFunction = function(styleFunction) { - this.styleFunction_ = styleFunction; +ol.Feature.prototype.setStyle = function(style) { + this.style_ = style; + this.styleFunction_ = ol.feature.createFeatureStyleFunction(style); this.dispatchChangeEvent(); }; @@ -219,6 +238,42 @@ ol.feature.defaultStyleFunction = function(feature, resolution) { }; +/** + * Convert the provided object into a feature style function. Functions passed + * through unchanged. Arrays of ol.style.Style or single style objects wrapped + * in a new feature style function. + * @param {ol.feature.FeatureStyleFunction|Array.| + * ol.style.Style} obj A feature style function, a single style, or an array + * of styles. + * @return {ol.feature.FeatureStyleFunction} A style function. + */ +ol.feature.createFeatureStyleFunction = function(obj) { + /** + * @type {ol.feature.FeatureStyleFunction} + */ + var styleFunction; + + if (goog.isFunction(obj)) { + styleFunction = /** @type {ol.feature.FeatureStyleFunction} */ (obj); + } else { + /** + * @type {Array.} + */ + var styles; + if (goog.isArray(obj)) { + styles = obj; + } else { + goog.asserts.assertInstanceof(obj, ol.style.Style); + styles = [obj]; + } + styleFunction = function(resolution) { + return styles; + }; + } + return styleFunction; +}; + + /** * Convert the provided object into a style function. Functions passed through * unchanged. Arrays of ol.style.Style or single style objects wrapped in a diff --git a/src/ol/format/kmlformat.js b/src/ol/format/kmlformat.js index 2b5548ad7f..8351b856dc 100644 --- a/src/ol/format/kmlformat.js +++ b/src/ol/format/kmlformat.js @@ -1448,7 +1448,7 @@ ol.format.KML.prototype.readPlacemark_ = function(node, objectStack) { } else { featureStyleFunction = ol.format.KML.makeFeatureStyleFunction_(style); } - feature.setStyleFunction(featureStyleFunction); + feature.setStyle(featureStyleFunction); return feature; }; diff --git a/test/spec/ol/feature.test.js b/test/spec/ol/feature.test.js index 5a07558349..afe29368ec 100644 --- a/test/spec/ol/feature.test.js +++ b/test/spec/ol/feature.test.js @@ -209,7 +209,7 @@ describe('ol.Feature', function() { describe('#getStyleFunction()', function() { - var styleFunction = function(feature, resolution) { + var styleFunction = function(resolution) { return null; }; @@ -218,9 +218,9 @@ describe('ol.Feature', function() { expect(feature.getStyleFunction()).to.be(undefined); }); - it('returns the function passed to setStyleFunction', function() { + it('returns the function passed to setStyle', function() { var feature = new ol.Feature(); - feature.setStyleFunction(styleFunction); + feature.setStyle(styleFunction); expect(feature.getStyleFunction()).to.be(styleFunction); }); @@ -230,21 +230,40 @@ describe('ol.Feature', function() { expect(feature.getStyleFunction()).to.be(undefined); }); - // TODO: also assert that 'styleFunction' passed to the constructor is - // not confused with the internal 'styleFunction_'. - // See https://github.com/openlayers/ol3/issues/1672 + it('does not get confused with "styleFunction" option', function() { + var feature = new ol.Feature({ + styleFunction: 'foo' + }); + expect(feature.getStyleFunction()).to.be(undefined); + }); }); - describe('#setStyleFunction()', function() { + describe('#setStyle()', function() { + + var style = new ol.style.Style(); var styleFunction = function(feature, resolution) { return null; }; - it('sets the style function', function() { + it('accepts a single style', function() { var feature = new ol.Feature(); - feature.setStyleFunction(styleFunction); + feature.setStyle(style); + var func = feature.getStyleFunction(); + expect(func()).to.eql([style]); + }); + + it('accepts an array of styles', function() { + var feature = new ol.Feature(); + feature.setStyle([style]); + var func = feature.getStyleFunction(); + expect(func()).to.eql([style]); + }); + + it('accepts a style function', function() { + var feature = new ol.Feature(); + feature.setStyle(styleFunction); expect(feature.getStyleFunction()).to.be(styleFunction); }); @@ -253,11 +272,60 @@ describe('ol.Feature', function() { feature.on('change', function() { done(); }); - feature.setStyleFunction(styleFunction); + feature.setStyle(style); }); }); + describe('#getStyle()', function() { + + var style = new ol.style.Style(); + + var styleFunction = function(resolution) { + return null; + }; + + it('returns what is passed to setStyle', function() { + var feature = new ol.Feature(); + + expect(feature.getStyle()).to.be(null); + + feature.setStyle(style); + expect(feature.getStyle()).to.be(style); + + feature.setStyle([style]); + expect(feature.getStyle()).to.eql([style]); + + feature.setStyle(styleFunction); + expect(feature.getStyle()).to.be(styleFunction); + + }); + + /** + * We should be able to make the assertion below, but the constructor + * calls setValues which calls setFoo when provided with 'foo'. This + * is different behavior than calling set('foo', 'bar'). + * See https://github.com/openlayers/ol3/issues/1672 + + + it('does not get confused with "style" option to constructor', function() { + var feature = new ol.Feature({ + style: 'foo' + }); + + expect(feature.getStyle()).to.be(null); + }); + + */ + + it('does not get confused with user set "style" property', function() { + var feature = new ol.Feature(); + feature.set('style', 'foo'); + + expect(feature.getStyle()).to.be(null); + }); + + }); }); @@ -291,6 +359,35 @@ describe('ol.feature.createStyleFunction()', function() { }); +describe('ol.feature.createFeatureStyleFunction()', function() { + var style = new ol.style.Style(); + + it('creates a feature style function from a single style', function() { + var styleFunction = ol.feature.createFeatureStyleFunction(style); + expect(styleFunction()).to.eql([style]); + }); + + it('creates a feature style function from an array of styles', function() { + var styleFunction = ol.feature.createFeatureStyleFunction([style]); + expect(styleFunction()).to.eql([style]); + }); + + it('passes through a function', function() { + var original = function() { + return [style]; + }; + var styleFunction = ol.feature.createFeatureStyleFunction(original); + expect(styleFunction).to.be(original); + }); + + it('throws on (some) unexpected input', function() { + expect(function() { + ol.feature.createFeatureStyleFunction({bogus: 'input'}); + }).to.throwException(); + }); + +}); + goog.require('goog.events'); goog.require('goog.object'); From 66d5d1d277cddd776fc6558f09586ebe02569420 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 12 Feb 2014 17:08:55 -0700 Subject: [PATCH 14/16] Export setStyle for vector layer --- src/ol/layer/vectorlayer.exports | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index 3dc18a2d27..ac7d597027 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1 +1,2 @@ @exportSymbol ol.layer.Vector +@exportProperty ol.layer.Vector.prototype.setStyle From 0ec5201f3a6bdf253776e94e188db5811e66f889 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 13 Feb 2014 10:37:10 -0700 Subject: [PATCH 15/16] Documentation and exports --- src/ol/feature.exports | 5 +++-- src/ol/featureoverlay.exports | 2 ++ src/ol/featureoverlay.js | 6 ++++++ src/ol/layer/vectorlayer.exports | 2 ++ src/ol/layer/vectorlayer.js | 10 ++++++---- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/ol/feature.exports b/src/ol/feature.exports index fda1ed0384..6c1f200ea6 100644 --- a/src/ol/feature.exports +++ b/src/ol/feature.exports @@ -1,7 +1,8 @@ @exportSymbol ol.Feature @exportProperty ol.Feature.prototype.getGeometryName -@exportProperty ol.Feature.prototype.setGeometryName @exportProperty ol.Feature.prototype.getId -@exportProperty ol.Feature.prototype.setId @exportProperty ol.Feature.prototype.getStyle +@exportProperty ol.Feature.prototype.getStyleFunction +@exportProperty ol.Feature.prototype.setGeometryName +@exportProperty ol.Feature.prototype.setId @exportProperty ol.Feature.prototype.setStyle diff --git a/src/ol/featureoverlay.exports b/src/ol/featureoverlay.exports index cf40b7937a..9cfe34b200 100644 --- a/src/ol/featureoverlay.exports +++ b/src/ol/featureoverlay.exports @@ -1,6 +1,8 @@ @exportSymbol ol.FeatureOverlay @exportProperty ol.FeatureOverlay.prototype.addFeature @exportProperty ol.FeatureOverlay.prototype.getFeatures +@exportProperty ol.FeatureOverlay.prototype.getStyle +@exportProperty ol.FeatureOverlay.prototype.getStyleFunction @exportProperty ol.FeatureOverlay.prototype.setFeatures @exportProperty ol.FeatureOverlay.prototype.setMap @exportProperty ol.FeatureOverlay.prototype.setStyle diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 6d9ac8863e..4962b7cd18 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -241,6 +241,9 @@ ol.FeatureOverlay.prototype.setMap = function(map) { /** + * Set the style for features. This can be a single style object, an array + * of styles, or a function that takes a feature and resolution and returns + * an array of styles. * @param {ol.style.Style|Array.|ol.feature.StyleFunction} style * Overlay style. * @todo stability experimental @@ -253,6 +256,8 @@ ol.FeatureOverlay.prototype.setStyle = function(style) { /** + * Get the style for features. This returns whatever was passed to the `style` + * option at construction or to the `setStyle` method. * @return {ol.style.Style|Array.|ol.feature.StyleFunction} * Overlay style. */ @@ -262,6 +267,7 @@ ol.FeatureOverlay.prototype.getStyle = function() { /** + * Get the style function. * @return {ol.feature.StyleFunction|undefined} Style function. */ ol.FeatureOverlay.prototype.getStyleFunction = function() { diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index ac7d597027..be493b810d 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1,2 +1,4 @@ @exportSymbol ol.layer.Vector +@exportProperty ol.layer.Vector.prototype.getStyle +@exportProperty ol.layer.Vector.prototype.getStyleFunction @exportProperty ol.layer.Vector.prototype.setStyle diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 291c466656..5123320e1b 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -68,9 +68,10 @@ goog.exportProperty( /** + * Get the style for features. This returns whatever was passed to the `style` + * option at construction or to the `setStyle` method. * @return {ol.style.Style|Array.|ol.feature.StyleFunction} * Layer style. - * @todo stability experimental */ ol.layer.Vector.prototype.getStyle = function() { return this.style_; @@ -78,6 +79,7 @@ ol.layer.Vector.prototype.getStyle = function() { /** + * Get the style function. * @return {ol.feature.StyleFunction} Layer style function. * @todo stability experimental */ @@ -103,9 +105,9 @@ goog.exportProperty( /** - * If the styles are changed by setting a new style function or by changing the - * value returned by the style function then `dispatchChangeEvent` should be - * called on the layer for the layer to be refreshed on the screen. + * Set the style for features. This can be a single style object, an array + * of styles, or a function that takes a feature and resolution and returns + * an array of styles. * @param {ol.style.Style|Array.|ol.feature.StyleFunction} style * Layer style. * @todo stability experimental From e9b4e42d84722ebf6520e01d7b418548c59bda9d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 13 Feb 2014 10:49:01 -0700 Subject: [PATCH 16/16] Create anonymous functions in an execution context with limited scope --- src/ol/feature.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/feature.js b/src/ol/feature.js index c9d1c09271..95e7f684e5 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -266,9 +266,7 @@ ol.feature.createFeatureStyleFunction = function(obj) { goog.asserts.assertInstanceof(obj, ol.style.Style); styles = [obj]; } - styleFunction = function(resolution) { - return styles; - }; + styleFunction = goog.functions.constant(styles); } return styleFunction; }; @@ -301,9 +299,7 @@ ol.feature.createStyleFunction = function(obj) { goog.asserts.assertInstanceof(obj, ol.style.Style); styles = [obj]; } - styleFunction = function(feature, resolution) { - return styles; - }; + styleFunction = goog.functions.constant(styles); } return styleFunction; };