git-svn-id: http://svn.openlayers.org/trunk/openlayers@10350 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
443 lines
14 KiB
JavaScript
443 lines
14 KiB
JavaScript
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
|
|
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
|
|
* full text of the license. */
|
|
|
|
|
|
/**
|
|
* @requires OpenLayers/Util.js
|
|
* @requires OpenLayers/Feature/Vector.js
|
|
*/
|
|
|
|
/**
|
|
* Class: OpenLayers.Style
|
|
* This class represents a UserStyle obtained
|
|
* from a SLD, containing styling rules.
|
|
*/
|
|
OpenLayers.Style = OpenLayers.Class({
|
|
|
|
/**
|
|
* Property: id
|
|
* {String} A unique id for this session.
|
|
*/
|
|
id: null,
|
|
|
|
/**
|
|
* APIProperty: name
|
|
* {String}
|
|
*/
|
|
name: null,
|
|
|
|
/**
|
|
* Property: title
|
|
* {String} Title of this style (set if included in SLD)
|
|
*/
|
|
title: null,
|
|
|
|
/**
|
|
* Property: description
|
|
* {String} Description of this style (set if abstract is included in SLD)
|
|
*/
|
|
description: null,
|
|
|
|
/**
|
|
* APIProperty: layerName
|
|
* {<String>} name of the layer that this style belongs to, usually
|
|
* according to the NamedLayer attribute of an SLD document.
|
|
*/
|
|
layerName: null,
|
|
|
|
/**
|
|
* APIProperty: isDefault
|
|
* {Boolean}
|
|
*/
|
|
isDefault: false,
|
|
|
|
/**
|
|
* Property: rules
|
|
* {Array(<OpenLayers.Rule>)}
|
|
*/
|
|
rules: null,
|
|
|
|
/**
|
|
* Property: context
|
|
* {Object} An optional object with properties that symbolizers' property
|
|
* values should be evaluated against. If no context is specified,
|
|
* feature.attributes will be used
|
|
*/
|
|
context: null,
|
|
|
|
/**
|
|
* Property: defaultStyle
|
|
* {Object} hash of style properties to use as default for merging
|
|
* rule-based style symbolizers onto. If no rules are defined,
|
|
* createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to
|
|
* true, the defaultStyle will only be taken into account if there are
|
|
* rules defined.
|
|
*/
|
|
defaultStyle: null,
|
|
|
|
/**
|
|
* Property: defaultsPerSymbolizer
|
|
* {Boolean} If set to true, the <defaultStyle> will extend the symbolizer
|
|
* of every rule. Properties of the <defaultStyle> will also be used to set
|
|
* missing symbolizer properties if the symbolizer has stroke, fill or
|
|
* graphic set to true. Default is false.
|
|
*/
|
|
defaultsPerSymbolizer: false,
|
|
|
|
/**
|
|
* Property: propertyStyles
|
|
* {Hash of Boolean} cache of style properties that need to be parsed for
|
|
* propertyNames. Property names are keys, values won't be used.
|
|
*/
|
|
propertyStyles: null,
|
|
|
|
|
|
/**
|
|
* Constructor: OpenLayers.Style
|
|
* Creates a UserStyle.
|
|
*
|
|
* Parameters:
|
|
* style - {Object} Optional hash of style properties that will be
|
|
* used as default style for this style object. This style
|
|
* applies if no rules are specified. Symbolizers defined in
|
|
* rules will extend this default style.
|
|
* options - {Object} An optional object with properties to set on the
|
|
* style.
|
|
*
|
|
* Valid options:
|
|
* rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the
|
|
* style.
|
|
*
|
|
* Return:
|
|
* {<OpenLayers.Style>}
|
|
*/
|
|
initialize: function(style, options) {
|
|
|
|
OpenLayers.Util.extend(this, options);
|
|
this.rules = [];
|
|
if(options && options.rules) {
|
|
this.addRules(options.rules);
|
|
}
|
|
|
|
// use the default style from OpenLayers.Feature.Vector if no style
|
|
// was given in the constructor
|
|
this.setDefaultStyle(style ||
|
|
OpenLayers.Feature.Vector.style["default"]);
|
|
|
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
|
},
|
|
|
|
/**
|
|
* APIMethod: destroy
|
|
* nullify references to prevent circular references and memory leaks
|
|
*/
|
|
destroy: function() {
|
|
for (var i=0, len=this.rules.length; i<len; i++) {
|
|
this.rules[i].destroy();
|
|
this.rules[i] = null;
|
|
}
|
|
this.rules = null;
|
|
this.defaultStyle = null;
|
|
},
|
|
|
|
/**
|
|
* Method: createSymbolizer
|
|
* creates a style by applying all feature-dependent rules to the base
|
|
* style.
|
|
*
|
|
* Parameters:
|
|
* feature - {<OpenLayers.Feature>} feature to evaluate rules for
|
|
*
|
|
* Returns:
|
|
* {Object} symbolizer hash
|
|
*/
|
|
createSymbolizer: function(feature) {
|
|
var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
|
|
OpenLayers.Util.extend({}, this.defaultStyle), feature);
|
|
|
|
var rules = this.rules;
|
|
|
|
var rule, context;
|
|
var elseRules = [];
|
|
var appliedRules = false;
|
|
for(var i=0, len=rules.length; i<len; i++) {
|
|
rule = rules[i];
|
|
// does the rule apply?
|
|
var applies = rule.evaluate(feature);
|
|
|
|
if(applies) {
|
|
if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
|
|
elseRules.push(rule);
|
|
} else {
|
|
appliedRules = true;
|
|
this.applySymbolizer(rule, style, feature);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if no other rules apply, apply the rules with else filters
|
|
if(appliedRules == false && elseRules.length > 0) {
|
|
appliedRules = true;
|
|
for(var i=0, len=elseRules.length; i<len; i++) {
|
|
this.applySymbolizer(elseRules[i], style, feature);
|
|
}
|
|
}
|
|
|
|
// don't display if there were rules but none applied
|
|
if(rules.length > 0 && appliedRules == false) {
|
|
style.display = "none";
|
|
}
|
|
|
|
return style;
|
|
},
|
|
|
|
/**
|
|
* Method: applySymbolizer
|
|
*
|
|
* Parameters:
|
|
* rule - {OpenLayers.Rule}
|
|
* style - {Object}
|
|
* feature - {<OpenLayer.Feature.Vector>}
|
|
*
|
|
* Returns:
|
|
* {Object} A style with new symbolizer applied.
|
|
*/
|
|
applySymbolizer: function(rule, style, feature) {
|
|
var symbolizerPrefix = feature.geometry ?
|
|
this.getSymbolizerPrefix(feature.geometry) :
|
|
OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
|
|
|
|
var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
|
|
|
|
if(this.defaultsPerSymbolizer === true) {
|
|
var defaults = this.defaultStyle;
|
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
|
pointRadius: defaults.pointRadius
|
|
});
|
|
if(symbolizer.stroke === true || symbolizer.graphic === true) {
|
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
|
strokeWidth: defaults.strokeWidth,
|
|
strokeColor: defaults.strokeColor,
|
|
strokeOpacity: defaults.strokeOpacity,
|
|
strokeDashstyle: defaults.strokeDashstyle,
|
|
strokeLinecap: defaults.strokeLinecap
|
|
});
|
|
}
|
|
if(symbolizer.fill === true || symbolizer.graphic === true) {
|
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
|
fillColor: defaults.fillColor,
|
|
fillOpacity: defaults.fillOpacity
|
|
});
|
|
}
|
|
if(symbolizer.graphic === true) {
|
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
|
pointRadius: this.defaultStyle.pointRadius,
|
|
externalGraphic: this.defaultStyle.externalGraphic,
|
|
graphicName: this.defaultStyle.graphicName,
|
|
graphicOpacity: this.defaultStyle.graphicOpacity,
|
|
graphicWidth: this.defaultStyle.graphicWidth,
|
|
graphicHeight: this.defaultStyle.graphicHeight,
|
|
graphicXOffset: this.defaultStyle.graphicXOffset,
|
|
graphicYOffset: this.defaultStyle.graphicYOffset
|
|
});
|
|
}
|
|
}
|
|
|
|
// merge the style with the current style
|
|
return this.createLiterals(
|
|
OpenLayers.Util.extend(style, symbolizer), feature);
|
|
},
|
|
|
|
/**
|
|
* Method: createLiterals
|
|
* creates literals for all style properties that have an entry in
|
|
* <this.propertyStyles>.
|
|
*
|
|
* Parameters:
|
|
* style - {Object} style to create literals for. Will be modified
|
|
* inline.
|
|
* feature - {Object}
|
|
*
|
|
* Returns:
|
|
* {Object} the modified style
|
|
*/
|
|
createLiterals: function(style, feature) {
|
|
var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
|
|
OpenLayers.Util.extend(context, this.context);
|
|
|
|
for (var i in this.propertyStyles) {
|
|
style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
|
|
}
|
|
return style;
|
|
},
|
|
|
|
/**
|
|
* Method: findPropertyStyles
|
|
* Looks into all rules for this style and the defaultStyle to collect
|
|
* all the style hash property names containing ${...} strings that have
|
|
* to be replaced using the createLiteral method before returning them.
|
|
*
|
|
* Returns:
|
|
* {Object} hash of property names that need createLiteral parsing. The
|
|
* name of the property is the key, and the value is true;
|
|
*/
|
|
findPropertyStyles: function() {
|
|
var propertyStyles = {};
|
|
|
|
// check the default style
|
|
var style = this.defaultStyle;
|
|
this.addPropertyStyles(propertyStyles, style);
|
|
|
|
// walk through all rules to check for properties in their symbolizer
|
|
var rules = this.rules;
|
|
var symbolizer, value;
|
|
for (var i=0, len=rules.length; i<len; i++) {
|
|
symbolizer = rules[i].symbolizer;
|
|
for (var key in symbolizer) {
|
|
value = symbolizer[key];
|
|
if (typeof value == "object") {
|
|
// symbolizer key is "Point", "Line" or "Polygon"
|
|
this.addPropertyStyles(propertyStyles, value);
|
|
} else {
|
|
// symbolizer is a hash of style properties
|
|
this.addPropertyStyles(propertyStyles, symbolizer);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return propertyStyles;
|
|
},
|
|
|
|
/**
|
|
* Method: addPropertyStyles
|
|
*
|
|
* Parameters:
|
|
* propertyStyles - {Object} hash to add new property styles to. Will be
|
|
* modified inline
|
|
* symbolizer - {Object} search this symbolizer for property styles
|
|
*
|
|
* Returns:
|
|
* {Object} propertyStyles hash
|
|
*/
|
|
addPropertyStyles: function(propertyStyles, symbolizer) {
|
|
var property;
|
|
for (var key in symbolizer) {
|
|
property = symbolizer[key];
|
|
if (typeof property == "string" &&
|
|
property.match(/\$\{\w+\}/)) {
|
|
propertyStyles[key] = true;
|
|
}
|
|
}
|
|
return propertyStyles;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: addRules
|
|
* Adds rules to this style.
|
|
*
|
|
* Parameters:
|
|
* rules - {Array(<OpenLayers.Rule>)}
|
|
*/
|
|
addRules: function(rules) {
|
|
Array.prototype.push.apply(this.rules, rules);
|
|
this.propertyStyles = this.findPropertyStyles();
|
|
},
|
|
|
|
/**
|
|
* APIMethod: setDefaultStyle
|
|
* Sets the default style for this style object.
|
|
*
|
|
* Parameters:
|
|
* style - {Object} Hash of style properties
|
|
*/
|
|
setDefaultStyle: function(style) {
|
|
this.defaultStyle = style;
|
|
this.propertyStyles = this.findPropertyStyles();
|
|
},
|
|
|
|
/**
|
|
* Method: getSymbolizerPrefix
|
|
* Returns the correct symbolizer prefix according to the
|
|
* geometry type of the passed geometry
|
|
*
|
|
* Parameters:
|
|
* geometry {<OpenLayers.Geometry>}
|
|
*
|
|
* Returns:
|
|
* {String} key of the according symbolizer
|
|
*/
|
|
getSymbolizerPrefix: function(geometry) {
|
|
var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
|
|
for (var i=0, len=prefixes.length; i<len; i++) {
|
|
if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
|
|
return prefixes[i];
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* APIMethod: clone
|
|
* Clones this style.
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Style>} Clone of this style.
|
|
*/
|
|
clone: function() {
|
|
var options = OpenLayers.Util.extend({}, this);
|
|
// clone rules
|
|
if(this.rules) {
|
|
options.rules = [];
|
|
for(var i=0, len=this.rules.length; i<len; ++i) {
|
|
options.rules.push(this.rules[i].clone());
|
|
}
|
|
}
|
|
// clone context
|
|
options.context = this.context && OpenLayers.Util.extend({}, this.context);
|
|
//clone default style
|
|
var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
|
|
return new OpenLayers.Style(defaultStyle, options);
|
|
},
|
|
|
|
CLASS_NAME: "OpenLayers.Style"
|
|
});
|
|
|
|
|
|
/**
|
|
* Function: createLiteral
|
|
* converts a style value holding a combination of PropertyName and Literal
|
|
* into a Literal, taking the property values from the passed features.
|
|
*
|
|
* Parameters:
|
|
* value - {String} value to parse. If this string contains a construct like
|
|
* "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
|
|
* will be replaced by the value of the "bar" attribute of the passed
|
|
* feature.
|
|
* context - {Object} context to take attribute values from
|
|
* feature - {<OpenLayers.Feature.Vector>} optional feature to pass to
|
|
* <OpenLayers.String.format> for evaluating functions in the
|
|
* context.
|
|
* property - {String} optional, name of the property for which the literal is
|
|
* being created for evaluating functions in the context.
|
|
*
|
|
* Returns:
|
|
* {String} the parsed value. In the example of the value parameter above, the
|
|
* result would be "foo valueOfBar", assuming that the passed feature has an
|
|
* attribute named "bar" with the value "valueOfBar".
|
|
*/
|
|
OpenLayers.Style.createLiteral = function(value, context, feature, property) {
|
|
if (typeof value == "string" && value.indexOf("${") != -1) {
|
|
value = OpenLayers.String.format(value, context, [feature, property]);
|
|
value = (isNaN(value) || !value) ? value : parseFloat(value);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
|
|
* {Array} prefixes of the sld symbolizers. These are the
|
|
* same as the main geometry types
|
|
*/
|
|
OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
|
|
'Raster'];
|