goog.provide('ol.Feature'); goog.provide('ol.feature'); goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('goog.functions'); goog.require('ol.Object'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); goog.require('ol.style.Circle'); goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); /** * @classdesc * A vector object for geographical features with a geometry and other * attribute properties, similar to the features in vector file formats like * GeoJSON. * Features can be styled individually or use the style of their vector layer. * * @constructor * @extends {ol.Object} * @fires change Triggered when the geometry or style of the feature changes. * @param {ol.geom.Geometry|Object.=} opt_geometryOrValues * Values or geometry. * @todo api */ ol.Feature = function(opt_geometryOrValues) { goog.base(this); /** * @private * @type {number|string|undefined} */ this.id_ = undefined; /** * @type {string} * @private */ 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_ = undefined; /** * @private * @type {goog.events.Key} */ this.geometryChangeKey_ = null; goog.events.listen( this, ol.Object.getChangeEventType(this.geometryName_), this.handleGeometryChanged_, false, this); if (goog.isDef(opt_geometryOrValues)) { if (opt_geometryOrValues instanceof ol.geom.Geometry || goog.isNull(opt_geometryOrValues)) { var geometry = /** @type {ol.geom.Geometry} */ (opt_geometryOrValues); this.setGeometry(geometry); } else { goog.asserts.assert(goog.isObject(opt_geometryOrValues)); var values = /** @type {Object.} */ (opt_geometryOrValues); this.setValues(values); } } }; goog.inherits(ol.Feature, ol.Object); /** * @return {ol.geom.Geometry|undefined} Geometry. * @todo api */ ol.Feature.prototype.getGeometry = function() { return /** @type {ol.geom.Geometry|undefined} */ ( this.get(this.geometryName_)); }; goog.exportProperty( ol.Feature.prototype, 'getGeometry', ol.Feature.prototype.getGeometry); /** * @return {number|string|undefined} Id. * @todo api */ ol.Feature.prototype.getId = function() { return this.id_; }; /** * @return {string} Geometry property name. * @todo api */ ol.Feature.prototype.getGeometryName = function() { return this.geometryName_; }; /** * @return {ol.style.Style|Array.| * ol.feature.FeatureStyleFunction} User provided style. * @todo api */ ol.Feature.prototype.getStyle = function() { return this.style_; }; /** * @return {ol.feature.FeatureStyleFunction|undefined} Style function. * @todo api */ ol.Feature.prototype.getStyleFunction = function() { return this.styleFunction_; }; /** * @private */ ol.Feature.prototype.handleGeometryChange_ = function() { this.dispatchChangeEvent(); }; /** * @private */ ol.Feature.prototype.handleGeometryChanged_ = function() { if (!goog.isNull(this.geometryChangeKey_)) { goog.events.unlistenByKey(this.geometryChangeKey_); this.geometryChangeKey_ = null; } var geometry = this.getGeometry(); if (goog.isDefAndNotNull(geometry)) { this.geometryChangeKey_ = goog.events.listen(geometry, goog.events.EventType.CHANGE, this.handleGeometryChange_, false, this); this.dispatchChangeEvent(); } }; /** * @param {ol.geom.Geometry|undefined} geometry Geometry. * @todo api */ ol.Feature.prototype.setGeometry = function(geometry) { this.set(this.geometryName_, geometry); }; goog.exportProperty( ol.Feature.prototype, 'setGeometry', ol.Feature.prototype.setGeometry); /** * @param {ol.style.Style|Array.| * ol.feature.FeatureStyleFunction} style Feature style. * @todo api */ ol.Feature.prototype.setStyle = function(style) { this.style_ = style; this.styleFunction_ = ol.feature.createFeatureStyleFunction(style); this.dispatchChangeEvent(); }; /** * @param {number|string|undefined} id Id. * @todo api */ ol.Feature.prototype.setId = function(id) { this.id_ = id; this.dispatchChangeEvent(); }; /** * @param {string} name Geometry property name. * @todo api */ ol.Feature.prototype.setGeometryName = function(name) { goog.events.unlisten( this, ol.Object.getChangeEventType(this.geometryName_), this.handleGeometryChanged_, false, this); this.geometryName_ = name; goog.events.listen( this, ol.Object.getChangeEventType(this.geometryName_), this.handleGeometryChanged_, false, this); this.handleGeometryChanged_(); }; /** * A function that takes a `{number}` representing the view's resolution. It * returns an Array of {@link ol.style.Style}. This way individual features * can be styled. The this keyword inside the function references the * {@link ol.Feature} to be styled. * * @typedef {function(this: ol.Feature, number): Array.} * @todo api */ ol.feature.FeatureStyleFunction; /** * Default style function for features. * @param {number} resolution Resolution. * @return {Array.} Style. * @this {ol.Feature} */ ol.feature.defaultFeatureStyleFunction = function(resolution) { var fill = new ol.style.Fill({ color: 'rgba(255,255,255,0.4)' }); var stroke = new ol.style.Stroke({ color: '#3399CC', width: 1.25 }); var styles = [ new ol.style.Style({ image: new ol.style.Circle({ fill: fill, stroke: stroke, radius: 5 }), fill: fill, stroke: stroke }) ]; // now that we've run it the first time, // replace the function with a constant version ol.feature.defaultFeatureStyleFunction = /** @type {function(this:ol.Feature):Array.} */( function(resolution) { return styles; }); return styles; }; /** * A function that takes an {@link ol.Feature} and a `{number}` representing * the view's resolution. The function should return an array of * {@link ol.style.Style}. This way e.g. a vector layer can be styled. * * @typedef {function(ol.Feature, number): Array.} * @todo api */ ol.feature.StyleFunction; /** * @param {ol.Feature} feature Feature. * @param {number} resolution Resolution. * @return {Array.} Style. */ ol.feature.defaultStyleFunction = function(feature, resolution) { var featureStyleFunction = feature.getStyleFunction(); if (!goog.isDef(featureStyleFunction)) { featureStyleFunction = ol.feature.defaultFeatureStyleFunction; } 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; }; /** * Default styles for editing features. * @return {Object.>} Styles */ ol.feature.createDefaultEditingStyles = function() { /** @type {Object.>} */ var styles = {}; var white = [255, 255, 255, 1]; var blue = [0, 153, 255, 1]; var width = 3; styles[ol.geom.GeometryType.POLYGON] = [ new ol.style.Style({ fill: new ol.style.Fill({ color: [255, 255, 255, 0.5] }) }) ]; styles[ol.geom.GeometryType.MULTI_POLYGON] = styles[ol.geom.GeometryType.POLYGON]; styles[ol.geom.GeometryType.LINE_STRING] = [ new ol.style.Style({ stroke: new ol.style.Stroke({ color: white, width: width + 2 }) }), new ol.style.Style({ stroke: new ol.style.Stroke({ color: blue, width: width }) }) ]; styles[ol.geom.GeometryType.MULTI_LINE_STRING] = styles[ol.geom.GeometryType.LINE_STRING]; styles[ol.geom.GeometryType.POINT] = [ new ol.style.Style({ image: new ol.style.Circle({ radius: width * 2, fill: new ol.style.Fill({ color: blue }), stroke: new ol.style.Stroke({ color: white, width: width / 2 }) }), zIndex: Infinity }) ]; styles[ol.geom.GeometryType.MULTI_POINT] = styles[ol.geom.GeometryType.POINT]; styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = styles[ol.geom.GeometryType.POLYGON].concat( styles[ol.geom.GeometryType.POINT] ); return styles; };