/posts  ·   /talks  ·   /boat  ·   /#30DayMapChallenge 2020 | 2022  ·  /About

On writing GeoServer SLDs (part I)

Right. So some time ago I needed to do a bunch of SLDs for use with GeoServer with categorical classes: every polygon’s fill colored with a different color. The easy solution was to style the layer in e.g. QGIS export the syle as SLD and use that with GeoServer. It would have been just this trivial a task but as always there were a couple of gotchas:

I knew previously about attribute-based styling where you define a FeatureTypeStyle/Rule with an associated Filter and a PolygonSymbolizer for this specific rule. Neat. But if you need to cook up filters for ump-teen different layers and then possibly someone changes something in any one of the codelist data that you were filtering your styles by … so no, this wouldn’t do.

But as the layers at hand were already SQL view- based, it was totally feasible to retrive the hex color code with the data from the database either precalculated or calculated per request. So what I was looking for was the capability of doing random coloring for polygons using SLD… except the colors had to be constant per value for every WMS GetMap request and not change in time… unless they needed to change.

To recap, essentially: a polygon’s fill can be of any random color but it needs to be constant in time based on whatever value that is chosen.

The solution that was finally adopted consisted of calculating a md5 hash of the property that you want to color your polygons by and then slice 6 chars this from this 32-char string for your hex color code in the GeoServer SQL view.

The only downside of this approach is that the WMS GetLegendGraphic requests will not return anything sensible (because the legend will be data-dependent). And even if it did who would want to see a 9K color-entry legend. There’s also a risk of neighboring areas being assigned the same-like colors but currently for the workflow needed this poses no problems. But it should be kept in mind still…

But right, the cool thing is - if you name this hex-color-column in your views by the same name you make it possible to reuse the style instantly.

But enough talk, lets do a run-through of this. You are welcome to follow along with your own dataset but if there’s nothing interesting you can find then lets download the Estonian administrative units settlements layer from the Estonian Land Board’s homepage and i’ll import that to my local PostGIS database using shp2pgsql.

~/data/tmp$ wget https://geoportaal.maaamet.ee/docs/haldus_asustus/asustusyksus_shp.zip -O asustusyksus_shp.zip
 [..]
~/data/tmp$ unzip asustusyksus_shp.zip
Archive:  asustusyksus_shp.zip
  inflating: asustusyksus_20200301.dbf  
  inflating: asustusyksus_20200301.prj  
  inflating: asustusyksus_20200301.shp  
  inflating: asustusyksus_20200301.shx
~/data/tmp$ shp2pgsql -d -s 3301 -g geom -W "cp1257" -I  asustusyksus_20200301.shp public.asustusyksus | psql -h localhost -d postgres -U postgres --quiet
 [..]
~/data/tmp$

Next in the GeoServer web ui set up a new SQL view- based layer with the following query (in a workspace called public using a PostGIS datastore)

select
    gid, animi as settlement_name, akood as settlement_code,
    tyyp as settlement_type, onimi as municipality_name, okood as municipality_code,  
    mnimi as county_name, mkood as county_code, geom
from
    public.asustusyksus

and publish it with the default settings. Now browsing the layer preview at your local GeoServer yields something in the line of

Settlement units WMS with all defaults

Right, this is nice and grayish. In order to bring some colors into play here we’ll need to do two things:

Note: I’m prefixing this computed column name with an underscore so for example for supporting GetFeatureInfo requests this column can easily be filtered out from the response.

So, open up the GeoServer web ui SQL view editor again and change the query to

select
    gid, animi as settlement_name, akood as settlement_code,
    tyyp as settlement_type, onimi as municipality_name, okood as municipality_code,  
    mnimi as county_name, mkood as county_code, geom,
    '#'||right(md5(onimi),6) as _hex
from
    public.asustusyksus

Now for the SLD style, create a new style. I’m calling it area.autostyle.hex. Otherwise it is essentially the same as any other polygon FeatureTypeStyle with the only difference that we’re using

<ogc:PropertyName>_hex</ogc:PropertyName>

as the input to PolygonSymbolizer’s fill

[..]
<FeatureTypeStyle>
  <Rule>
    <PolygonSymbolizer>
      <Fill>
        <CssParameter name="fill">
          <ogc:PropertyName>_hex</ogc:PropertyName>
        </CssParameter>
        <CssParameter name="fill-opacity">0.8</CssParameter>
      </Fill>
      <Stroke>
        <CssParameter name="stroke">#030303</CssParameter>
        <CssParameter name="stroke-width">0.1</CssParameter>
      </Stroke>
    </PolygonSymbolizer>
  </Rule>
</FeatureTypeStyle>
[..]

The full SLD file is available here

Don’t forget to change the layer’s default style to the new area.autostyle.hex aswell. Now browsing again the layer preview at your local GeoServer yields something in the line of

Settlement units colored by municipality name

All required $GEOSERVER_DATA_DIR/workspaces files to set this up locally are available at this github repo under CC-BY-SA 4.0

Next time around I’ll write something on how to compose a SLD file so you would never have compose any more SLDs again.