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

Creating stunning maps with GeoServer With SLD, CSS, YSLD and MBStyles

Creating stunning maps with GeoServer With SLD, CSS, YSLD and MBStyles

Simone Giannecchini

August 29, 2019
Tweet

More Decks by Simone Giannecchini

Other Decks in Technology

Transcript

  1. Creating stunning maps with GeoServer With SLD, CSS, YSLD and

    MBStyles Ing. Andrea Aime GeoSolutions 1
  2. GeoSolutions ⚫ Founded in Italy in late 2006 ⚫ Expertise

    • Image Processing, GeoSpatial Data Fusion • Java, Java Enterprise, C++, Python • JPEG2000, JPIP, Advanced 2D visualization ⚫ Supporting/Developing FOSS4G projects ⚫ GeoServer, MapStore ⚫ GeoNetwork, GeoNode, Ckan ⚫ Clients ⚫ Public Agencies ⚫ Private Companies ⚫ http://www.geo-solutions.it 2 FOSS4G 2019, August 26th/30th, Bucharest
  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 4 Map rendering FOSS4G 2019, August 26th/30th, Bucharest
  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 5 FOSS4G 2019, August 26th/30th, Bucharest
  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 6 FOSS4G 2019, August 26th/30th, Bucharest
  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 FOSS4G 2019, August 26th/30th, Bucharest
  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 8 FOSS4G 2019, August 26th/30th, Bucharest
  8. GeoCSS ⚫ Compact syntax, familiar for web developers ⚫ CQL

    based filtering, rule nesting and cascading keeps complex styling compact (you just express the overrides to the base) ⚫ Autocomplete in the style editor ⚫ 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'); } 9 FOSS4G 2019, August 26th/30th, Bucharest
  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 FOSS4G 2019, August 26th/30th, Bucharest
  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 FOSS4G 2019, August 26th/30th, Bucharest
  11. What’s next ⚫ Going to explore styling concepts ⚫ Languages

    used in examples ⚫ SLD 1.0 + GS extensions ⚫ YSLD ⚫ GeoCSS YSLD SLD CSS 12 FOSS4G 2019, August 26th/30th, Bucharest
  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 14 FOSS4G 2019, August 26th/30th, Bucharest
  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 ⚫ YSLD: ⚫ scale: (1000, 1000000) ⚫ scale: (1e3, 1e6) ⚫ zoom: (8, 16) grid: name: WGS84 15 FOSS4G 2019, August 26th/30th, Bucharest
  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; … line: stroke-color: '#0000FF' uom: metre; stroke-width: '5'; SLD YSLD CSS 16 FOSS4G 2019, August 26th/30th, Bucharest
  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 17 FOSS4G 2019, August 26th/30th, Bucharest
  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 18 FOSS4G 2019, August 26th/30th, Bucharest
  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 20 FOSS4G 2019, August 26th/30th, Bucharest
  18. Marks (SVG in this case) [type = 'bank'][@sd < 6k]

    { mark: symbol('file://symbols/bank.svg'); :mark { fill: #734a08 }; mark-size: 14; } feature-styles: - rules: - filter: type = 'bank' scale: (,6000.0) symbolizers: - point: symbols: - mark: shape: file://symbols/bank.svg fill-color: ! '#734a08' size: 14 YSLD CSS 21 FOSS4G 2019, August 26th/30th, Bucharest
  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 22 FOSS4G 2019, August 26th/30th, Bucharest
  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 23 FOSS4G 2019, August 26th/30th, Bucharest
  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 25 FOSS4G 2019, August 26th/30th, Bucharest
  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 26 FOSS4G 2019, August 26th/30th, Bucharest
  23. Filling with repeating images (YSLD) feature-styles: - rules: - filter:

    type IN ('cemetery','grave_yard') scale: (,800000.0) symbolizers: - polygon: fill-color: ! '#aacbaf' - filter: (type IN ('cemetery','grave_yard')) AND religion = 'jewish' scale: (,50000.0) symbolizers: - polygon: fill-graphic: symbols: - external: url: symbols/grave_yard_jewish.png format: image/png # continues in the next slide First uniform background fill Then foreground symbols for various religions YSLD 27 FOSS4G 2019, August 26th/30th, Bucharest
  24. Filling with repeating images (YSLD) - filter: (type IN ('cemetery','grave_yard'))

    AND religion = 'christian' scale: (,50000.0) symbolizers: - polygon: fill-graphic: symbols: - external: url: symbols/grave_yard_christian.png format: image/png - filter: (type IN ('cemetery','grave_yard')) AND religion = 'INT-generic' scale: (,50000.0) symbolizers: - polygon: fill-graphic: symbols: - external: url: symbols/grave_yard_generic.png format: image/png YSLD 28 FOSS4G 2019, August 26th/30th, Bucharest
  25. 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 FOSS4G 2019, August 26th/30th, Bucharest
  26. 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 FOSS4G 2019, August 26th/30th, Bucharest
  27. 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 FOSS4G 2019, August 26th/30th, Bucharest
  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 35 FOSS4G 2019, August 26th/30th, Bucharest
  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 36 FOSS4G 2019, August 26th/30th, Bucharest
  30. 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> 37 FOSS4G 2019, August 26th/30th, Bucharest
  31. 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 FOSS4G 2019, August 26th/30th, Bucharest
  32. 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 FOSS4G 2019, August 26th/30th, Bucharest
  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 43 FOSS4G 2019, August 26th/30th, Bucharest
  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 44 FOSS4G 2019, August 26th/30th, Bucharest
  35. Rendering transformations * { 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 45 FOSS4G 2019, August 26th/30th, Bucharest
  36. New transform in 2.14: 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 FOSS4G 2019, August 26th/30th, Bucharest
  37. QGIS SLD export work ⚫ In QGIS 2.18, Bonn code

    sprint improvements ⚫ In QGIS 3.0 support for label exports (thanks for OpenGeoGroep sponsoring) 48 FOSS4G 2019, August 26th/30th, Bucharest
  38. Raster symbolizer export ⚫ Thanks to OSGeo UK sponsorship, GeoServer

    PSC donation, and GeoSolutions in- kind support ⚫ Available since QGIS 3.4.5 49 FOSS4G 2019, August 26th/30th, Bucharest