Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I'm developing on a OpenLayers application which creates dynamic rules for a vector layer using the geostats library. I want to create these rules inside a simple loop through the number of classes. I'm pushing every rule to a array and add this array via stale.addRules(). In my opinion every rule has to be cloned before adding it to the array. The problem is, I am using a OpenLayers.Filter.Function in which the clone() function is not implemented yet (OpenLayers 2.12-rc4). How can I solve this?

At the moment, only the last rule gets visualized. It seems like the other rules get lost, due to the reference value issue...

My code looks like this:

for (var i = 0; i < numClasses; i++) {
    filter_x = new OpenLayers.Filter.Function({
            evaluate: function(attributes) {
                [...]
            }
        })
    var rule_x = new OpenLayers.Rule({
        filter: filter_x,
        symbolizer: { fillColor: colors[i],
                    fillOpacity: 0.5, strokeColor: "white"}
    });
    //var clone = filter_x.clone(); //returns null! clone is not implemented
    rules.push(rule_x)

}
style.addRules(rules);

Thank you for your help!

share|improve this question
add comment

2 Answers

up vote 1 down vote accepted

I think you hit a very common problem of imperative languages that have first-class functions as functional languages do but lack referential transparency. The result are (as a functional programmer would call it) broken closures. I hit this problem in Python some years ago, too and is was quite frustrating. Some weeks ago, this blog post hit my Google+ stream discussing the same issue.

As a sample, take the following code (please excuse I'm not demonstrating it using OpenLayers since I know nothing about OpenLayers and nearly nothing about JavaScript (so excuse the possible use of JS anti-patterns))

function foo_broken() {
    var xs = [];

    for (var i = 0; i < 10; i++) {
        var bar = function() { return i; }
        xs.push(bar);
    }

    return xs;
}

function foo_working() {
    var xs = [];

    for (var i = 0; i < 10; i++) {
        var bar = function(t) { return function() { return t; } };
        xs.push(bar(i));
    }

    return xs;
}

I think most users would expect foo_broken() and foo_working() to do exact the same thing, but no, not in JavaScript. The test code below alerts 10,10,10,10,10,10,10,10,10,10 when evaluating the list returned by foo_broken() and 0,1,2,3,4,5,6,7,8,9 for foo_working().

The problem is that the closure for the anonymous function assigned to bar does not contain the current value of i but a reference to the variable i. That's the reason it's always the last value assigned to that variable. It's IMHO broken but it's like JavaScript, Python 2 and some other languages do it :-(.

The pattern to fight this issue I always use is to define another function which takes as many arguments as the values I'd like to force. In the demo above it's only one parameter (t) because I simply want the value of i. So to force the evaluation of variable i I pass it as a parameter to the anonymous function inside the loop and so the JavaScript interpreter is forced to evaluate it inside the loop.

function alert_values(fun) {
    var bars = fun();
    value_strs = "";
    for (var i = 0; i < 10; i++) {
        value_strs += "" + bars[i]() + ",";
    }
    alert(value_strs);
}

alert_values(foo_broken);
alert_values(foo_working);
share|improve this answer
    
You definitely identified the problem of my code! Thank you for the detailed explanations. It is up and running now. ;) –  rjw May 29 '12 at 20:00
add comment

i dont know whether this works or not, but you can try to apply other filter functions' clone object to OpenLayers.Filter.Function or you can write your own clone method for this function...

the following codes are examples and not tested yet.

clone: function() {
       return OpenLayers.Util.extend(new OpenLayers.Filter.Function(), this);

or

clone: function() {
        var params= [];
        for(var i=0, len=this.params.length; i<len; ++i) {
            params.push(this.params[i].clone());
        }
        return new OpenLayers.Filter.Function({
            name: this.name,
            params: filters
        });
    }

i hope it helps you...

share|improve this answer
    
Thank you for your help! I was successful in cloning the filter and the rule, but it did not solve the problem.After some passes with Firebug, I recognized that the evaluate()-function of the filters of the rules in the rules array reference to my function above. But the definition of this function changes with every loop cycle. Is there another or better way to use a changing OpenLayers.Filter.Function in a loop? –  rjw May 28 '12 at 13:52
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.