creating stunning maps in geoserver: mastering sld and css styles
TRANSCRIPT
Creating stunning maps
with GeoServerMastering SLD and CSS
Ing. Andrea AimeGeoSolutions
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
FOSS4G 2017, BostonAugust 14th-19th 2017
SLD vs CSS
Styled Layer Descriptor
OGC standard
XML based, verbose, hard to hand edit
Only showing relevant bits of the SLD
GeoCSS
CSS with extensions for map rendering
Simple, Compact, designed for human beings
Not a standard (several incompatible variants for mapping, GeoServer one has nothing to do with CartoDB/MapBox one)
FOSS4G 2017, BostonAugust 14th-19th 2017
CSS features
Familiar for web developers
Compact syntax
Symbolization triggered by “key” properties (stroke, fill, mark, label, channel-selection)
CQL based filtering
Cascading keeps complex styling compact (you just express the overrides to the base)
Interactive editor for productive style development
FOSS4G 2017, BostonAugust 14th-19th 2017
GeoCSS in a nutshell
• Filter by attribute/env variable in CQL
• Filter by scale
• Set properties to control symbolization
• Key properties activate certain symbolization:
– mark/fill/stroke/label/raster-channels
FOSS4G 2017, BostonAugust 14th-19th 2017
[admin_level < 2][@sd < 1M][@sd > 100k] {label: name;font-family: ‘Noto Sans’;…/* this is nested rule */[special = true][@sd < 500k] {font-weight: bold;…
}}
SLD equivalents
FOSS4G 2017, BostonAugust 14th-19th 2017
<sld:Rule><ogc:Filter>
<ogc:PropertyIsEqualTo><ogc:PropertyName>admin_level</ogc:PropertyName><ogc:Literal>2</ogc:Literal>
</ogc:PropertyIsEqualTo></ogc:Filter><sld:MinScaleDenominator>100000</sld:MinScaleDenominator><sld:MaxScaleDenominator>1000000</sld:MaxScaleDenominator><sld:TextSymbolizer>
<sld:Label><ogc:PropertyName>name</ogc:PropertyName>
</sld:Label><sld:Font>
<sld:CssParameter name="font-family">Noto Sans
</sld:CssParameter><sld:CssParameter name="font-size">10
...</sld:TextSymbolizer>
<!–- Override would require a separate rule specifying everything again --></sld:Rule>
Filter
Scale
dependency
Symbolizers
and their
properties
Scale dependencies
FOSS4G 2017, BostonAugust 14th-19th 2017
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
FOSS4G 2017, BostonAugust 14th-19th 2017
Expressing scale dependency filters
SLD:
<MinScaleDenominator>
<MaxScaleDenominator>
CSS
Legacy: [@scale > 10000000]
GeoServer 2.12+: [@sd > 1M]
More compact variable, more correct (it’s a scale denominator, not a scale!)
Compact expression of large numbers makes them readable at a glance, e.g., 100k, 1M
FOSS4G 2017, BostonAugust 14th-19th 2017
Unit of Measure
FOSS4G 2017, BostonAugust 14th-19th 2017
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;
}
Transformation functions
FOSS4G 2017, BostonAugust 14th-19th 2017
OSM like, setting a different value depending on the current scale/zoom level
Not a linear scale mind
[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
Transformation functions in SLD
FOSS4G 2017, BostonAugust 14th-19th 2017
<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>
Point styling
FOSS4G 2017, BostonAugust 14th-19th 2017
Simple symbol
FOSS4G 2017, BostonAugust 14th-19th 2017
[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>
Marks (SVG in this case)
FOSS4G 2017, BostonAugust 14th-19th 2017
[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.0</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>
New in
GeoServer
2.12!
Marks composition and override
FOSS4G 2017, BostonAugust 14th-19th 2017
[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; };
}}
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://MULTILINESTRING((-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/extensions/pointsymbols.html
Other mark options
FOSS4G 2017, BostonAugust 14th-19th 2017
Filling polygons
FOSS4G 2017, BostonAugust 14th-19th 2017
Solid filling
FOSS4G 2017, BostonAugust 14th-19th 2017
* { fill: lightgrey; stroke: black;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>
Filling with repeating images
fill
FOSS4G 2017, BostonAugust 14th-19th 2017
[@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');};
};}
Filling with repeating images (SLD)
FOSS4G 2017, BostonAugust 14th-19th 2017
<sld:Rule><ogc:Filter><ogc:And><ogc:Or>
<ogc:PropertyIsEqualTo><ogc:PropertyName>type</ogc:PropertyName><ogc:Literal>cemetery</ogc:Literal>
</ogc:PropertyIsEqualTo><ogc:PropertyIsEqualTo><ogc:PropertyName>type</ogc:PropertyName><ogc:Literal>grave_yard</ogc:Literal>
</ogc:PropertyIsEqualTo></ogc:Or><ogc:PropertyIsEqualTo>
<ogc:PropertyName>religion</ogc:PropertyName><ogc:Literal>jewish</ogc:Literal>
</ogc:PropertyIsEqualTo></ogc:And>
</ogc:Filter><sld:MaxScaleDenominator>50000.0</sld:MaxScaleDenominator>
One sample rule (the Jewish religion one).
Three more needed to get the same
display as with CSS
Filling with repeating images (SLD)
FOSS4G 2017, BostonAugust 14th-19th 2017
<sld:PolygonSymbolizer><sld:Fill><sld:CssParameter name="fill">#aacbaf</sld:CssParameter>
</sld:Fill></sld:PolygonSymbolizer><sld:PolygonSymbolizer><sld:Fill><sld:GraphicFill>
<sld:Graphic><sld:ExternalGraphic>
<sld:OnlineResourcexmlns:xlink="http://www.w3.org/1999/xlink"xlink:type="simple" xlink:href="symbols/grave_yard_jewish.png" />
<sld:Format>image/png</sld:Format></sld:ExternalGraphic>
</sld:Graphic></sld:GraphicFill>
</sld:Fill></sld:PolygonSymbolizer>
</sld:Rule>
Filling with marks
FOSS4G 2017, BostonAugust 14th-19th 2017
<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; }
}
Painting lines
FOSS4G 2017, BostonAugust 14th-19th 2017
Solid lines (OSM admin borders)
FOSS4G 2017, BostonAugust 14th-19th 2017
[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;
}}
<!-- 5 different rules with different filters followed by this --><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#ac46ac</sld:CssParameter><sld:CssParameter name="stroke-opacity">0.4</sld:CssParameter>
</sld:Stroke></sld:LineSymbolizer>
Dashes
FOSS4G 2017, BostonAugust 14th-19th 2017
…<sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#6B4900</sld:CssParameter><sld:CssParameter name="stroke-width">0.1</sld:CssParameter><sld:CssParameter name="stroke-dasharray">2 2</sld:CssParameter>
</sld:Stroke></sld:LineSymbolizer>
[@sd < 75k] {stroke: #6B4900;stroke-width: 0.1;stroke-dasharray: 2;
}
Alternating dashes with marks
FOSS4G 2017, BostonAugust 14th-19th 2017
* { 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
Labeling
FOSS4G 2017, BostonAugust 14th-19th 2017
Vendor options
FOSS4G 2017, BostonAugust 14th-19th 2017
Point labels and obstacles
FOSS4G 2017, BostonAugust 14th-19th 2017
[@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 URLLabels won’t overlap
the symbol
Line labels
FOSS4G 2017, BostonAugust 14th-19th 2017
[@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
Polygon labels
FOSS4G 2017, BostonAugust 14th-19th 2017
<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>
Support for pre-laid-out labels
FOSS4G 2017, BostonAugust 14th-19th 2017
Raster styling
FOSS4G 2017, BostonAugust 14th-19th 2017
A DEM and a color map
FOSS4G 2016, Bonn24nd – 24th August 2016
SRTM from USGS
Standard color map
No-data natively transparent in 2.8.x thanks to JAI-EXT
[@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);
}
Contrast enhancement
http://docs.geoserver.org/latest/en/user/styling/sld-
reference/rastersymbolizer.html#contrastenhancement
FOSS4G 2017, BostonAugust 14th-19th 2017
<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
Other assorted features
FOSS4G 2017, BostonAugust 14th-19th 2017
Geometry transformations
FOSS4G 2017, BostonAugust 14th-19th 2017
[@scale < 10000] {fill-geometry: [offset(the_geom, 6, -6)];fill: darkgray;z-index: 0;
}
[@scale < 10000] {fill: #b3b3b3;z-index: 1;
}
Rendering transformations
FOSS4G 2017, BostonAugust 14th-19th 2017
* {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
}
Masking via alpha compositing
FOSS4G 2016, Bonn24nd – 24th August 2016
destination-in
Color blendin
FOSS4G 2017, BostonAugust 14th-19th 2017
http://docs.geoserver.org/stable/user/styling/sld-extensions/composite-blend/index.html
More info at:
Z ordering
http://docs.geoserver.org/latest/en/user/styling/sld-
extensions/z-order/example.html
FOSS4G 2017, BostonAugust 14th-19th 2017
[class = 'motorways'] {stroke: #990000;stroke-width: 8;z-index: 0;
}[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;
}[class = 'motorways'] {
stroke: #ff6666;stroke-width: 6;stroke-linecap: round;z-index: 3;
}
* {sort-by: "z_order";sort-by-group: "roadsGroup";}
New in 2.12
FOSS4G 2017, BostonAugust 14th-19th 2017
Autocomplete for CSS editor
Because there are too many properties to remember!
FOSS4G 2017, BostonAugust 14th-19th 2017
No more -gt- prefix in CSS
Vendor options used to be prefixed with “-gt-”
But CSS is its own language, no need to mark something as “vendor” or refer to GeoTools
Don’t worry, old syntax still supported
FOSS4G 2017, BostonAugust 14th-19th 2017
* {...-gt-mark-label-obstacle: true;-gt-label-priority: 200000;-gt-label-auto-wrap: 100;
}\
* {...mark-label-obstacle: true;label-priority: 200000;label-auto-wrap: 100;
}
GeoCSS env variable expansion
Use @var or @var(defaultValue) in place of calling the env function
@var -> env(‘var’)
@var(defaultValue) env(‘var’, defaultValue)
Exception for @sd
@sd -> env(‘wms_scale_denominator’)
to get a more fluent syntax for scale dependencies
New in 2.12
FOSS4G 2017, BostonAugust 14th-19th 2017
Underline and strikethrough
New vendor options to control underline and strikethrough
FOSS4G 2017, BostonAugust 14th-19th 2017
<VendorOption name="underlineText">true</VendorOption><VendorOption name="strikethroughText">true</VendorOption>
Char and word spacing
Control how much space there is between each char
And between words
FOSS4G 2017, BostonAugust 14th-19th 2017
<VendorOption name="charSpacing">3</VendorOption><VendorOption name="wordSpacing">5</VendorOption>
charSpacing = 3 wordSpacing = 5
SVG marks
Refer to a SVG file insindemark/WellKnownMark
The outline of the SVG will be extracted
Then filled and stroked as requested
Suitable for simple shapes
FOSS4G 2017, BostonAugust 14th-19th 2017
:mark {fill: red;stroke: yellow;stroke-width: 3;stroke-opacity: 50%;
}
More QGIS SLD export work
In QGIS 3.0 support for label exports (thanks for OpenGeoGroep sponsoring)
Want to see more? Help us fund the effort
FOSS4G 2017, BostonAugust 14th-19th 2017