From OpenStreetMap Wiki
< Halcyon(Redirected from MapCSS/Implementation)
Jump to: navigation, search

Details of how Halcyon parses a MapCSS file. You can see the full code at http://trac.openstreetmap.org/browser/applications/editors/potlatch2/net/systemeD/halcyon/styleparser .


The actual .css file is parsed on initial load by the MapCSS parser class, which mostly uses regular expressions.

This creates a list of StyleChoosers.


Each StyleChooser is equivalent to a CSS selector plus declaration. So

   way[highway=primary] { color: red; width: 5; }

is one StyleChooser.


The selector (way[highway=primary]) is called a Rule.

A Rule is a list of conditions (e.g. 'highway=primary', 'oneway=true'), a zoom range (e.g. 13-19), and a subject - i.e. 'node', 'way', or 'relation'.


The declaration ( { color: red; width: 5; } ) is called a Style.

A Style can be a PointStyle, a ShapeStyle, a TextStyle, an InstructionStyle (rare) or a ShieldStyle (not yet supported). Each one's properties are directly equivalent to CSS properties, so a ShapeStyle has width, color, opacity, etc.; a TextStyle has font_family, font_size, text_color etc.; and so on.

So, for each object, Halcyon just runs each StyleChooser. If the Rule evaluates true, the Style is applied.

Complex StyleChoosers

I'm actually simplifying a bit above. :)

A single StyleChooser can have more than one selector:

   way[highway=primary], way[highway=trunk] { width: 5; }

would apply to both primary and trunk roads.

It can also have nested items, e.g. "node within way" or "way within relation":

   relation[network=ncn] way { width: 5; }
   way[highway] node[barrier=gate] { icon-image: gate.png; }

This is documented at the start of StyleChooser.as if you want to see more.

The other major thing to consider is layer parsing, for the OSM layer tag and the MapCSS z-index property (the latter principally used to make sure primary roads render above secondary roads, and so on).


Obviously performance is absolutely crucial to an app like Potlatch. Rendering time should be invisible in an editor - it's not the primary purpose of the app, and users will be annoyed if all the 'shininess' is getting in the way of their work. Fortunately, Halcyon has proved very fast in practice, and it's the actual drawing that takes the time - in particular, slow operations like dashed lines and text-on-a-path - rather than running through the rules.

That aside, there's a handful of things I can think of to speed it up. 80% of OSM data, I'm guessing, uses 20% of tags; the other 20% of data uses the other 80% of tags. So you could 'cache' the most common tag combinations to save running through all the rules.

The two biggest optimisations are more philosophical.

I started from the point that "CSS is good"; it's a clear grammar in itself, and it's widely understood. CSS is a benefit for any rendering project, and ideally such projects should share the same vocabulary. In other words, don't feel ashamed about implementing MapCSS but only with a part of the spec. You could say, for example, that you wouldn't 'merge' rules. In Halcyon you can do:

   way[highway=primary] { color: red; }
   way[ref=~/^A/] { width: 5; }

which will combine to give a single red, 5px-wide ShapeStyle for "highway=primary, ref=A4095". Obviously there's a performance hit in merging the styles. So if you thought performance was more important, you could miss that out; force people to write discrete rules for each case.

Also, the stylesheet is within the user's control. If the user has a fast computer and wants to write a complex stylesheet, that's great. If the user has a slower computer, they may choose to write a simpler stylesheet. I'm happy with giving them that choice. Potlatch 2 will have at least two built-in stylesheets: one full, Mapnik-like rendering, and a second, simple wireframe one like current Potlatch. (MapCSS does give users the option to add their own optimisations with the 'exit' directive, i.e. "don't process any more StyleChoosers for this object".)

Finally, of course, there's Moore's Law. One of the reasons I wrote a better renderer for Potlatch 2 is that it runs under a faster virtual machine (the ActionScript 3 VM is much more efficient than AS1), and in turn, computers are faster now than when I wrote the very first Potlatch.