GeoServer WMS – Clipping Geometry Against a Polygon in GeoServer WMS Filtering

cql-filterfiltergeoserversldwms

My data set is a series of multilinestrings representing roads. During a WMS GetMap request, I want to specify a polygon and display only the portions of those roads that fall within the polygon. I am fine using ECQL or SLD.

My current CQL_FILTER looks like:

INTERSECTS(wkb_geometry, POLYGON((-117 34 ... ...)))

This returns all roads intersecting the polygon, but displays the full extent of those roads – i.e. it displays all vertices/segments for the entire road, not just those contained within the polygon. Image example:

enter image description here

Is there a way to extract the vertices from each geometry, and filter against those individual vertices, instead of the entire line?

Something like the following command, although this doesn't work,

INTERSECTS(VERTICES(wkb_geometry), POLYGON((... ...))))

Failing with the error message:

Rendering process failed Binary geometry filter, but first expression is not a property name? (it's a class org.geotools.filter.function.FilterFunction_vertices)

This makes sense, as VERTICES(wkb_geometry) doesn't actually return a property of the dataset, but an entirely new set of data. I assume this is called "geometry transformation", but I can't figure out whether it even CAN be done in this way, and if so, how.

Best Answer

Figured it out!

enter image description here

The following SLD uses a rendering transformation to call "gs:Clip", which clips vector features to a bounding geometry. The result is then rasterized by GeoServer's WMS.

<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor version="1.0.0"
                       xmlns:ogc="http://www.opengis.net/ows"
                       xmlns:gml="http://www.opengis.net/gml">
    <NamedLayer>
        <Name>as15:placemarks_line</Name>
        <UserStyle>
            <FeatureTypeStyle>
                <Transformation>
                    <Function name="gs:Clip">
                        <Function name="parameter">
                            <Literal>features</Literal>
                        </Function>
                        <Function name="parameter">
                            <Literal>clip</Literal>
                            <Literal>POLYGON((
                                -118.0 33.8,
                                -118.2 33.8,
                                -118.2 34.0,
                                -118.0 34.0,
                                -118.0 33.8
                                ))
                            </Literal>
                        </Function>
                    </Function>
                </Transformation>
                <Rule>
                    <LineSymbolizer>
                        <Stroke>
                            <CssParameter name="stroke">#0000FF</CssParameter>
                            <CssParameter name="stroke-width">3</CssParameter>
                        </Stroke>
                    </LineSymbolizer>
                </Rule>
            </FeatureTypeStyle>
        </UserStyle>
    </NamedLayer> 

    <UserLayer>
        <Name>Inline</Name>
        <IsDefault>1</IsDefault>
        <InlineFeature>
            <FeatureCollection>
                <featureMember>
                    <feature>
                        <geometryProperty>
                            <gml:Polygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
                                <gml:outerBoundaryIs>
                                    <gml:LinearRing>
                                        <gml:coordinates>
                                            -118.0,33.8 -118.2,33.8 -118.2,34.0 -118.0,34.0 -118.0,33.8
                                        </gml:coordinates>
                                    </gml:LinearRing>
                                </gml:outerBoundaryIs>
                            </gml:Polygon>
                        </geometryProperty>
                        <title></title>
                    </feature>
                </featureMember>
            </FeatureCollection>
        </InlineFeature>
        <UserStyle>
            <FeatureTypeStyle>
                <Rule>
                    <PolygonSymbolizer>
                        <Stroke>
                            <CssParameter name="stroke">#FF0000</CssParameter>
                            <CssParameter name="stroke-width">2</CssParameter>
                        </Stroke>
                    </PolygonSymbolizer>
                </Rule>
            </FeatureTypeStyle>
        </UserStyle>
    </UserLayer>
</StyledLayerDescriptor>

Note that everything after the first NamedLayer element is optional - it's simply drawing the bounding polygon to help visualize the results.

Haven't figured out how to dynamically specify the bounding polygon yet, although it's likely possible via feature definitions or SLD variable substitution.

Also, haven't figured out how to specify an existing style - i.e. when the filter passes, use an existing style (the default, actually), so for now, the roads are simply drawn in a blue style. I'm going to post another question asking about that.