OpenLayers OpenLayers

Ticket #1531: 1531-r7137-B0.patch

File 1531-r7137-B0.patch, 20.7 kB (added by ahocevar, 8 months ago)
  • tests/Format/SLD/v1_0_0.html

    old new  
    1515                '<Name>AAA161</Name>' +  
    1616                '<UserStyle>' +  
    1717                    '<FeatureTypeStyle>' +  
     18                        '<SemanticTypeIdentifier>generic:geometry</SemanticTypeIdentifier>' + 
    1819                        '<Rule>' +  
    1920                            '<Name>stortsteen</Name>' +  
    2021                            '<ogc:Filter>' + 
  • tests/StyleMap.html

    old new  
    3636        t.ok(!styleMap.styles, "StyleMap styles successfully destroyed"); 
    3737    } 
    3838     
     39    function test_StyleMap_makeGeometryTypeAware(t) { 
     40        t.plan(7); 
     41        var styleMap = new OpenLayers.StyleMap({"Point": "foo", "Line": "bar"}); 
     42        styleMap.styles["default"].addRules([new OpenLayers.Rule({symbolizer: { 
     43            "Polygon": "foobar"}})]); 
     44        styleMap.makeGeometryTypeAware(); 
     45        t.eq(styleMap.styles["default"].rules.length, 3, "correct number of rules created"); 
     46        t.eq(styleMap.styles["default"].rules[0].symbolizer, "foo", "assigned point symbolizer from defaultStyle correctly to point rule"); 
     47        t.eq(styleMap.styles["default"].rules[0].geometryTypes[0], "generic:point", "assign geometry type correctly to point rule"); 
     48        t.eq(styleMap.styles["default"].rules[1].symbolizer, "bar", "assigned line symbolizer from defaultStyle correctly to point rule"); 
     49        t.eq(styleMap.styles["default"].rules[1].geometryTypes[0], "generic:line", "assign geometry type correctly to line rule"); 
     50        t.eq(styleMap.styles["default"].rules[2].symbolizer, "foobar", "assigned polygon symbolizer correctly to polygon rule"); 
     51        t.eq(styleMap.styles["default"].rules[2].geometryTypes[0], "generic:polygon", "assign geometry type correctly to polygon rule"); 
     52    } 
     53     
    3954    </script>  
    4055</head>  
    4156<body>  
  • tests/Rule.html

    old new  
    2121        rule.destroy(); 
    2222        t.eq(rule.symbolizer, null, "symbolizer hash nulled properly"); 
    2323    } 
     24     
     25    function test_Rule_evaluate(t) { 
     26        t.plan(2); 
     27         
     28        var rule = new OpenLayers.Rule(); 
     29        rule.geometryTypes = ["generic:line"]; 
    2430 
     31        var feature = {geometry: new OpenLayers.Geometry.MultiLineString()}; 
     32        t.eq(rule.evaluate(feature), true, "Rule correctly evaluated for line feature"); 
     33 
     34        feature.geometry = new OpenLayers.Geometry.Point(); 
     35        t.eq(rule.evaluate(feature), false, "Rule correctly evaluated for point feature"); 
     36    } 
     37 
    2538    </script>  
    2639</head>  
    2740<body>  
  • tests/Style.html

    old new  
    102102    } 
    103103     
    104104    function test_Style_createSymbolizer(t) { 
    105         t.plan(2); 
     105        t.plan(4); 
    106106        var style = new OpenLayers.Style(); 
    107107        var rule = new OpenLayers.Rule({ 
    108             id: Math.random() 
     108            symbolizer: { 
     109                id: Math.random() 
     110            } 
    109111        }); 
    110112        var elseRule = new OpenLayers.Rule({ 
    111             id: Math.random(), 
     113            symbolizer: { 
     114                id: Math.random() 
     115            }, 
    112116            elseFilter: true 
    113117        }); 
    114118        style.addRules([rule, elseRule]); 
    115119 
    116120        // test that applySymbolizer is only called with rule 
    117         style.applySymbolizer = function(r) { 
    118             t.eq(r.id, rule.id, "(plain) applySymbolizer called with correct rule"); 
     121        style.applySymbolizer = function(m, s) { 
     122            t.eq(s.id, rule.symbolizer.id, "(plain) applySymbolizer called with correct rule"); 
     123            return s; 
    119124        } 
     125        style.defaultStyle.id = rule.symbolizer.id; 
    120126        style.createSymbolizer(new OpenLayers.Feature.Vector()); 
    121127 
    122128        rule.evaluate = function() {return false;}; 
    123         style.applySymbolizer = function(r) { 
    124             t.eq(r.id, elseRule.id, "(else) applySymbolizer called with correct rule"); 
     129        style.applySymbolizer = function(m, s) { 
     130            t.eq(s.id, elseRule.symbolizer.id, "(else) applySymbolizer called with correct rule"); 
     131            return s; 
    125132        } 
     133        style.defaultStyle.id = elseRule.symbolizer.id; 
    126134        style.createSymbolizer(new OpenLayers.Feature.Vector()); 
    127135    } 
    128136     
  • lib/OpenLayers/Format/SLD/v1.js

    old new  
    137137            }, 
    138138            "FeatureTypeStyle": function(node, style) { 
    139139                // OpenLayers doesn't have a place for FeatureTypeStyle 
    140                 // Name, Title, Abstract, FeatureTypeName, or 
    141                 // SemanticTypeIdentifier so, we make a temporary object 
    142                 // and later just use the Rule(s). 
     140                // Name, Title, Abstract, or FeatureTypeName, so we make 
     141                // a temporary object and later just use the Rule(s), which 
     142                // will hold the SemanticTypeIdentifier from the 
     143                // FeatureTypeStyle. 
    143144                var obj = { 
     145                    semanticTypeIdentifiers: [], 
    144146                    rules: [] 
    145147                }; 
    146148                this.readChildNodes(node, obj); 
    147149                style.rules = obj.rules; 
    148150            }, 
     151            "SemanticTypeIdentifier": function(node, obj) { 
     152                obj.semanticTypeIdentifiers.push(this.getChildValue(node)); 
     153            }, 
    149154            "Rule": function(node, obj) { 
    150155                var rule = new OpenLayers.Rule(); 
    151156                this.readChildNodes(node, rule); 
     157                rule.geometryTypes = obj.semanticTypeIdentifiers; 
    152158                obj.rules.push(rule); 
    153159            }, 
    154160            "ElseFilter": function(node, rule) { 
     
    602608                } 
    603609                 
    604610                // add FeatureTypeStyles 
    605                 this.writeNode(node, "FeatureTypeStyle", style); 
     611                // create one FeatureTypeStyle for each unique set of  
     612                // SemanticTypeIdentifiers found in our rules 
     613                var featureTypeRules = {}; 
     614                var rule, geometryType; 
     615                for(var i=0; i<style.rules.length; ++i) { 
     616                    rule = style.rules[i]; 
     617                    geometryType = rule.geometryTypes.join(","); 
     618                    if (!geometryType) { 
     619                        geometryType = "ol:undefined"; 
     620                    } 
     621                    if (!featureTypeRules[geometryType]) { 
     622                        featureTypeRules[geometryType] = [rule]; 
     623                    } else { 
     624                        featureTypeRules[geometryType].push(rule); 
     625                    } 
     626                } 
     627                var rules; 
     628                for (var i in featureTypeRules) { 
     629                    rules = featureTypeRules[i]; 
     630                    this.writeNode(node, "FeatureTypeStyle", { 
     631                        rules: rules, 
     632                        semanticTypeIdentifiers: rules[0].geometryTypes 
     633                    }); 
     634                } 
    606635                 
    607636                return node; 
    608637            }, 
     
    611640                    "IsDefault", {value: (bool) ? "1" : "0"} 
    612641                ); 
    613642            }, 
    614             "FeatureTypeStyle": function(style) { 
     643            "FeatureTypeStyle": function(obj) { 
     644                var rules = obj.rules; 
     645                var semanticTypeIdentifiers = 
     646                        obj.semanticTypeIdentifiers; 
     647                 
    615648                var node = this.createElementNSPlus("FeatureTypeStyle"); 
    616649                 
    617650                // OpenLayers currently stores no Name, Title, Abstract, 
    618651                // FeatureTypeName, or SemanticTypeIdentifier information 
    619                 // related to FeatureTypeStyle 
     652                // related to FeatureTypeStyle. So we create one 
     653                // FeatureTypeStyle for each unique set of SemanticTypeIdentifiers 
     654                // (i.e. geometryType) found inside our rules 
    620655                 
     656                for (var i=0; i<semanticTypeIdentifiers.length; ++i) { 
     657                    this.writeNode(node, "SemanticTypeIdentifier", 
     658                            semanticTypeIdentifiers[i]); 
     659                } 
     660                 
    621661                // add in rules 
    622                 for(var i=0; i<style.rules.length; ++i) { 
    623                     this.writeNode(node, "Rule", style.rules[i]); 
     662                for (var i=0; i<rules.length; ++i) { 
     663                    this.writeNode(node, "Rule", rules[i]); 
    624664                } 
    625665                 
    626666                return node; 
    627667            }, 
     668            "SemanticTypeIdentifier": function(identifier) { 
     669                return this.createElementNSPlus( 
     670                    "SemanticTypeIdentifier", {value: identifier}); 
     671            }, 
    628672            "Rule": function(rule) { 
    629673                var node = this.createElementNSPlus("Rule"); 
    630674 
  • lib/OpenLayers/StyleMap.js

    old new  
    144144            })); 
    145145        } 
    146146        this.styles[renderIntent].addRules(rules); 
     147        return this; 
    147148    }, 
     149     
     150    /** 
     151     * Method: makeGeometryTypeAware 
     152     * Convenience method that converts a rule with a symbolizer hash 
     153     * keyed by geometry types for which the symbolizers are intended, to 
     154     * geometry type aware rules. In SLD, a rule with different symbolizers 
     155     * for point, line and polygon means that the feature will be rendered as 
     156     * point, line and polygon, independent of its original feature type. 
     157     * This allows us to e.g. render a point symbol at the centroid point of a 
     158     * polygon. People sometimes define symbolizers like that, hoping that 
     159     * this means if we have a point, line or polygon feature, only the point, 
     160     * line or polygon symbolizer will be applied. This method will convert 
     161     * the whole style map so that rules with such symbolizers become a set of 
     162     * rules with an according {<OpenLayers.Rule.geometryType>}.  
     163     *  
     164     * Parameters: 
     165     * mapping - {Object} A hash that maps the geometry type keys found in 
     166     *     the symbolizer hash to SemanticTypeIdentifier compliant geometry 
     167     *     type identifiers. If not specified, the following default mapping 
     168     *     will be used: 
     169     *     (code) 
     170     *     { 
     171     *        "Point":   ["generic:point"], 
     172     *        "Line":    ["generic:line"], 
     173     *        "Polygon": ["generic:polygon"] 
     174     *     } 
     175     *     (end) 
     176     *  
     177     * Returns: 
     178     * {<OpenLayers.StyleMap>} the modified style map (which will also be 
     179     *     modified inline). 
     180     */ 
     181    makeGeometryTypeAware: function(mapping) { 
     182        if (!mapping) { 
     183            mapping = { 
     184                "Point":   ["generic:point"], 
     185                "Line":    ["generic:line"], 
     186                "Polygon": ["generic:polygon"] 
     187            } 
     188        } 
     189         
     190        var modified; 
     191        var defaultRule, style, rules, rule, symbolizer; 
     192        for (var s in this.styles) { 
     193            style = this.styles[s]; 
     194            rules = style.rules || []; 
     195            defaultRule = new OpenLayers.Rule( 
     196                {symbolizer: style.defaultStyle}); 
     197            rules.push(defaultRule); 
     198            for (var i=rules.length-1; i>=0; --i) { 
     199                rule = rules[i]; 
     200                for (var m in mapping) { 
     201                    symbolizer = rule.symbolizer[m]; 
     202                    modified = false; 
     203                    if (symbolizer) { 
     204                        rules.push(OpenLayers.Util.applyDefaults({ 
     205                            id: OpenLayers.Util.createUniqueID( 
     206                                    rule.CLASS_NAME + "_"), 
     207                            symbolizer: symbolizer, 
     208                            geometryTypes: mapping[m] 
     209                        }, rule)); 
     210                        modified = true; 
     211                    } 
     212                } 
     213                if (modified == true || rule == defaultRule) { 
     214                    OpenLayers.Util.removeItem(rules, rule); 
     215                } 
     216            } 
     217        } 
     218        return this; 
     219    }, 
    148220 
    149221    CLASS_NAME: "OpenLayers.StyleMap" 
    150222}); 
  • lib/OpenLayers/Rule.js

    old new  
    6666     * Property: symbolizer 
    6767     * {Object} Symbolizer or hash of symbolizers for this rule. If hash of 
    6868     * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The 
    69      * latter if useful if it is required to style e.g. vertices of a line 
    70      * with a point symbolizer. Note, however, that this is not implemented 
    71      * yet in OpenLayers, but it is the way how symbolizers are defined in 
    72      * SLD
     69     * latter if useful if it is required to style e.g. the centroid of a 
     70     * polygon with a point symbolizer. Note, however, that this is not 
     71     * implemented yet in OpenLayers. To specify a rule that only applies 
     72     * to specific geometry types, the <geometryType> property can be used
    7373     */ 
    7474    symbolizer: null, 
    7575     
    7676    /** 
     77     * Property: geometryTypes 
     78     * {Array(String)} If set, the rule will only be applied to feature 
     79     * geometries of a specified geometry type. The values used here are the 
     80     * generic values defined in the SLD spec for the <SemanticTypeIdentifier> 
     81     * property of a FeatureTypeStyle. Supported values are: 
     82     * - "generic:point"  
     83     * - "generic:line" 
     84     * - "generic:polygon" 
     85     */ 
     86    geometryTypes: null, 
     87     
     88    /** 
    7789     * APIProperty: minScaleDenominator 
    7890     * {Number} or {String} minimum scale at which to draw the feature. 
    7991     * In the case of a String, this can be a combination of text and 
     
    103115    initialize: function(options) { 
    104116        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); 
    105117        this.symbolizer = {}; 
     118        this.geometryTypes = []; 
    106119 
    107120        OpenLayers.Util.extend(this, options); 
    108121    }, 
     
    131144     */ 
    132145    evaluate: function(feature) { 
    133146        var context = this.getContext(feature); 
     147        return (this.checkSemanticType(feature) && 
     148                this.checkScaleRange(feature, context) && 
     149                this.checkFilter(feature, context));         
     150    }, 
     151     
     152    /** 
     153     * Method: checkSemanticType 
     154     * Parameters: 
     155     * feature - {<OpenLayers.Feature.Vector>} 
     156     */ 
     157    checkSemanticType: function(feature) { 
     158        var applies = this.geometryTypes.length == 0; 
     159        var semanticType; 
     160        for (var i=0; i<this.geometryTypes.length; ++i) { 
     161            switch(this.geometryTypes[i]) { 
     162                case "generic:point": 
     163                    applies = applies || 
     164                        feature.geometry.CLASS_NAME.indexOf("Point") != -1; 
     165                    break; 
     166                case "generic:line": 
     167                    applies = applies || 
     168                        feature.geometry.CLASS_NAME.indexOf("Line") != -1; 
     169                    break; 
     170                case "generic:polygon": 
     171                    applies = applies || 
     172                        feature.geometry.CLASS_NAME.indexOf("Polygon") != -1; 
     173                    break; 
     174            } 
     175        } 
     176        return applies; 
     177    }, 
     178     
     179    /** 
     180     * Method: checkScaleRange 
     181     * Parameters: 
     182     * feature - {<OpenLayers.Feature.Vector>} 
     183     * context - {Object} 
     184     */ 
     185    checkScaleRange: function(feature, context) { 
    134186        var applies = true; 
    135  
    136187        if (this.minScaleDenominator || this.maxScaleDenominator) { 
    137188            var scale = feature.layer.map.getScale(); 
    138189        } 
     
    146197            applies = scale < OpenLayers.Style.createLiteral( 
    147198                    this.maxScaleDenominator, context); 
    148199        } 
    149          
    150         // check if optional filter applies 
    151         if(applies && this.filter) { 
     200        return applies; 
     201    }, 
     202     
     203    /** 
     204     * Method: checkFilter 
     205     * Parameters: 
     206     * feature - {<OpenLayers.Feature.Vector>} 
     207     * context - {Object} 
     208     */ 
     209    checkFilter: function(feature, context) { 
     210        var applies = true; 
     211        if(this.filter) { 
    152212            // feature id filters get the feature, others get the context 
    153213            if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") { 
    154214                applies = this.filter.evaluate(feature); 
     
    156216                applies = this.filter.evaluate(context); 
    157217            } 
    158218        } 
    159  
    160219        return applies; 
    161220    }, 
    162221     
  • lib/OpenLayers/Style.js

    old new  
    127127     * {Object} symbolizer hash 
    128128     */ 
    129129    createSymbolizer: function(feature) { 
    130         var style = this.createLiterals( 
    131             OpenLayers.Util.extend({}, this.defaultStyle), feature); 
     130        var style = this.applySymbolizer({}, this.defaultStyle, feature); 
    132131         
    133132        var rules = this.rules; 
    134133 
     
    145144                    elseRules.push(rule); 
    146145                } else { 
    147146                    appliedRules = true; 
    148                     this.applySymbolizer(rule, style, feature); 
     147                    this.applySymbolizer(style, rule.symbolizer, feature); 
    149148                } 
    150149            } 
    151150        } 
     
    154153        if(appliedRules == false && elseRules.length > 0) { 
    155154            appliedRules = true; 
    156155            for(var i=0; i<elseRules.length; i++) { 
    157                 this.applySymbolizer(elseRules[i], style, feature); 
     156                this.applySymbolizer(style, elseRules[i].symbolizer, feature); 
    158157            } 
    159158        } 
    160159 
     
    172171     * Method: applySymbolizer 
    173172     * 
    174173     * Parameters: 
    175      * rule - {OpenLayers.Rule
    176      * style - {Object} 
     174     * mergedSymbolizer - {Object
     175     * symbolizer - {Object} 
    177176     * feature - {<OpenLayer.Feature.Vector>} 
    178177     * 
    179178     * Returns: 
    180      * {Object} A style with new symbolizer applied. 
     179     * {Object} The merged symbolizer with a new symbolizer applied. 
    181180     */ 
    182     applySymbolizer: function(rule, style, feature) { 
     181    applySymbolizer: function(mergedSymbolizer, symbolizer, feature) { 
     182        // TBD This is not the behavior as intended by the SLD spec. 
     183        // Currently, we only apply PointSymbolizers to points, 
     184        // LineSymbolizers to lines and so on. In the future, a 
     185        // PointSymbolizer should also be applyable e.g. to a polygon, which 
     186        // means we want to render a point at its centroid point. 
    183187        var symbolizerPrefix = feature.geometry ? 
    184188                this.getSymbolizerPrefix(feature.geometry) : 
    185189                OpenLayers.Style.SYMBOLIZER_PREFIXES[0]; 
    186190 
    187         var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer; 
    188  
    189191        // merge the style with the current style 
     192        // TBD In the future, we should render a separate geometry for each 
     193        // symbolizer. This will make it possible to render e.g. a highway 
     194        // with a narrow black line on top a wide yellow line 
    190195        return this.createLiterals( 
    191                 OpenLayers.Util.extend(style, symbolizer), feature); 
     196                OpenLayers.Util.extend(mergedSymbolizer, 
     197                symbolizer[symbolizerPrefix] || symbolizer), feature); 
    192198    }, 
    193199     
    194200    /** 
  • examples/tasmania/sld-tasmania.xml

    old new  
    1818        <sld:Title>title</sld:Title> 
    1919        <sld:Abstract>abstract</sld:Abstract> 
    2020        <sld:FeatureTypeName>Feature</sld:FeatureTypeName> 
    21         <sld:SemanticTypeIdentifier>generic:geometry</sld:SemanticTypeIdentifier> 
     21        <sld:SemanticTypeIdentifier>generic:polygon</sld:SemanticTypeIdentifier> 
    2222        <sld:Rule> 
    2323          <sld:Name>testRuleName</sld:Name> 
    2424          <sld:Title>title</sld:Title>