Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Simone Giannecchini

October 04, 2021
Tweet

More Decks by Simone Giannecchini

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. What’s next • Going to explore styling concepts • Languages

    used in examples • SLD 1.0 + GS extensions • GeoCSS SLD CSS
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. • 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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>
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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>
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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 >
  38. QGIS SLD export • Basic vector and raster styling covered

    • Much advanced bits missing though • Needs sponsoring on both ends (QGIS and GeoServer)