Smathermather's Weblog

Remote Sensing, GIS, Ecology, and Oddball Techniques

Posts Tagged ‘SLD’

Advanced Cartography in #GeoServer– #SLDs can make for very pretty maps

Posted by smathermather on May 10, 2012

I said it in the title, I’ll say it again, SLDs are not just for ugly maps.

I’ve heard from several professionals in this geospatial sector that CartoCSS is so cool– “look at the great cartography you can get out of it”. And, truth be told, creating a standard for cartography that aligns so well with existing web development standards is a brilliant way to get designers in on the map development process (as demonstrated aptly by e.g. Stamen), something sorely needed since Geography decided that cartographers weren’t needed anymore, some time in the late 90s/ early double aughts. That said, and at the risk of sounding parochial, beautiful maps can be produced using SLDs in GeoServer too. The below map is a snapshot from our GeoServer instance, and evolved from a set of cartographic conventions we’ve been developing in our shop, adapted in 4 hours by a darn smart part time employee (Tom Kraft) who had never built an SLD before.

Without further addo:

Posted in GeoServer | Tagged: , , , , | 6 Comments »

Complex Symbolization in GeoServer or Compass Rose Mania– the GeoServer Version

Posted by smathermather on November 30, 2011

At my place of employment, we have a vegetation survey program with enough potential plots to serve 50 years of data collection. The points are laid out in a Generalized Random Tessellation Stratified (GRTS) to maximize the statistical power of the analyses we do with them. Read more about GRTS. I dare you. Actually, it’s not so bad if you understand Quad-Trees and the like– it’s just a wicked clever way to ensure spatially-balanced random sampling in order to maximize power.

But GRTS is not the point. The point is that we wanted to show in a map a 40 ft meter buffer, with some cues for the cardinal directions for our field staff. We also wanted them to be able to make their own maps. Enter some funky complex symbolization with a dashed line for the buffer, and dots for the cardinal directions, kind of like thousands of compass roses across the landscape:

Compass Rose Mania-- the GeoServer Version

.

(BTW, find the flaw in this approach and you get my praise. That and a dollar will buy you a cup of coffee– hint: we’re working in a State Plane that is Lambert Conformal Conic).

SLD below:

<?xml version="1.0" encoding="ISO-8859-1"?>
<StyledLayerDescriptor version="1.0.0" xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc"
  xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd">
  <NamedLayer>
    <Name>40 meter buffer</Name>
    <UserStyle>
      <Title>40 meter buffer</Title>
      <Abstract></Abstract>
      <FeatureTypeStyle>
        <Rule>
          <Title>40 meter buffer</Title>
          <Name>40 meter buffer</Name>
          <MaxScaleDenominator>100000</MaxScaleDenominator>
          <PolygonSymbolizer>
            <Geometry>
              <ogc:Function name="buffer">
                <ogc:PropertyName>the_geom</ogc:PropertyName>
                <ogc:Literal>131.2</ogc:Literal>
              </ogc:Function>
            </Geometry>
            <Stroke>
              <CssParameter name="stroke">#d3d3d3</CssParameter>
              <CssParameter name="stroke-width">2</CssParameter>
            <CssParameter name="stroke-dasharray">2 2</CssParameter>
            </Stroke>
          </PolygonSymbolizer>

          <PointSymbolizer>

            <Geometry>
               <ogc:Function name="offset">
                  <ogc:PropertyName>the_geom</ogc:PropertyName>
                  <ogc:Literal>0</ogc:Literal>
                 <ogc:Literal>131.2</ogc:Literal>
                 </ogc:Function>
            </Geometry>

            <Graphic>
              <Mark>
                <WellKnownName>circle</WellKnownName>
                <Fill>
                  <CssParameter name="fill">#bebebe</CssParameter>
                </Fill>
              </Mark>
              <Size>6</Size>
            </Graphic>
          </PointSymbolizer>
       
          <PointSymbolizer>

            <Geometry>
               <ogc:Function name="offset">
                  <ogc:PropertyName>the_geom</ogc:PropertyName>
                  <ogc:Literal>0</ogc:Literal>
                 <ogc:Literal>-131.2</ogc:Literal>
                 </ogc:Function>
            </Geometry>

            <Graphic>
              <Mark>
                <WellKnownName>circle</WellKnownName>
                <Fill>
                  <CssParameter name="fill">#bebebe</CssParameter>
                </Fill>
              </Mark>
              <Size>6</Size>
            </Graphic>
          </PointSymbolizer>
          
          <PointSymbolizer>

            <Geometry>
               <ogc:Function name="offset">
                  <ogc:PropertyName>the_geom</ogc:PropertyName>
                  <ogc:Literal>131.2</ogc:Literal>
                 <ogc:Literal>0</ogc:Literal>
                 </ogc:Function>
            </Geometry>

            <Graphic>
              <Mark>
                <WellKnownName>circle</WellKnownName>
                <Fill>
                  <CssParameter name="fill">#bebebe</CssParameter>
                </Fill>
              </Mark>
              <Size>6</Size>
            </Graphic>
          </PointSymbolizer>
          
          <PointSymbolizer>

            <Geometry>
               <ogc:Function name="offset">
                  <ogc:PropertyName>the_geom</ogc:PropertyName>
                  <ogc:Literal>-131.2</ogc:Literal>
                 <ogc:Literal>0</ogc:Literal>
                 </ogc:Function>
            </Geometry>

            <Graphic>
              <Mark>
                <WellKnownName>circle</WellKnownName>
                <Fill>
                  <CssParameter name="fill">#bebebe</CssParameter>
                </Fill>
              </Mark>
              <Size>6</Size>
            </Graphic>
          </PointSymbolizer>          
          
          
        </Rule>

      </FeatureTypeStyle>
    </UserStyle>
  </NamedLayer>
</StyledLayerDescriptor>
</code></pre>


Posted in GeoServer | Tagged: , , , , | 5 Comments »

Contours– Structuring PostGIS data for viewing with GeoServer

Posted by smathermather on May 25, 2011

Naively structured data is my bane– the desire (and need) to get stuff done so often overtakes the time needed to do things the better way. So, we bootstrap.

A long time ago, we managed to load in a few tens of gigs of contour data into PostGIS, partitioned it into 2ft, 10ft, 20ft, 50ft, 100ft and 250ft tables using select queries with a modulus operator, e.g.


CREATE TABLE base.cuy_contours_10
	AS
	SELECT elevation, the_geom
		FROM base.cuy_contours_2
		WHERE base.cuy_contours_2.elevation % 10 = 0;

And then we built SLD‘s to display the data in GeoServer and formed the data into layer groups, and away we went… .

However, layer groups can often be replaced by properly structured PostGIS tables. For large (and homogenous) datasets like this, it’s the only way to go. In addition, properly structuring this allows us to take advantage of GeoServer’s ability to modify the legend based on zoom extent, which for representing contours at a range of scales is an almost “must have”.

To structure the table, we could be disciplined and build this out as a proper normalized relational dataset where our gid is used to determine if a contour is divisible by 10, 20, 50 etc., and while “I don’t wanna” isn’t a good enough reason not to do this, I think the computational overhead of a database view piecing these data back into a single table each time we need to access this would not be justified in the light of the static nature of the table. So database normalization be darned, disk space is cheap, full speed ahead. Let’s add some boolean fields for flagging whether a contour is divisible by our numbers and calculate that out:


UPDATE base.contours_2
	SET div_10 = CAST( contours_2.elevation % 10 AS BOOLEAN );


UPDATE base.contours_2
	SET div_250 = CAST( contours_2.elevation % 250 AS BOOLEAN );

yields the following (with highlights added):

Sort of the opposite of what I intended, the “falses” maybe should be “trues” and vice versa, but hey, it’ll work anyway. BTW, we did test doing this calculation a few different ways, and while I can’t remember all the details, doing this as a calculation instead of doing an update query with a while statement testing the modulus was much faster (3x faster).

Ok, so now we style an sld for it, and sit back and enjoy (pics later…).

Posted in Database, Database Optimization, GeoServer, PostGIS, SQL | Tagged: , , , , , , , , , | Leave a Comment »

SLD for contour data

Posted by smathermather on May 25, 2011

See other post for explanation:

<?xml version="1.0" encoding="ISO-8859-1"?>
<StyledLayerDescriptor version="1.0.0" xmlns="http://www.opengis.net/sld"

xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/sld

http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd">
<NamedLayer>
<Name>contours</Name>
<UserStyle>
<Title>contours</Title>
<Abstract>Contour lines with index</Abstract>
<FeatureTypeStyle>

<Rule>

<Name>rule01</Name>
<Title>2 ft contours</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_10</ogc:PropertyName>
<ogc:Literal>1</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>1</MinScaleDenominator>
<MaxScaleDenominator>4799</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#ffd700</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule02</Name>
<Title>10 ft contours index</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_10</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>1</MinScaleDenominator>
<MaxScaleDenominator>4799</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#D95F02</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule03</Name>
<Title>10 ft contours</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_10</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>4800</MinScaleDenominator>
<MaxScaleDenominator>7199</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#ffd700</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule04</Name>
<Title>50 foot contours index</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_50</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>4800</MinScaleDenominator>
<MaxScaleDenominator>7199</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#D95F02</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule05</Name>
<Title>20 ft contours</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_20</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>7200</MinScaleDenominator>
<MaxScaleDenominator>20999</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#ffd700</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule06</Name>
<Title>100 foot contours index</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_100</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>7200</MinScaleDenominator>
<MaxScaleDenominator>20999</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#D95F02</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule07</Name>
<Title>50 ft contours</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_50</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>21000</MinScaleDenominator>
<MaxScaleDenominator>100000</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#ffd700</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

<Rule>

<Name>rule08</Name>
<Title>250 foot contours index</Title>
<Abstract>Abstract</Abstract>

<ogc:Filter>
<ogc:PropertyIsEqualTo>
<ogc:PropertyName>div_250</ogc:PropertyName>
<ogc:Literal>0</ogc:Literal>
</ogc:PropertyIsEqualTo>
</ogc:Filter>

<MinScaleDenominator>21000</MinScaleDenominator>
<MaxScaleDenominator>100000</MaxScaleDenominator>

<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#D95F02</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linecap">
<ogc:Literal>butt</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-linejoin">
<ogc:Literal>miter</ogc:Literal>
</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>1</ogc:Literal>
</CssParameter>

</Stroke>
</LineSymbolizer>
</Rule>

</FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>

Posted in GeoServer | Tagged: , , , | 1 Comment »

GeoServer Optimization

Posted by smathermather on February 5, 2011

As we move away from a simple stack of PostGIS/GeoServer/GeoWebCache/Openlayers to wrapping a MapFish print service into the stack, it’s time to think more seriously about optimizing and stabilizing GeoServer.

In preparation for this step, I’ve been setting up a series of VMWare ESX-hosted Debian Linux VMs to function as the cluster of geospatial services.

Fortunately for me, there’s plenty of great advice in Andre Aime’s 2009 Foss4G presentation GeoServer in Production. Here’s what I gleaned from the presentation (any mistakes are undoubtedly mine and not Aime’s), plus a little bit of expansion from me:

1) Control the requests coming into the system. In this case, Andre talks about application container requests, limiting, e.g. Tomcat concurrent requests to 20 instead of the default 200:

maxThreads="20" minSpareThreads="20"

2) Set up a high availability (HA) cluster. There are lots of ways to skin this beast, but a cheap and easy way is via the Ultimate Cheapskate Cluster. In Aime’s presentation, this is using vrrpd + balance, but with the current option of using “Pen“, stateful protocols like WFS-Transactional should be supported in addition to WMS.

3) Set up your java virtual machines intelligently. Most of this information get’s covered in GeoServer’s documentation page Running in a Production Environment. Additions from Andre’s presentation which might still be relevant are the following JVM flags:

-XX:NewRatio=2
-XX:+AggressiveOpt

If you use the second one, the JVM will use experimental optimizations, so test for stability before using this in a production environment.  The first one notifies the virtual machine that there will be many temporary objects.

(FYI, for the nubes like me out there– JVM flags for Tomcat are set in the Catalina.sh startup script.)

3a) This get’s a special subheading, ’cause I couldn’t figure out why my WMS rendering was slow and unstable when I switched from Windows to Linux: Install and Use JAI & JAI Image I/O.

4) Finally, make sure your data are structured properly. If it’s really big imagery (>2GB), use an Image Pyramid, but otherwise, take advantage of internal tiling and overviews.  Examples from gdal’s utilities include
gdal_translate -of GTiff -co "TILED=YES" utm.tif utm_tiled.tif
which creates internal tiling, and

gdaladdo -r average utm_tiled.tif 2 4 8 16 32 64 128 256

which adds overviews.  You might also look to optimize the size of internal tiling.

For vector data, use PostGIS (not shapefiles), and index on your geometry and any attributes that are used in your SLD as filters. Also, show simple symbology when “zoomed out”, and reserve the complex rules for closer zoom levels.

An alternative that Aime doesn’t mention is that for really complicated data, you can do additional optimization. You can create generalized geometry columns as alternate columns. This is the vector equivalent of overviews. The SLD can then be coded to use the alternate simplified geometry at coarser scales (see e.g. this post for info on how to specify the geometry column in an SLD).  I wish I could find the GeoServer post that originally advocated this technique… .

Hopefully this helps stabilize, optimize, and increase the availability of your GeoServer instance.  Hopefully it does so for mine as well… .

Posted in GeoServer | Tagged: , , , , , , , , | 4 Comments »

Parcel Annotations in GeoServer (with some Maplex help) (cont. 2)

Posted by smathermather on February 4, 2011

I promised pics from our labeled parcels:

 

https://smathermather.wordpress.com/2011/02/01/parcel-annotations-in-geoserver-with-some-maplex-help/

https://smathermather.wordpress.com/2011/02/01/parcel-annotations-in-geoserver-with-some-maplex-help-cont-1/

Posted in GeoServer, PostGIS | Tagged: , , , , , , , , | Leave a Comment »

Parcel Annotations in GeoServer (with some Maplex help)

Posted by smathermather on February 1, 2011

smathermather:

We have a guest blogger today– Ramon, a bright and hard-working intern I’ve had the pleasure of working with for over a year.  If you’re looking for someone versed in Postgre/PostGIS/GeoServer/OpenLayers, Ramon’s been my rock as we’ve been building our internal system, doing everything from basic grunt work to esoteric trouble-shooting.  I’m trying hard not to fall on my face now that he’s moved on.

We wanted to take advantage of the advance labeling of Maplex for parcels in GeoServer.  GeoServer SLDs don’t yet (I think) have automatic rotation for best fit for polygons, but Maplex (an ESRI label extension that’s rolled in with the ArcINFO license in ArcGIS desktop) does.  Here’s how we took advantage of that:

Ramon:
The general procedure to generate the attribute table is as follows: (Note: It is best to do this in sections because in the limitations of labels that could be generated per annotation feature)
1) Generate the labels using Maplex as the label engine in Arcmap.
2) Convert the labels into annotation (in a database)
3) Join the annotation table back to the original polygon that needs labeled.
4) Export the dataset to create a new shapefile with the combined attribute table of the original shapefile and “annotation feature”
5) Import the shapefile to PostgreSQL

Notes: I added the following fields to the shapefie before importing it to PostgreSQL
a) acreage – area of the parcel in acres
b) res_prop – “Y” if parcel is within 1500 ft of our area of interest, otherwise “N”
c) ang – small integer conversion of the “Angle” value generated by Maplex (this may be scrapped from the workflow in the future)
d) Rot_Ang – the rotation angle in terms of Geoserver convention.
– calculated as “zero” minus “ang” (see the UPDATE SQL below)
e.g. UPDATE base.parcel_annotations_med SET rot_ang = 0 – ang;

smathermather:

This final step step converts annotation rotation values, which are rotated from horizontal up to 90 degrees either in a positive or negative to GeoServer label rotation convention, which run the full arc of a circle.

We’ll have a post that follows with the final SLD, and screen shot of the great labeling effect.  Stay posted.

Posted in GeoServer, PostGIS | Tagged: , , , , , , , , | 2 Comments »