Add optional context property to OpenLayers.Rule, so rules can now be evaluated against diffent contexts than feature.attributes. This changeset also renames Rule.Logical.children to Rule.Logical.rules, to make it more consistent with OL.Style. r=crschmidt (closes #1331)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@6116 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
@@ -343,7 +343,7 @@ OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML, {
|
||||
var filters = filter[0].childNodes;
|
||||
for (var i=0; i<filters.length; i++) {
|
||||
if (filters[i].nodeType == 1) {
|
||||
rule.children.push(this.parseFilter(filters[i]));
|
||||
rule.rules.push(this.parseFilter(filters[i]));
|
||||
}
|
||||
}
|
||||
return rule;
|
||||
@@ -357,7 +357,7 @@ OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML, {
|
||||
var filters = filter[0].childNodes;
|
||||
for (var i=0; i<filters.length; i++) {
|
||||
if (filters[i].nodeType == 1) {
|
||||
rule.children.push(this.parseFilter(filters[i]));
|
||||
rule.rules.push(this.parseFilter(filters[i]));
|
||||
}
|
||||
}
|
||||
return rule;
|
||||
@@ -368,7 +368,7 @@ OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML, {
|
||||
if (filter) {
|
||||
var rule = new OpenLayers.Rule.Logical(
|
||||
{type: OpenLayers.Rule.Logical.NOT});
|
||||
rule.children.push(this.parseFilter(filter[0]));
|
||||
rule.rules.push(this.parseFilter(filter[0]));
|
||||
return rule;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,14 @@ OpenLayers.Rule = OpenLayers.Class({
|
||||
*/
|
||||
name: 'default',
|
||||
|
||||
/**
|
||||
* Property: context
|
||||
* {Object} An optional object with properties that the rule and its
|
||||
* symbolizers' property values should be evaluatad against. If no
|
||||
* context is specified, feature.attributes will be used
|
||||
*/
|
||||
context: null,
|
||||
|
||||
/**
|
||||
* Property: elseFilter
|
||||
* {Boolean} Determines whether this rule is only to be applied only if
|
||||
@@ -93,8 +101,40 @@ OpenLayers.Rule = OpenLayers.Class({
|
||||
* This rule is the default rule and always returns true.
|
||||
*/
|
||||
evaluate: function(feature) {
|
||||
// Default rule always applies. Subclasses will want to override this.
|
||||
return true;
|
||||
var context = this.getContext(feature);
|
||||
var applies = true;
|
||||
|
||||
if (this.minScaleDenominator || this.maxScaleDenominator) {
|
||||
var scale = feature.layer.map.getScale();
|
||||
}
|
||||
|
||||
// check if within minScale/maxScale bounds
|
||||
if (this.minScaleDenominator) {
|
||||
applies = scale >= OpenLayers.Style.createLiteral(
|
||||
this.minScaleDenominator, context);
|
||||
}
|
||||
if (applies && this.maxScaleDenominator) {
|
||||
applies = scale < OpenLayers.Style.createLiteral(
|
||||
this.maxScaleDenominator, context);
|
||||
}
|
||||
|
||||
return applies;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: getContext
|
||||
* Gets the context for evaluating this rule
|
||||
*
|
||||
* Paramters:
|
||||
* feature - {<OpenLayers.Feature>} feature to take the context from if
|
||||
* none is specified.
|
||||
*/
|
||||
getContext: function(feature) {
|
||||
var context = this.context;
|
||||
if (!context) {
|
||||
context = feature.attributes || feature.data;
|
||||
}
|
||||
return context;
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Rule"
|
||||
|
||||
@@ -33,7 +33,7 @@ OpenLayers.Rule.Comparison = OpenLayers.Class(OpenLayers.Rule, {
|
||||
/**
|
||||
* APIProperty: property
|
||||
* {String}
|
||||
* name of the feature attribute to compare
|
||||
* name of the context property to compare
|
||||
*/
|
||||
property: null,
|
||||
|
||||
@@ -84,34 +84,37 @@ OpenLayers.Rule.Comparison = OpenLayers.Class(OpenLayers.Rule, {
|
||||
|
||||
/**
|
||||
* APIMethod: evaluate
|
||||
* evaluates this rule for a specific feature
|
||||
* evaluates this rule for a specific context
|
||||
*
|
||||
* Parameters:
|
||||
* feature - {<OpenLayers.Feature>} feature to apply the rule to.
|
||||
* context - {Object} context to apply the rule to.
|
||||
*
|
||||
* Returns:
|
||||
* {boolean} true if the rule applies, false if it does not
|
||||
*/
|
||||
evaluate: function(feature) {
|
||||
var attributes = feature.attributes || feature.data;
|
||||
if (!OpenLayers.Rule.prototype.evaluate.apply(this, arguments)) {
|
||||
return false;
|
||||
}
|
||||
var context = this.getContext(feature);
|
||||
switch(this.type) {
|
||||
case OpenLayers.Rule.Comparison.EQUAL_TO:
|
||||
case OpenLayers.Rule.Comparison.LESS_THAN:
|
||||
case OpenLayers.Rule.Comparison.GREATER_THAN:
|
||||
case OpenLayers.Rule.Comparison.LESS_THAN_OR_EQUAL_TO:
|
||||
case OpenLayers.Rule.Comparison.GREATER_THAN_OR_EQUAL_TO:
|
||||
return this.binaryCompare(feature, this.property, this.value);
|
||||
return this.binaryCompare(context, this.property, this.value);
|
||||
|
||||
case OpenLayers.Rule.Comparison.BETWEEN:
|
||||
var result =
|
||||
attributes[this.property] > this.lowerBoundary;
|
||||
context[this.property] > this.lowerBoundary;
|
||||
result = result &&
|
||||
attributes[this.property] < this.upperBoundary;
|
||||
context[this.property] < this.upperBoundary;
|
||||
return result;
|
||||
case OpenLayers.Rule.Comparison.LIKE:
|
||||
var regexp = new RegExp(this.value,
|
||||
"gi");
|
||||
return regexp.test(attributes[this.property]);
|
||||
return regexp.test(context[this.property]);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -165,28 +168,27 @@ OpenLayers.Rule.Comparison = OpenLayers.Class(OpenLayers.Rule, {
|
||||
* Compares a feature property to a rule value
|
||||
*
|
||||
* Parameters:
|
||||
* feature - {<OpenLayers.Feature>}
|
||||
* context - {Object}
|
||||
* property - {String} or {Number}
|
||||
* value - {String} or {Number}, same as property
|
||||
*
|
||||
* Returns:
|
||||
* {boolean}
|
||||
*/
|
||||
binaryCompare: function(feature, property, value) {
|
||||
var attributes = feature.attributes || feature.data;
|
||||
binaryCompare: function(context, property, value) {
|
||||
switch (this.type) {
|
||||
case OpenLayers.Rule.Comparison.EQUAL_TO:
|
||||
return attributes[property] == value;
|
||||
return context[property] == value;
|
||||
case OpenLayers.Rule.Comparison.NOT_EQUAL_TO:
|
||||
return attributes[property] != value;
|
||||
return context[property] != value;
|
||||
case OpenLayers.Rule.Comparison.LESS_THAN:
|
||||
return attributes[property] < value;
|
||||
return context[property] < value;
|
||||
case OpenLayers.Rule.Comparison.GREATER_THAN:
|
||||
return attributes[property] > value;
|
||||
return context[property] > value;
|
||||
case OpenLayers.Rule.Comparison.LESS_THAN_OR_EQUAL_TO:
|
||||
return attributes[property] <= value;
|
||||
return context[property] <= value;
|
||||
case OpenLayers.Rule.Comparison.GREATER_THAN_OR_EQUAL_TO:
|
||||
return attributes[property] >= value;
|
||||
return context[property] >= value;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ OpenLayers.Rule.FeatureId = OpenLayers.Class(OpenLayers.Rule, {
|
||||
* {boolean} true if the rule applies, false if it does not
|
||||
*/
|
||||
evaluate: function(feature) {
|
||||
if (!OpenLayers.Rule.prototype.evaluate.apply(this, arguments)) {
|
||||
return false;
|
||||
}
|
||||
for (var i=0; i<this.fids.length; i++) {
|
||||
var fid = feature.fid || feature.id;
|
||||
if (fid == this.fids[i]) {
|
||||
|
||||
@@ -20,7 +20,7 @@ OpenLayers.Rule.Logical = OpenLayers.Class(OpenLayers.Rule, {
|
||||
* APIProperty: children
|
||||
* {Array(<OpenLayers.Rule>)} child rules for this rule
|
||||
*/
|
||||
children: null,
|
||||
rules: null,
|
||||
|
||||
/**
|
||||
* APIProperty: type
|
||||
@@ -43,7 +43,7 @@ OpenLayers.Rule.Logical = OpenLayers.Class(OpenLayers.Rule, {
|
||||
* {<OpenLayers.Rule.Logical>}
|
||||
*/
|
||||
initialize: function(options) {
|
||||
this.children = [];
|
||||
this.rules = [];
|
||||
OpenLayers.Rule.prototype.initialize.apply(this, [options]);
|
||||
},
|
||||
|
||||
@@ -52,10 +52,10 @@ OpenLayers.Rule.Logical = OpenLayers.Class(OpenLayers.Rule, {
|
||||
* nullify references to prevent circular references and memory leaks
|
||||
*/
|
||||
destroy: function() {
|
||||
for (var i=0; i<this.children.length; i++) {
|
||||
this.children[i].destroy();
|
||||
for (var i=0; i<this.rules.length; i++) {
|
||||
this.rules[i].destroy();
|
||||
}
|
||||
this.children = null;
|
||||
this.rules = null;
|
||||
OpenLayers.Rule.prototype.destroy.apply(this, arguments);
|
||||
},
|
||||
|
||||
@@ -70,25 +70,28 @@ OpenLayers.Rule.Logical = OpenLayers.Class(OpenLayers.Rule, {
|
||||
* {boolean} true if the rule applies, false if it does not
|
||||
*/
|
||||
evaluate: function(feature) {
|
||||
if (!OpenLayers.Rule.prototype.evaluate.apply(this, arguments)) {
|
||||
return false;
|
||||
}
|
||||
switch(this.type) {
|
||||
case OpenLayers.Rule.Logical.AND:
|
||||
for (var i=0; i<this.children.length; i++) {
|
||||
if (this.children[i].evaluate(feature) == false) {
|
||||
for (var i=0; i<this.rules.length; i++) {
|
||||
if (this.rules[i].evaluate(feature) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case OpenLayers.Rule.Logical.OR:
|
||||
for (var i=0; i<this.children.length; i++) {
|
||||
if (this.children[i].evaluate(feature) == true) {
|
||||
for (var i=0; i<this.rules.length; i++) {
|
||||
if (this.rules[i].evaluate(feature) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case OpenLayers.Rule.Logical.NOT:
|
||||
return (!this.children[0].evaluate(feature));
|
||||
return (!this.rules[0].evaluate(feature));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -112,34 +112,24 @@ OpenLayers.Style = OpenLayers.Class({
|
||||
|
||||
var rules = this.rules;
|
||||
|
||||
var rule;
|
||||
var rule, context;
|
||||
var elseRules = [];
|
||||
var appliedRules = false;
|
||||
for(var i=0; i<rules.length; i++) {
|
||||
rule = rules[i];
|
||||
context = rule.context;
|
||||
if (!context) {
|
||||
context = feature.attributes || feature.data;
|
||||
}
|
||||
// does the rule apply?
|
||||
var applies = rule.evaluate(feature);
|
||||
|
||||
if (rule.minScaleDenominator || rule.maxScaleDenominator) {
|
||||
var scale = feature.layer.map.getScale();
|
||||
}
|
||||
|
||||
// check if within minScale/maxScale bounds
|
||||
if (rule.minScaleDenominator) {
|
||||
applies = scale >= OpenLayers.Style.createLiteral(
|
||||
rule.minScaleDenominator, feature);
|
||||
}
|
||||
if (applies && rule.maxScaleDenominator) {
|
||||
applies = scale < OpenLayers.Style.createLiteral(
|
||||
rule.maxScaleDenominator, feature);
|
||||
}
|
||||
|
||||
if(applies) {
|
||||
if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
|
||||
elseRules.push(rule);
|
||||
} else {
|
||||
appliedRules = true;
|
||||
this.applySymbolizer(rule, style, feature);
|
||||
this.applySymbolizer(rule, style, feature, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,13 +138,10 @@ OpenLayers.Style = OpenLayers.Class({
|
||||
if(appliedRules == false && elseRules.length > 0) {
|
||||
appliedRules = true;
|
||||
for(var i=0; i<elseRules.length; i++) {
|
||||
this.applySymbolizer(elseRules[i], style, feature);
|
||||
this.applySymbolizer(elseRules[i], style, feature, context);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate literals for all styles in the propertyStyles cache
|
||||
this.createLiterals(style, feature);
|
||||
|
||||
// don't display if there were rules but none applied
|
||||
if(rules.length > 0 && appliedRules == false) {
|
||||
style.display = "none";
|
||||
@@ -172,18 +159,21 @@ OpenLayers.Style = OpenLayers.Class({
|
||||
* rule - {OpenLayers.Rule}
|
||||
* style - {Object}
|
||||
* feature - {<OpenLayer.Feature.Vector>}
|
||||
* context - {Object}
|
||||
*
|
||||
* Returns:
|
||||
* {Object} A style with new symbolizer applied.
|
||||
*/
|
||||
applySymbolizer: function(rule, style, feature) {
|
||||
applySymbolizer: function(rule, style, feature, context) {
|
||||
var symbolizerPrefix = feature.geometry ?
|
||||
this.getSymbolizerPrefix(feature.geometry) :
|
||||
OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
|
||||
|
||||
// merge the style with the current style
|
||||
var symbolizer = rule.symbolizer[symbolizerPrefix];
|
||||
return OpenLayers.Util.extend(style, symbolizer);
|
||||
|
||||
// merge the style with the current style
|
||||
return this.createLiterals(
|
||||
OpenLayers.Util.extend(style, symbolizer), context);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -194,14 +184,16 @@ OpenLayers.Style = OpenLayers.Class({
|
||||
* Parameters:
|
||||
* style - {Object} style to create literals for. Will be modified
|
||||
* inline.
|
||||
* feature - {<OpenLayers.Feature.Vector>} feature to take properties from
|
||||
* context - {Object} context to take property values from. Defaults to
|
||||
* feature.attributes (or feature.data, if attributes are not
|
||||
* available)
|
||||
*
|
||||
* Returns;
|
||||
* {Object} the modified style
|
||||
*/
|
||||
createLiterals: function(style, feature) {
|
||||
createLiterals: function(style, context) {
|
||||
for (var i in this.propertyStyles) {
|
||||
style[i] = OpenLayers.Style.createLiteral(style[i], feature);
|
||||
style[i] = OpenLayers.Style.createLiteral(style[i], context);
|
||||
}
|
||||
return style;
|
||||
},
|
||||
@@ -302,17 +294,16 @@ OpenLayers.Style = OpenLayers.Class({
|
||||
* "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.
|
||||
* feature {<OpenLayers.Feature>} feature to take attribute values from
|
||||
* context {Object} context to take attribute values from
|
||||
*
|
||||
* 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, feature) {
|
||||
OpenLayers.Style.createLiteral = function(value, context) {
|
||||
if (typeof value == "string" && value.indexOf("${") != -1) {
|
||||
var attributes = feature.attributes || feature.data;
|
||||
value = OpenLayers.String.format(value, attributes)
|
||||
value = OpenLayers.String.format(value, context)
|
||||
value = isNaN(value) ? value : parseFloat(value);
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
var rule = new OpenLayers.Rule.Logical();
|
||||
rule.destroy();
|
||||
t.eq(rule.children, null, "children array nulled properly");
|
||||
t.eq(rule.rules, null, "rules array nulled properly");
|
||||
}
|
||||
|
||||
function test_Logical_evaluate(t) {
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
var rule = new OpenLayers.Rule.Logical({
|
||||
type: OpenLayers.Rule.Logical.NOT});
|
||||
rule.children.push(new OpenLayers.Rule());
|
||||
rule.rules.push(new OpenLayers.Rule());
|
||||
|
||||
var feature = new OpenLayers.Feature.Vector();
|
||||
|
||||
|
||||
@@ -111,8 +111,23 @@
|
||||
t.eq(r.id, elseRule.id, "(else) applySymbolizer called with correct rule");
|
||||
}
|
||||
style.createStyle(new OpenLayers.Feature.Vector());
|
||||
}
|
||||
|
||||
|
||||
function test_Style_context(t) {
|
||||
t.plan(1);
|
||||
var context = {
|
||||
foo: "bar",
|
||||
size: 10};
|
||||
var rule = new OpenLayers.Rule.Comparison({
|
||||
type: OpenLayers.Rule.Comparison.LESS_THAN,
|
||||
context: context,
|
||||
property: "size",
|
||||
value: 11,
|
||||
symbolizer: {"Point": {externalGraphic: "${foo}.png"}}});
|
||||
var style = new OpenLayers.Style();
|
||||
style.addRules([rule]);
|
||||
var styleHash = style.createStyle(new OpenLayers.Feature.Vector());
|
||||
t.eq(styleHash.externalGraphic, "bar.png", "correctly evaluated rule against a custom context");
|
||||
}
|
||||
|
||||
function test_Style_destroy(t) {
|
||||
|
||||
Reference in New Issue
Block a user