Cross-layer filtering
Cross-layer filtering provides the ability to find features from layer A that have a certain relationship to features in layer B. This can be used, for example, to find all bus stops within a given distance from a specified shop, or to find all coffee shops contained in a specified city district.
The querylayer module adds filter functions that implement cross-layer filtering. The functions work by querying a secondary layer within a filter being applied to a primary layer. The name of the secondary layer and an attribute to extract from it are provided as arguments, along with an ECQL filter expression to determine which features are of interest. A common use case is to extract a geometry-valued attribute, and then use the value(s) in a spatial predicate against a geometry attribute in the primary layer.
Filter functions are widely supported in GeoServer, so cross-layer filtering can be used in SLD rules and WMS and WFS requests, in either XML or CQL filters.
Installing the querylayer module
-
Visit the website download page, locate your release, and download: geoserver-2.24.2-querylayer-plugin.zip (nightly geoserver-2.24-SNAPSHOT-querylayer-plugin.zip)
::: warning ::: title Warning :::
The version of the extension must match the version of the GeoServer instance :::
-
Extract the contents of the extension archive into the
WEB-INF/lib
directory of the GeoServer installation. - To check the module is properly installed request the WFS 1.1 capabilities from the GeoServer home page. The
Filter_Capabilities
section should contain a reference to a function namedqueryCollection
.
...
<ogc:Filter_Capabilities>
...
<ogc:ArithmeticOperators>
...
<ogc:Functions>
<ogc:FunctionNames>
...
<ogc:FunctionName nArgs="-1">queryCollection</ogc:FunctionName>
<ogc:FunctionName nArgs="-1">querySingle</ogc:FunctionName>
...
</ogc:FunctionNames>
</ogc:Functions>
</ogc:ArithmeticOperators>
</ogc:Scalar_Capabilities>
...
</ogc:Filter_Capabilities>
...
Function reference
The extension provides the following filter functions to support cross-layer filtering.
Name | Arguments | Description |
querySingle | layer : String, attribute : String, filter : String |
Queries the specified layer applying the specified ECQL filter and returns the value of attribute from the first feature in the result set. The layer name must be qualified (e.g. topp:states ). If no filtering is desired use the filter INCLUDE . |
queryCollection | layer : String, attribute : String, filter : String |
Queries the specified layer applying the specified ECQL filter and returns a list containing the value of attribute for every feature in the result set. The layer name must be qualified (e.g. topp:states ). If no filtering is desired use the filter INCLUDE . An exception is thrown if too many results are collected (see Memory Limits). |
collectGeometries | geometries : a list of Geometry objects |
Converts a list of geometries into a single Geometry object. The output of queryCollection must be converted by this function in order to use it in spatial filter expressions (since geometry lists cannot be used directly). An exception is thrown if too many coordinates are collected (see Memory Limits). |
Optimizing performance
In the GeoServer 2.1.x series, in order to have cross-layer filters execute with optimal performance it is necessary to specify the following system variable when starting the JVM:
-Dorg.geotools.filter.function.simplify=true
This ensures the functions are evaluated once per query, instead of once per result feature. This flag is not necessary for the GeoServer 2.2.x series. (Hopefully this behavior will become the default in 2.1.x as well.)
Memory limits
The queryCollection
and collectGeometries
functions do not perform a true database-style join. Instead they execute a query against the secondary layer every time they are executed, and load the entire result into memory. The functions thus risk using excessive server memory if the query result set is very large, or if the collected geometries are very large. To prevent impacting server stability there are built-in limits to how much data can be processed:
- at most 1000 features are collected by
queryCollection
- at most 37000 coordinates (1MB worth of Coordinate objects) are collected by
collectGeometries
These limits can be overridden by setting alternate values for the following parameters (this can be done using JVM system variables, servlet context variables, or environment variables):
QUERY_LAYER_MAX_FEATURES
controls the maximum number of features collected byqueryCollection
GEOMETRY_COLLECT_MAX_COORDINATES
controls the maximum number of coordinates collected bycollectGeometries
WMS Examples
The following examples use the sf:bugsites
, sf:roads
and sf:restricted
demo layers available in the standard GeoServer download.
- Display only the bug sites overlapping the restricted area whose category is 3:
The CQL cross-layer filter on the bugsites
layer is
INTERSECTS(the_geom, querySingle('restricted', 'the_geom','cat = 3'))
.
The WMS request is:
http://localhost:8080/geoserver/wms?LAYERS=sf%3Aroads%2Csf%3Arestricted%2Csf%3Abugsites&STYLES=&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A26713&CQL_FILTER=INCLUDE%3BINCLUDE%3BINTERSECTS(the_geom%2C%20querySingle(%27restricted%27%2C%20%27the_geom%27%2C%27cat%20%3D%203%27))&BBOX=589081.6705629,4914128.1213261,609174.02430924,4928177.0717971&WIDTH=512&HEIGHT=358
The result is:
- Display all bug sites within 200 meters of any road:
The CQL cross-layer filter on the bugsites
layer is
DWITHIN(the_geom, collectGeometries(queryCollection('sf:roads','the_geom','INCLUDE')), 200, meters)
.
The WMS request is:
http://localhost:8080/geoserver/wms?LAYERS=sf%3Aroads%2Csf%3Arestricted%2Csf%3Abugsites&STYLES=&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A26713&CQL_FILTER=INCLUDE%3BINCLUDE%3BDWITHIN(the_geom%2C%20collectGeometries(queryCollection(%27sf%3Aroads%27%2C%27the_geom%27%2C%27INCLUDE%27))%2C%20200%2C%20meters)&BBOX=589042.42768447,4914010.3926913,609134.78143081,4928059.3431623&WIDTH=512&HEIGHT=358
The result is:
WFS Examples
The following examples use the sf:bugsites
, sf:roads
and sf:restricted
demo layers available in the standard GeoServer download.
- Retrieve only the bug sites overlapping the restricted area whose category is 3:
<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs"
xmlns:sf="http://www.openplans.org/spearfish"
xmlns:ogc="http://www.opengis.net/ogc"
service="WFS" version="1.0.0">
<wfs:Query typeName="sf:bugsites">
<ogc:Filter>
<ogc:Intersects>
<ogc:PropertyName>the_geom</ogc:PropertyName>
<ogc:Function name="querySingle">
<ogc:Literal>sf:restricted</ogc:Literal>
<ogc:Literal>the_geom</ogc:Literal>
<ogc:Literal>cat = 3</ogc:Literal>
</ogc:Function>
</ogc:Intersects>
</ogc:Filter>
</wfs:Query>
</wfs:GetFeature>
- Retrieve all bugsites within 200 meters of any road:
<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs"
xmlns:sf="http://www.openplans.org/spearfish"
xmlns:ogc="http://www.opengis.net/ogc"
service="WFS" version="1.0.0">
<wfs:Query typeName="sf:bugsites">
<ogc:Filter>
<ogc:DWithin>
<ogc:PropertyName>the_geom</ogc:PropertyName>
<ogc:Function name="collectGeometries">
<ogc:Function name="queryCollection">
<ogc:Literal>sf:roads</ogc:Literal>
<ogc:Literal>the_geom</ogc:Literal>
<ogc:Literal>INCLUDE</ogc:Literal>
</ogc:Function>
</ogc:Function>
<ogc:Distance units="meter">100</ogc:Distance>
</ogc:DWithin>
</ogc:Filter>
</wfs:Query>
</wfs:GetFeature>