GeoServer SLD styles and labels: from a rule to a verifiable map
Define server-side symbols and labels and test their behavior by scale and density.
Editorial review: 2026-09-23
A successful style migration preserves a map's meaning at its intended scales. Copying a fill color is not enough when categories, missing values, label priority or printed output influence decisions. GeoServer styles should be reviewed against a small cartographic specification, not a single favorable screenshot.
Separate data problems from styling problems
First publish the synthetic layer from the PostGIS/GeoServer tutorial and confirm its WFS attributes. This exercise uses publish:parcels, geometry geom, identifier parcel_id, and status values review and approved. If a source uses numeric domain codes, decide whether the published view exposes codes, labels or both. An SLD filter on a translated label will not match a stored integer.
Document the map purpose, supported scales, category labels and exceptions. For a cadastral review map, orange might mean “requires review” and green “approved”; those are fictional exercise meanings, not official regulatory colors. Include an outline and a text label so color is not the only way to distinguish important records.
Begin with one complete style
Save this as parcels.sld, add an SLD style in GeoServer and validate it before assigning it to the layer. It draws an orange polygon with a dark boundary and an identifier label. It deliberately avoids category rules until the basic rendering works.
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor version="1.0.0"
xmlns="http://www.opengis.net/sld"
xmlns:ogc="http://www.opengis.net/ogc">
<NamedLayer>
<Name>parcels</Name>
<UserStyle>
<Title>Synthetic parcel review</Title>
<FeatureTypeStyle>
<Rule>
<PolygonSymbolizer>
<Fill><CssParameter name="fill">#F1A340</CssParameter></Fill>
<Stroke>
<CssParameter name="stroke">#25322D</CssParameter>
<CssParameter name="stroke-width">1</CssParameter>
</Stroke>
</PolygonSymbolizer>
<TextSymbolizer>
<Label><ogc:PropertyName>parcel_id</ogc:PropertyName></Label>
<Font>
<CssParameter name="font-family">SansSerif</CssParameter>
<CssParameter name="font-size">12</CssParameter>
</Font>
<Halo><Radius>1</Radius><Fill>
<CssParameter name="fill">#FFFFFF</CssParameter>
</Fill></Halo>
<Fill><CssParameter name="fill">#25322D</CssParameter></Fill>
</TextSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
The SLD polygon cookbook and labeling reference document the building blocks. This example is an original minimal style; your fonts and GeoServer version still need local verification.
Add categories and scale rules one change at a time
Create a separate rule for each status using an OGC property-equality filter. Give rules human-readable titles so the legend has meaning. Add an explicit fallback for unexpected or null values; otherwise a data-quality problem can become an invisible polygon. Test all categories with a fixture containing every value before using a production dataset.
Decide at what scale identifiers remain readable. Add minimum/maximum scale denominators only after testing three deliberate views: regional overview, neighborhood and parcel detail. A symbol defined in pixels behaves differently from a ground-distance representation; choose according to the task. Avoid interpreting a change in line appearance as a geometry error until scale and output dimensions are fixed.
Compare server output and the consuming application
Use one WMS GetMap request with fixed bbox, CRS, width and height for repeatable review. Save a baseline image and change one style property at a time. Request the legend separately and compare category order, text and swatches. Then inspect the map in QGIS or the web viewer at matching extents.
| Check | Passing behavior | Common failure |
|---|---|---|
| Category coverage | Every expected value draws | Stored code differs from label filter |
| Missing value | Visible fallback | Null feature silently disappears |
| Text encoding | Names retain accents | Import or font problem |
| Label density | Critical labels remain legible | Overlap or excessive repetition |
| Font availability | Server uses approved font | Desktop font absent on server |
| Transparency | Basemap remains understandable | Fill opacity masks context |
| Cached output | Latest approved style appears | Old tiles survive style update |
A QGIS style export can be a starting point, but do not promise one-to-one conversion of expressions, symbol layers or labeling rules. Test the exported result and record manual adjustments. Preserve both the original project and the server style so future changes can be traced.
Release and maintain the map
Store styles with the application configuration, use a distinct revision, and retain the preceding working version. Coordinate publication with GeoWebCache invalidation; a correct style does not immediately replace previously generated tiles. If a change alters category meaning, communicate that meaning in the legend and metadata rather than silently changing colors.
The useful deliverable is a small style package, fixed comparison requests, accepted images at three scales, font dependencies and documented exceptions. That is a maintainable migration result; a screenshot without its request parameters is difficult to reproduce.