cascading.rst 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. .. _css_cascading:
  2. Understanding Cascading in CSS
  3. ==============================
  4. Cascading Style Sheets are the styling language of the web, use a simple syntax, but sometimes their
  5. simplicity can be deceitful if the writer is not aware of how the "Cascading" part of it works.
  6. The confusion might become greater by looking at the translated SLD, and wondering how all the SLD
  7. rules came to be from a much smaller set of CSS rules.
  8. This document tries to clarify how cascading works, how it can be controlled in SLD translation,
  9. and for those that would prefer simpler, if more verbose, styles, shows how to turn cascading off for good.
  10. CSS rules application
  11. ---------------------
  12. Given a certain feature, how are CSS rules applied to it? This is roughly the algorithm:
  13. * Locate all rules whose selector matches the current feature
  14. * Sort them by specificity, less specific to more specific
  15. * Have more specific rules add to and override properties set in less specific rules
  16. As you can see, depending on the feature attributes a new rule is built by the above algorithm, mixing all
  17. the applicable rules for that feature.
  18. The core of the algorithm allows to prepare rather succinct style sheets for otherwise very complex rule sets,
  19. by setting the common bits in less specific rules, and override them specifying the exceptions to the norm
  20. in more specific rules.
  21. Understanding specificity
  22. -------------------------
  23. In web pages CSS `specificity <http://www.w3.org/TR/CSS21/cascade.html#specificity>`_ is setup as a tuple of four numbers called a,b,c,d:
  24. * ``a``: set to 1 if the style is local to an element, that is, defined in the element ``style`` attribute
  25. * ``b``: counts the number of ID attributes in the selector
  26. * ``c``: count the number of other attributes and pseudo classes in the selector
  27. * ``d``: count the number of element names or pseudo elements in the selector
  28. ``a`` is more important than ``b``, which is more important than ``c``, and so on, so for example, if one rule has ``a=1`` and then second has ``a=0``, the first
  29. is more specific, regardless of what values have ``b``, ``c`` and ``d``.
  30. Here are some examples from the CSS specification, from less specific to more specific:
  31. .. code-block:: css
  32. * {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
  33. li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
  34. li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
  35. ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
  36. ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
  37. h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
  38. ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
  39. li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
  40. #x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
  41. style="..." /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
  42. In cartographic CSS there are no HTML elements that could have a local style, so ``a`` is always zero.
  43. The others are calculated as follows:
  44. * ``b``: number of feature ids in the rule
  45. * ``c``: number of attributes in CQL filters and pseudo-classes (e.g., ``:mark``) used in the selector
  46. * ``d``: 1 if a typename is specified, 0 otherwise
  47. Here are some examples, from less to more specific:
  48. .. code-block:: css
  49. * {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
  50. topp:states {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
  51. :mark {} /* a=0 b=0 c=1 d=0 -> specificity = 0,0,1,0 */
  52. [a = 1 and b > 10] {} /* a=0 b=0 c=1 d=0 -> specificity = 0,0,2,0 */
  53. #states.1 {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
  54. In case two rules have the same specificity, the last one in the document wins.
  55. Understanding CSS to SLD translation in cascading mode
  56. ------------------------------------------------------
  57. As discussed above, CSS rule application can potentially generate a different rule for each
  58. feature, depending on its attributes and how they get matched by the various CSS selectors.
  59. SLD on the other hand starts from the rules, and applies all of them, in turn, to each feature,
  60. painting each matching rule. The two evaluation modes are quite different, in order to turn
  61. CSS into SLD the translator has to generate every possible CSS rule combination, while making
  62. sure the generated SLD rules are mutually exclusive (CSS generated a single rule for a given
  63. feature in the end).
  64. The combination of all rules is called a `power set <https://en.wikipedia.org/wiki/Power_set>`_, and the exclusivity is guaranteed by
  65. negating the filters of all previously generated SLD rules and adding to the current one.
  66. As one might imagine, this would result in a lot of rules, with very complex filters.
  67. The translator addresses the above concerns by applying a few basic strategies:
  68. * The generated filters are evaluated in memory, if the filter is found to be "impossible", that is, something that
  69. could never match an exiting feature, the associated rule is not emitted (e.g., ``a = 1 and a = 2`` or ``a = 1 and not(a = 1)``)
  70. * The generated SLD has a vendor option ``<sld:VendorOption name="ruleEvaluation">first</sld:VendorOption>`` which forces
  71. the renderer to give up evaluating further rules once one of them actually matched a feature
  72. The above is nice and sufficient in theory, while in practice it can break down with very complex CSS styles
  73. having a number of orthogonal selectors (e.g., 10 rules controlling the fill on the values of attribute ``a`` and
  74. 10 rules controlling the stroke on values of attribute ``b``, and another 10 rules controlling the opacity of fill and stroke based on attribute ``c``,
  75. resulting in 1000 possible combinations).
  76. For this reason by default the translator will try to generated simplified and fully exclusive
  77. rules only if the set of rules is "small", and will instead generate the full power set
  78. otherwise, to avoid incurring in a CSS to SLD translation time of minutes if not hours.
  79. The translation modes are controlled by the ``@mode`` directive, with the following values:
  80. * ``'Exclusive'``: translate the style sheet in a minimum set of SLD rules with simplified selectors, taking whatever time and memory required
  81. * ``'Simple'``: just generated the power set without trying to build a minimum style sheet, ensuring the translation is fast, even if the resulting SLD might look very complex
  82. * ``'Auto'``: this is the default value, it will perform the power set expansion, and then will proceed in ``Exclusive`` mode if the power set contains less than 100 derived rules, or in ``Simple`` mode otherwise. The rule count threshold can be manually controlled by using the ``@autoThreshold`` directive.
  83. The Flat translation mode
  84. -------------------------
  85. The ``@mode`` directive has one last possible value, ``Flat``, which enables a flat translation mode in
  86. which specificity and cascading are not applied.
  87. In this mode the CSS will be translated almost 1:1 into a corresponding SLD, each CSS rule producing and equivalent SLD rule,
  88. with the exception of the rules with pseudo-classes specifying how to stroke/fill marks and symbols in general.
  89. Care should be taken when writing rules with pseudo classes, they will be taken into consideration only
  90. if their selector matches the one of the preceding rule. Consider this example:
  91. .. code-block:: css
  92. @mode "Flat";
  93. [type = 'Capital'] {
  94. mark: symbol(circle);
  95. }
  96. [type = 'Capital'] :mark {
  97. fill: white;
  98. size: 6px;
  99. }
  100. :mark {
  101. stroke: black;
  102. stroke-width: 2px;
  103. }
  104. In the above example, the first rule with the ``:mark`` pseudo class will be taken into consideration and
  105. merged with the capital one, the second one instead will be ignored. The resulting SLD will thus not
  106. contain any stroke specification for the 'circle' mark:
  107. .. code-block:: xml
  108. <?xml version="1.0" encoding="UTF-8"?><sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld"
  109. xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc"
  110. xmlns:gml="http://www.opengis.net/gml" version="1.0.0">
  111. <sld:NamedLayer>
  112. <sld:Name/>
  113. <sld:UserStyle>
  114. <sld:Name>Default Styler</sld:Name>
  115. <sld:FeatureTypeStyle>
  116. <sld:Rule>
  117. <ogc:Filter>
  118. <ogc:PropertyIsEqualTo>
  119. <ogc:PropertyName>type</ogc:PropertyName>
  120. <ogc:Literal>Capital</ogc:Literal>
  121. </ogc:PropertyIsEqualTo>
  122. </ogc:Filter>
  123. <sld:PointSymbolizer>
  124. <sld:Graphic>
  125. <sld:Mark>
  126. <sld:WellKnownName>circle</sld:WellKnownName>
  127. <sld:Fill>
  128. <sld:CssParameter name="fill">#ffffff</sld:CssParameter>
  129. </sld:Fill>
  130. </sld:Mark>
  131. <sld:Size>6</sld:Size>
  132. </sld:Graphic>
  133. </sld:PointSymbolizer>
  134. </sld:Rule>
  135. </sld:FeatureTypeStyle>
  136. </sld:UserStyle>
  137. </sld:NamedLayer>
  138. </sld:StyledLayerDescriptor>
  139. The advantages of flat mode are:
  140. * Easy to understand, the rules are applied in the order they are written
  141. * Legend control, the generated legend contains no surprises as rules are not mixed together and are not reordered
  142. The main disadvantage is that there is no more a way to share common styling bits in general rules, all common bits have to be
  143. repeated in all rules.
  144. .. note:: In the future we hope to add the ability to nest rules, which is going to address some of the limitations of flat mode without introducing the most complex bits of the standard cascading mode
  145. Comparing cascading vs flat modes, an example
  146. ---------------------------------------------
  147. Consider the following CSS:
  148. .. code-block:: css
  149. * { stroke: black; stroke-width: 10 }
  150. [cat = 'important'] { stroke: yellow; }
  151. If the above style is translated in cascading mode, it will generate two mutually exclusive SLD rules:
  152. * One applying a 10px wide yellow stroke on all features whose cat attribute is 'important'
  153. * One applying a 10px wide black stroke on all feature whose cat attribute is not 'important'
  154. Thus, each feature will be painted by a single line, either yellow or black.
  155. If instead the style contains a ``@mode 'Flat'`` directive at the top, it will generated two non mutually exclusive SLD rules:
  156. * One applying a 10px wide black stroke on all features
  157. * One applying a 1px wide yewllow stroke on all feature whose cat attribute is 'important'
  158. Thus, all features will at least be painted 10px black, but the 'important' ones will also have a second 1px yellow line *on top of the first one*