Save 37% off PRO during our Black Friday Sale! »

Creating Maps in GeoServer using CSS and SLD - FOSS4G 2021 Edition

Creating Maps in GeoServer using CSS and SLD - FOSS4G 2021 Edition

The presentation aims to provide attendees with enough information to master GeoServer styling documents and most of GeoServer extensions to generate appealing, informative, readable maps that can be quickly rendered on screen. Examples will be provided from the OSM data directory GeoSolutions shared with the community.

Bac74c17d65c22d0ae63915251f7750f?s=128

Simone Giannecchini
PRO

October 04, 2021
Tweet

Transcript

  1. Creating stunning maps with GeoServer With SLD and CSS Ing.

    Andrea Aime GeoSolutions 1
  2. GeoSolutions • Offices in Italy & US, Clients Worldwide •

    30+ collaborators, 25+ Engineers • www.geosolutionsgroup.com • Our products • Our Offer Enterprise Support Services Deployment Subscription Professional Training Customized Solutions GeoNode
  3. Affiliations We strongly support Open Source, it Is in our

    core We actively participate in OGC working groups and get funded to advance new open standards We support standards critical to GEOINT
  4. Quick tour of styling languages 4

  5. Languages SLD 1.0 core SLD 1.1 core YSLD extension MBStyles

    community GeoCSS extension SLD 1.0 inspired object model, with extensions parse parse parse parse and translate parse and translate 5 Map rendering
  6. Shared concepts • Layer (the styles applies to) • Rules

    • Filters/selectors (what should be painted) • Scale dependencies (zoomed in, zoomed out?) • Symbolizers (how should it be painted) • Point • Line • Polygon • Text 6
  7. Styled Layer Descriptor 1.0 and 11 • The only OGC

    styling standard • XML based, verbose, hard to hand edit • Was meant for machine export but ended up being edited a lot by hand • Autocomplete in the style editor • Can be generated by external tools and imported • However interoperability is limited • Often needs to be hand tweaked 7
  8. SLD 1.0 example <!–- Boilerplate beginning ommitted --> … <sld:Rule>

    <ogc:Filter> <ogc:PropertyIsEqualTo> <ogc:PropertyName>type</ogc:PropertyName> <ogc:Literal>alpine_hut</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:Filter> <sld:MaxScaleDenominator>100000.0</sld:MaxScaleDenominator> <sld:PointSymbolizer> <sld:Graphic> <sld:ExternalGraphic> <sld:OnlineResource xlink:type="simple" xlink:href="symbols/alpinehut.p.16.png"/> <sld:Format>image/png</sld:Format> </sld:ExternalGraphic> </sld:Graphic> </sld:PointSymbolizer> </sld:Rule> <!–- Boilerplate end ommitted --> Filter Scale dep. Symbolizer
  9. YSLD • SLD in YAML syntax • Filtering by CQL

    • Can define reusable variables and blocks • Verbosity it’s between SLD and CSS • Has a notion of zoom levels, if needed feature-styles: - rules: - filter: type = 'alpine_hut' scale: (,100000.0) symbolizers: - point: symbols: - external: url: symbols/alpinehut.p.16.png format: image/png 9
  10. GeoCSS • Compact syntax, familiar for web developers • CQL

    based filtering, rule nesting and cascading keeps styling compact • Does not get any more compact than this: • Cons: some are confused by “rule cascading” 🡪 can now be turned off [type = 'alpine_hut'][@sd < 100k] { mark: url('symbols/alpinehut.p.16.png'); } 10
  11. MBStyles (aka MapBox GL) • JSON based, designed for GUI

    editing (like SLD) instead of hand editing • Only Web Mercator zoom level based scale control • Symbols coming from “sprites” (symbol collections) • Unlike others, no styling extensions • No rendering transformations • Limited expressions usage • Pro: can be applied both on the client side and the server side
  12. MBStyles (aka MapBox GL) { "version": 8, … boilerplate omitted

    here "name": "mountain huts" , "sprite": "https://my.sever/sprite" , "layers": [ { "id": "huts", "type": "symbol", "minzoom": 9, "filter": [ "==", "type", "alpine_hut" ], "layout": { "icon-image" : "alpinehut", "icon-size": 1, "icon-allow-overlap" : true } } ] } Filter (in postfix notation!) Scale dep. Symbolizer The sprint contains various images, like in videogames
  13. What’s next • Going to explore styling concepts • Languages

    used in examples • SLD 1.0 + GS extensions • GeoCSS SLD CSS
  14. Scale dependencies 14

  15. Types of Scale dependency • Decide whether to symbolize based

    on the scale or not • E.g., at lower scales/lower zoom levels do not show buildings • Symbolize in a different way depending on the scale • E.g., different thickness based on the current zoom
  16. Expressing scale dependency filters • SLD: • <MinScaleDenominator> 1000 </MinScaleDenominator>

    • <MaxScaleDenominator> 1000000 </MaxScaleDenominator> • CSS • [@sd > 1k][@sd < 1M] Compact expression of large numbers makes them readable at a glance, e.g., 100k, 1M , 10M
  17. Unit of Measure • Useful if you have real world

    measures of line thicknesses and the like … <LineSymbolizer uom="http://www.opengeospatial.org/se/units/metre"> <Stroke> <CssParameter name="stroke">#0000FF</CssParameter> <CssParameter name="stroke-width">5</CssParameter> </Stroke> </LineSymbolizer> ... … stroke: blue; stroke-width: 5m; SLD CSS
  18. Transformation functions • Another way of setting a different value

    depending on the current scale/zoom level • Useful if the scaling is not linear [class = 'highway’ and type in ('motorway’, 'motorway_link’)] [@sd < 25M] { stroke: #e66e89; stroke-width: categorize(@sd, 2, 400k, 1.9, 800k, 1.4, 1.5M, 1, 3M, 0.8, 6M, 0.5); • Less than 400k 🡪 2px • [400k, 800k] 🡪 1.9px • [800k, 1.5M] 🡪 1.4px • [1.5M, 3M] 🡪 1 • [3M, 6M] 🡪 0.8 • Above 6M -> 0.5 CSS
  19. Transformation functions in SLD <sld:LineSymbolizer> <sld:Stroke> <sld:CssParameter name="stroke">#e66e89</sld:CssParameter> <sld:CssParameter name="stroke-width">

    <ogc:Function name="Categorize"> <ogc:Function name="env"> <ogc:Literal>wms_scale_denominator</ogc:Literal> </ogc:Function> <ogc:Literal>2</ogc:Literal> <ogc:Literal>400000</ogc:Literal> <ogc:Literal>1.9</ogc:Literal> <ogc:Literal>800000</ogc:Literal> <ogc:Literal>1.4</ogc:Literal> <ogc:Literal>1500000</ogc:Literal> <ogc:Literal>1</ogc:Literal> <ogc:Literal>3000000</ogc:Literal> <ogc:Literal>0.8</ogc:Literal> <ogc:Literal>6000000</ogc:Literal> <ogc:Literal>0.5</ogc:Literal> </ogc:Function> </sld:CssParameter> </sld:Stroke> </sld:LineSymbolizer> SLD
  20. Point styling 20

  21. Simple image [type = 'alpine_hut'][@sd < 100k] { mark: url('symbols/alpinehut.p.16.png');

    } <sld:Rule> <ogc:Filter> <ogc:PropertyIsEqualTo> <ogc:PropertyName>type</ogc:PropertyName> <ogc:Literal>alpine_hut</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:Filter> <sld:MaxScaleDenominator>100000.0</sld:MaxScaleDenominator> <sld:PointSymbolizer> <sld:Graphic> <sld:ExternalGraphic> <sld:OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="symbols/alpinehut.p.16.png"/> <sld:Format>image/png</sld:Format> </sld:ExternalGraphic> </sld:Graphic> </sld:PointSymbolizer> </sld:Rule> SLD CSS
  22. Marks (SVG in this case) [type = 'bank'][@sd < 6k]

    { mark: symbol('file://symbols/bank.svg'); :mark { fill: #734a08 }; mark-size: 14; } <sld:Rule> <ogc:Filter> <ogc:PropertyIsEqualTo> <ogc:PropertyName>type</ogc:PropertyName> <ogc:Literal>bank</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:Filter> <sld:MaxScaleDenominator>6000</sld:MaxScaleDenominator> <sld:PointSymbolizer> <sld:Graphic> <sld:Mark> <sld:WellKnownName>file://symbols/bank.svg</sld:WellKnownName> <sld:Fill> <sld:CssParameter name="fill">#734a08</sld:CssParameter> </sld:Fill> </sld:Mark> <sld:Size>14</sld:Size> </sld:Graphic> </sld:PointSymbolizer> </sld:Rule> CSS bank.svg
  23. Marks composition and override [type = 'fountain’] { [@sd <

    6k] { mark: symbol(circle), symbol(circle); :nth-mark(1) { fill: #b5d0d0 }; :nth-mark(2) { fill: #576ddf }; mark-size: 10, 3; }; [@sd < 3k] { mark: symbol('file://symbols/fountain.svg'); :mark { fill: #576ddf; }; } } CSS
  24. • Many other options! • Built-in symbol names (well known

    marks): circle, square, triangle, … • From TTF fonts using the name ttf://<fontname>#charcode • Windbarbs, e.g.: windbarbs://default(15)[kts] • WKT specification, e.g. wkt://MULTIPOLYGON(((-0.25 -0.25, -0.125 -0.25), (0.125 -0.25, 0.25 -0.25)), (-0.25 0.25, -0.125 0.25), (0.125 0.25, 0.25 0.25) …… ) • See more here: http://docs.geoserver.org/latest/en/user/styling/sld/exte nsions/pointsymbols.html Other mark options
  25. Filling polygons 25

  26. Solid filling * { fill: lightgrey; stroke-width: 0.5; } <sld:PolygonSymbolizer>

    <sld:Fill> <sld:CssParameter name="fill">#d3d3d3</sld:CssParameter> </sld:Fill> <sld:Stroke> <sld:CssParameter name="stroke-width">0.5</sld:CssParameter> </sld:Stroke> </sld:PolygonSymbolizer> SLD CSS
  27. Solid fills and repeating images fill [@sd < 800k][type in

    ('cemetery', 'grave_yard')] { fill: #aacbaf; [@sd < 50k] { [religion = 'jewish'] { fill: #aacbaf, url('symbols/grave_yard_jewish.png'); }; [religion = 'christian'] { fill: #aacbaf, url('symbols/grave_yard_christian.png'); }; [religion = 'INT-generic'] { fill: #aacbaf, url('symbols/grave_yard_generic.png'); }; }; } CSS
  28. Hatching <sld:PolygonSymbolizer> <sld:Fill> <sld:GraphicFill> <sld:Graphic> <sld:Mark> <sld:WellKnownName>shape://times</sld:WellKnownName> <sld:Stroke> <sld:CssParameter name="stroke">#ADD8E6</sld:CssParameter>

    </sld:Stroke> </sld:Mark> <sld:Size>8</sld:Size> </sld:Graphic> </sld:GraphicFill> </sld:Fill> </sld:PolygonSymbolizer> </sld:Rule> [@scale < 10000] { fill: symbol('shape://times'); fill-size: 8; :fill { stroke: #ADD8E6; } } SLD CSS
  29. Filling Painting lines 29

  30. Solid lines (OSM admin borders) [type = 'administrative'] { [admin_level

    <= 4], [admin_level = 5 or admin_level = 6][@sd <= 400k], [admin_level = 7 or admin_level = 8][@sd <= 200k], [admin_level = 9 or admin_level = 10][@sd <= 100k] { stroke: #ac46ac; stroke-opacity: 0.4; } } CSS
  31. Dashes/Marks/images along a line * { stroke: darkRed, symbol('circle'); stroke-dasharray:

    10 14, 6 18; stroke-dashoffset: 14, 0; :stroke { fill: darkRed; size: 6; } } • Two coordinated dashed lines • One made with lines • One made with circles • The offset shifts them to have one appear in the empty spaces of the other CSS
  32. Labeling 32

  33. Vendor options • Priority labelling, static or attribute based •

    Deluge of vendor options to control in detail labelling: • group • labelAllGroup • spaceAround • followLine • maxDisplacement • repeat • maxAngleDelta • autoWrap • forceLeftToRight • conflictResolution • goodnessOfFit • polygonAlign • graphic-resize • graphic-margin • partials • underlineText • strikethroughText • charSpacing • wordSpacing
  34. Polygon labels <sld:TextSymbolizer> <sld:Label> <ogc:PropertyName>FULLNAME</ogc:PropertyName> </sld:Label> <sld:Font> <sld:CssParameter name="font-family">Arial</sld:CssParameter> <sld:CssParameter

    name="font-size">14.0</sld:CssParameter> <sld:CssParameter name="font-weight">bold</sld:CssParameter> </sld:Font> <sld:LabelPlacement> <sld:PointPlacement> <sld:AnchorPoint> <sld:AnchorPointX>0.5</sld:AnchorPointX> <sld:AnchorPointY>0.5</sld:AnchorPointY> </sld:AnchorPoint> </sld:PointPlacement> </sld:LabelPlacement> <sld:Fill> <sld:CssParameter name="fill">#000000</sld:CssParameter> </sld:Fill> <sld:Priority>50000</sld:Priority> <sld:VendorOption name="autoWrap">100</sld:VendorOption> <sld:VendorOption name="maxDisplacement">200</sld:VendorOption> <sld:VendorOption name="goodnessOfFit">0.9</sld:VendorOption> </sld:TextSymbolizer>
  35. Point labels and obstacles [@sd < 200k] { label: [FULLNAME];

    label-anchor: 0.5 1.0; label-offset: 0.0 -14.0; font-fill: #000033; font-family: Arial; font-size: 12; halo-color: white; halo-radius: 1.5; label-priority: 200000; label-auto-wrap: 100; mark: url('./img/landmarks/${IMAGE}’); mark-label-obstacle: true; } «FULLNAME» attribute Auto wrapping label with halo. Data driven symbol URL Labels won’t overlap the symbol CSS
  36. Line labels [@sd < 200k] { label: [LABEL_NAME]; font-fill: #000000;

    font-family: Arial; font-size: 13; font-style: normal; font-weight: bold; halo-color: #FFFFFF; halo-radius: 1; label-follow-line: true; label-repeat: 400; label-group: true; label-max-displacement: 200; } Draw «LABEL_NAME», black, with white halo Draw them along lines, fuse segments with same label, repeat CSS
  37. Raster styling 37

  38. A DEM and a color map • SRTM from USGS

    • Standard color map • Also shaded relief (not shown in the css below) [@sd > 75000] { raster-channels: auto; raster-color-map: color-map-entry(#00BFBF, -100.0, 0) color-map-entry(#00FF00, 920.0, 0) color-map-entry(#00FF00, 920.0, 1.0) color-map-entry(#FFFF00, 1940.0, 1.0) color-map-entry(#FFFF00, 1940.0, 1.0) color-map-entry(#FF7F00, 2960.0, 1.0) color-map-entry(#FF7F00, 2960.0, 1.0) color-map-entry(#BF7F3F, 3980.0, 1.0) color-map-entry(#BF7F3F, 3980.0, 1.0) color-map-entry(#141514, 5000.0, 1.0); } CSS
  39. Contrast enhancement http://docs.geoserver.org/latest/en/user/styling/sld-reference/rastersymbolizer. html#contrastenhancement <sld:RasterSymbolizer> <sld:ContrastEnhancement> <sld:Normalize> <sld:VendorOption name="algorithm"> StretchToMinimumMaximum

    </sld:VendorOption> <sld:VendorOption name="minValue">50</sld:VendorOption> <sld:VendorOption name="maxValue">800</sld:VendorOption> </sld:Normalize> </sld:ContrastEnhancement> </sld:RasterSymbolizer> GeoServer vendor extension SLD
  40. Other assorted features 40

  41. Color blending and alpha compositing multiply color-burn difference Source Destination

    https://docs.geoserver.org/latest/en/user/styling/sld/extensions/composite-blend/index.html More info at: Mask with alpha compositing <VendorOption name="composite"> multiply, 0.5 </VendorOption>
  42. Z ordering http://docs.geoserver.org/latest/en/user/styling/sld- extensions/z-order/example.html [class = 'motorways'] { stroke: #990000,

    #ff6666; stroke-width: 8, 6; stroke-linecap: round; z-index: 0, 3; } [class = 'railways'] { stroke: #333333; stroke-width: 3; z-index: 2; } [class = 'railways'] { stroke: #ffffff; stroke-width: 1.5; stroke-dasharray: 5 5; z-index: 3; } * { sort-by: "z_order"; sort-by-group: "roadsGroup"; } CSS 42
  43. Geometry transformations [@scale < 10000] { fill-geometry: [offset(the_geom, 6, -6)];

    fill: darkgray; z-index: 0; } [@scale < 10000] { fill: #b3b3b3; z-index: 1; } CSS 43
  44. Rendering transformations • WPS + WMS + rendering optimizations •

    In this slide, on the fly contour extraction * { transform: ras:Contour(levels: 1100 1200 1300 1400 1500 1600 1700 1800); stroke: black; label: [GRAY-INDEX]; font-fill: black; font-family: Sans; font-size: 12; halo-radius: 2; halo-color: white; label-follow-line: true } CSS 44
  45. Jiffle map algebra * { transform: ras:Jiffle(script: ‘ nir =

    src[7]; vir = src[3]; dest = (nir-vir)/(nir+vir);’); raster-channels: auto; raster-color-map: color-map-entry(#00BFBF, -100.0, 0) color-map-entry(#00FF00, 920.0, 0) … } CSS
  46. Legend control too (2.20.x) That “boundary” rule does help making

    the legend readable. Yet, we need borders and labels. Valid values: mapOnly, legendOnly <Rule> <Title>Boundary</ Title> <VendorOption name="inclusion" >mapOnly</VendorOption >
  47. Point and click editors 47

  48. QGIS SLD export • Basic vector and raster styling covered

    • Much advanced bits missing though • Needs sponsoring on both ends (QGIS and GeoServer)
  49. GeoStyler (community module)

  50. MapStore styler

  51. That’s all folks! Questions? info@geosolutionsgroup.com 51