1
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3
* full text of the license. */
7
* @requires OpenLayers/Util.js
8
* @requires OpenLayers/Feature/Vector.js
12
* Class: OpenLayers.Style
13
* This class represents a UserStyle obtained
14
* from a SLD, containing styling rules.
16
OpenLayers.Style = OpenLayers.Class({
26
* {String} Title of this style (set if included in SLD)
31
* Property: description
32
* {String} Description of this style (set if abstract is included in SLD)
37
* APIProperty: layerName
38
* {<String>} name of the layer that this style belongs to, usually
39
* according to the NamedLayer attribute of an SLD document.
44
* APIProperty: isDefault
51
* {Array(<OpenLayers.Rule>)}
57
* {Object} An optional object with properties that symbolizers' property
58
* values should be evaluated against. If no context is specified,
59
* feature.attributes will be used
64
* Property: defaultStyle
65
* {Object} hash of style properties to use as default for merging
66
* rule-based style symbolizers onto. If no rules are defined,
67
* createSymbolizer will return this style.
72
* Property: propertyStyles
73
* {Hash of Boolean} cache of style properties that need to be parsed for
74
* propertyNames. Property names are keys, values won't be used.
80
* Constructor: OpenLayers.Style
81
* Creates a UserStyle.
84
* style - {Object} Optional hash of style properties that will be
85
* used as default style for this style object. This style
86
* applies if no rules are specified. Symbolizers defined in
87
* rules will extend this default style.
88
* options - {Object} An optional object with properties to set on the
92
* {<OpenLayers.Style>}
94
initialize: function(style, options) {
97
// use the default style from OpenLayers.Feature.Vector if no style
98
// was given in the constructor
99
this.setDefaultStyle(style ||
100
OpenLayers.Feature.Vector.style["default"]);
102
OpenLayers.Util.extend(this, options);
107
* nullify references to prevent circular references and memory leaks
109
destroy: function() {
110
for (var i=0, len=this.rules.length; i<len; i++) {
111
this.rules[i].destroy();
112
this.rules[i] = null;
115
this.defaultStyle = null;
119
* Method: createSymbolizer
120
* creates a style by applying all feature-dependent rules to the base
124
* feature - {<OpenLayers.Feature>} feature to evaluate rules for
127
* {Object} symbolizer hash
129
createSymbolizer: function(feature) {
130
var style = this.createLiterals(
131
OpenLayers.Util.extend({}, this.defaultStyle), feature);
133
var rules = this.rules;
137
var appliedRules = false;
138
for(var i=0, len=rules.length; i<len; i++) {
140
// does the rule apply?
141
var applies = rule.evaluate(feature);
144
if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
145
elseRules.push(rule);
148
this.applySymbolizer(rule, style, feature);
153
// if no other rules apply, apply the rules with else filters
154
if(appliedRules == false && elseRules.length > 0) {
156
for(var i=0, len=elseRules.length; i<len; i++) {
157
this.applySymbolizer(elseRules[i], style, feature);
161
// don't display if there were rules but none applied
162
if(rules.length > 0 && appliedRules == false) {
163
style.display = "none";
172
* Method: applySymbolizer
175
* rule - {OpenLayers.Rule}
177
* feature - {<OpenLayer.Feature.Vector>}
180
* {Object} A style with new symbolizer applied.
182
applySymbolizer: function(rule, style, feature) {
183
var symbolizerPrefix = feature.geometry ?
184
this.getSymbolizerPrefix(feature.geometry) :
185
OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
187
var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
189
// merge the style with the current style
190
return this.createLiterals(
191
OpenLayers.Util.extend(style, symbolizer), feature);
195
* Method: createLiterals
196
* creates literals for all style properties that have an entry in
197
* <this.propertyStyles>.
200
* style - {Object} style to create literals for. Will be modified
205
* {Object} the modified style
207
createLiterals: function(style, feature) {
208
var context = this.context || feature.attributes || feature.data;
210
for (var i in this.propertyStyles) {
211
style[i] = OpenLayers.Style.createLiteral(style[i], context, feature);
217
* Method: findPropertyStyles
218
* Looks into all rules for this style and the defaultStyle to collect
219
* all the style hash property names containing ${...} strings that have
220
* to be replaced using the createLiteral method before returning them.
223
* {Object} hash of property names that need createLiteral parsing. The
224
* name of the property is the key, and the value is true;
226
findPropertyStyles: function() {
227
var propertyStyles = {};
229
// check the default style
230
var style = this.defaultStyle;
231
this.addPropertyStyles(propertyStyles, style);
233
// walk through all rules to check for properties in their symbolizer
234
var rules = this.rules;
235
var symbolizer, value;
236
for (var i=0, len=rules.length; i<len; i++) {
237
var symbolizer = rules[i].symbolizer;
238
for (var key in symbolizer) {
239
value = symbolizer[key];
240
if (typeof value == "object") {
241
// symbolizer key is "Point", "Line" or "Polygon"
242
this.addPropertyStyles(propertyStyles, value);
244
// symbolizer is a hash of style properties
245
this.addPropertyStyles(propertyStyles, symbolizer);
250
return propertyStyles;
254
* Method: addPropertyStyles
257
* propertyStyles - {Object} hash to add new property styles to. Will be
259
* symbolizer - {Object} search this symbolizer for property styles
262
* {Object} propertyStyles hash
264
addPropertyStyles: function(propertyStyles, symbolizer) {
266
for (var key in symbolizer) {
267
property = symbolizer[key];
268
if (typeof property == "string" &&
269
property.match(/\$\{\w+\}/)) {
270
propertyStyles[key] = true;
273
return propertyStyles;
277
* APIMethod: addRules
278
* Adds rules to this style.
281
* rules - {Array(<OpenLayers.Rule>)}
283
addRules: function(rules) {
284
this.rules = this.rules.concat(rules);
285
this.propertyStyles = this.findPropertyStyles();
289
* APIMethod: setDefaultStyle
290
* Sets the default style for this style object.
293
* style - {Object} Hash of style properties
295
setDefaultStyle: function(style) {
296
this.defaultStyle = style;
297
this.propertyStyles = this.findPropertyStyles();
301
* Method: getSymbolizerPrefix
302
* Returns the correct symbolizer prefix according to the
303
* geometry type of the passed geometry
306
* geometry {<OpenLayers.Geometry>}
309
* {String} key of the according symbolizer
311
getSymbolizerPrefix: function(geometry) {
312
var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
313
for (var i=0, len=prefixes.length; i<len; i++) {
314
if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
320
CLASS_NAME: "OpenLayers.Style"
325
* Function: createLiteral
326
* converts a style value holding a combination of PropertyName and Literal
327
* into a Literal, taking the property values from the passed features.
330
* value - {String} value to parse. If this string contains a construct like
331
* "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
332
* will be replaced by the value of the "bar" attribute of the passed
334
* context - {Object} context to take attribute values from
335
* feature - {OpenLayers.Feature.Vector} The feature that will be passed
336
* to <OpenLayers.String.format> for evaluating functions in the context.
339
* {String} the parsed value. In the example of the value parameter above, the
340
* result would be "foo valueOfBar", assuming that the passed feature has an
341
* attribute named "bar" with the value "valueOfBar".
343
OpenLayers.Style.createLiteral = function(value, context, feature) {
344
if (typeof value == "string" && value.indexOf("${") != -1) {
345
value = OpenLayers.String.format(value, context, [feature]);
346
value = (isNaN(value) || !value) ? value : parseFloat(value);
352
* Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
353
* {Array} prefixes of the sld symbolizers. These are the
354
* same as the main geometry types
356
OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text'];