Dwins’s Weblog


GeoServer CSS – Conversion

Posted in Cool Stuff,Development,Open Source Software by dwins on July 25, 2010

When I first posted about GeoServer CSS, I was planning to follow up more frequently, but over the past couple of weekends I’ve been distracted by implementing some new features (integration with the Scala variant of GeoScriptstyled marks) as well as getting  away from the keyboard a bit.  This past week, however, I’ve been working on some speed improvements and I was thinking it would be nice to blog about those. Unfortunately, I haven’t explained what the conversion process is doing in the first place, so any discussion of performance would be a bit premature.  I suppose that means I should start at the beginning.

Last time I blogged about this, I said a bit about the parser that takes in a CSS file and breaks it up into objects that the Scala code inside GeoServer CSS can manipulate more easily.  The conversion process I’m talking about today disassembles and reassembles those rules into style components from the GeoTools API, and then you have a style that you can pass to GeoTools’s style serializer or straight to the renderer or whatever you like. Or, more graphically (ASCII art, woo):

 [CSS File] -- parser --> [CSS Objects] -- translator --> [SLD Objects]

The parser isn’t incredibly fast, but the real bottleneck is the translator.  Part of the point of the CSS converter in the first place is that CSS uses a very different model than SLD for combining rules: SLD uses the so-called Painter’s model, where each rule is applied in its entirety to any feature that matches its filter, and if multiple style rules apply to the same feature then they are drawn on top of each other.  CSS on the other hand, allows rules to override properties from other, less specific rules. The way I came up with to deal with this impedance mismatch was to inspect the rules, and produce one SLD rule for each possible combination of CSS rules.  For example, starting with this CSS source:

[a > 1] {
   fill: blue;
} 
[b < 2] {
   stroke: yellow;
}

We produce 3 SLD rules – one for the case where [a > 1 AND NOT b <2], one for [a > 1 AND B < 2], and one for [NOT A > 1 AND B < 2].  The fourth combination, with both rules negated, is a valid combination, but we throw it out since there are no styling attributes for that combination.  In general, there are (2^n – 1) potential SLD rules for a stylesheet containing n CSS rules.  That’s a lot; for only 10 input rules the output stylesheet could contain over 1000 rules.  I say “could” because there are some optimizations that can help to avoid enumerating all these possibilities… but as I said before, I’ll leave that for another post. After combining all these style rules, we still need to re-encode them as SLD.  Even this step is not straightforward as the CSS module unifies certain “contextual” filters with the SLD Filter concept, and also uses z-indexing instead of SLD’s “stack of featuretypes” model for controlling rendering order.

So, when a CSS rule combination is applicable to multiple featuretypes, or is applicable in multiple zoom ranges, or has symbolizers at multiple z-indexes, the equivalent SLD style will have multiple Rules corresponding to that single combination of CSS Rules.  So, we have to inspect the selectors to figure out the right set of SLD rules to produce.  In pseudocode:

featuretypes = extract_featuretypes(rule.selectors)
symbolizers_per_z_index = split_by_zindex(rule.symbolizers)
scale_ranges = extract_scale_ranges(rule.selectors)
for (ft in featuretypes)
    for (z, symbolizers in symbolizers_per_z_index)
        ftstyle = create_featuretypestyle(ft)
        for (range in scale_ranges)
             ftstyle += create_sld_rule(range, rule.selectors, symbolizers)
    output += ftstyle

Then, on top of that, we need to keep the total number of ftstyles produced as low as possible since each one requires an extra buffer at render time.  The code in Translator.css2sld does something similar, but shuffling all the rule combinations into one SLD style as it goes. So, clearly a lot going on here.  Sometime soon I’ll talk a bit about some of the things I’ve done to keep the conversion time under control (and for power users, how to make your styles friendly to the converter).