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/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/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/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/image-vector-layer.js b/examples/image-vector-layer.js index bc38cedc5e..83bf47477b 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 + }) + }) }) }) ], @@ -46,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/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..ed8bb3714c 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() { @@ -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/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..def9194a29 100644 --- a/examples/select-features.js +++ b/examples/select-features.js @@ -15,36 +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' }), - styleFunction: function(feature, resolution) { - return 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/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..ca33ead397 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({ @@ -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 c57fc51ad8..9ffa917fca 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -389,7 +389,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 */ @@ -541,7 +542,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 */ @@ -575,7 +576,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. */ /** @@ -745,8 +746,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/feature.exports b/src/ol/feature.exports index 08e79a2c8c..6c1f200ea6 100644 --- a/src/ol/feature.exports +++ b/src/ol/feature.exports @@ -1,5 +1,8 @@ @exportSymbol ol.Feature @exportProperty ol.Feature.prototype.getGeometryName -@exportProperty ol.Feature.prototype.setGeometryName @exportProperty ol.Feature.prototype.getId +@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/feature.js b/src/ol/feature.js index 68f36d1cec..276efe83e8 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,20 @@ 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} + */ + this.styleFunction_; + /** * @private * @type {goog.events.Key} @@ -51,9 +57,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) { @@ -103,18 +106,23 @@ 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 */ 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 +150,6 @@ ol.Feature.prototype.handleGeometryChanged_ = function() { }; -/** - * @private - */ -ol.Feature.prototype.handleStyleFunctionChange_ = function() { - this.dispatchChangeEvent(); -}; - - /** * @param {ol.geom.Geometry|undefined} geometry Geometry. * @todo stability experimental @@ -164,17 +164,15 @@ 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.set(ol.FeatureProperty.STYLE_FUNCTION, styleFunction); +ol.Feature.prototype.setStyle = function(style) { + this.style_ = style; + this.styleFunction_ = ol.feature.createFeatureStyleFunction(style); + this.dispatchChangeEvent(); }; -goog.exportProperty( - ol.Feature.prototype, - 'setStyleFunction', - ol.Feature.prototype.setStyleFunction); /** @@ -247,3 +245,70 @@ ol.feature.defaultStyleFunction = function(feature, resolution) { } return featureStyleFunction.call(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 = goog.functions.constant(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 + * 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 = goog.functions.constant(styles); + } + return styleFunction; +}; diff --git a/src/ol/featureoverlay.exports b/src/ol/featureoverlay.exports index 120ff391d2..9cfe34b200 100644 --- a/src/ol/featureoverlay.exports +++ b/src/ol/featureoverlay.exports @@ -1,7 +1,9 @@ @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.setStyleFunction +@exportProperty ol.FeatureOverlay.prototype.setStyle @exportProperty ol.FeatureOverlay.prototype.removeFeature diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index ccc5f68b45..4962b7cd18 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -52,11 +52,18 @@ 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} */ - this.styleFunction_ = undefined; + this.styleFunction_ = goog.isDef(options.style) ? + ol.feature.createStyleFunction(options.style) : undefined; if (goog.isDef(options.features)) { if (goog.isArray(options.features)) { @@ -69,10 +76,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); } @@ -238,16 +241,33 @@ ol.FeatureOverlay.prototype.setMap = function(map) { /** - * @param {ol.feature.StyleFunction} styleFunction Style function. + * 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 */ -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_(); }; /** + * 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. + */ +ol.FeatureOverlay.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * Get the style function. * @return {ol.feature.StyleFunction|undefined} Style function. */ ol.FeatureOverlay.prototype.getStyleFunction = function() { 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/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); diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index 3dc18a2d27..be493b810d 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1 +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 e9bd4110d3..5123320e1b 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'); @@ -8,8 +9,7 @@ goog.require('ol.layer.Layer'); * @enum {string} */ ol.layer.VectorProperty = { - RENDER_GEOMETRY_FUNCTIONS: 'renderGeometryFunctions', - STYLE_FUNCTION: 'styleFunction' + RENDER_GEOMETRY_FUNCTIONS: 'renderGeometryFunctions' }; @@ -25,11 +25,28 @@ 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)); - // FIXME veryify this - if (goog.isDef(options.styleFunction)) { - this.setStyleFunction(options.styleFunction); + 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.setStyle(options.style); } }; @@ -51,17 +68,24 @@ goog.exportProperty( /** - * @return {ol.feature.StyleFunction|undefined} Style function. + * 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. + */ +ol.layer.Vector.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * Get the style function. + * @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); /** @@ -81,16 +105,15 @@ 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. + * 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 */ -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/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index 97d84e32e6..d6050ac8d4 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -44,8 +44,9 @@ ol.source.ImageVector = function(options) { * @private * @type {!ol.feature.StyleFunction} */ - this.styleFunction_ = goog.isDef(options.styleFunction) ? - options.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..afe29368ec 100644 --- a/test/spec/ol/feature.test.js +++ b/test/spec/ol/feature.test.js @@ -207,10 +207,191 @@ describe('ol.Feature', function() { }); + describe('#getStyleFunction()', function() { + + var styleFunction = function(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 setStyle', function() { + var feature = new ol.Feature(); + feature.setStyle(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); + }); + + it('does not get confused with "styleFunction" option', function() { + var feature = new ol.Feature({ + styleFunction: 'foo' + }); + expect(feature.getStyleFunction()).to.be(undefined); + }); + + }); + + describe('#setStyle()', function() { + + var style = new ol.style.Style(); + + var styleFunction = function(feature, resolution) { + return null; + }; + + it('accepts a single style', function() { + var feature = new ol.Feature(); + 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); + }); + + it('dispatches a change event', function(done) { + var feature = new ol.Feature(); + feature.on('change', function() { + done(); + }); + 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); + }); + + }); + + +}); + +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(); + }); + +}); + +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'); goog.require('ol.Feature'); +goog.require('ol.feature'); goog.require('ol.geom.Point'); +goog.require('ol.style.Style'); diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js new file mode 100644 index 0000000000..f00c97ba41 --- /dev/null +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -0,0 +1,117 @@ +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]); + }); + + }); + + 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'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Style');