GeoSPARQL support in QLever - ad-freiburg/qlever GitHub Wiki
QLever has limited support for features from the OGC GeoSPARQL standard. Additionally, QLever supports some custom spatial querying features.
Currently QLever implements these functions from GeoSPARQL:
The function geof:distance(?a, ?b, ?unit)
currently expects two values with geo:wktLiteral
datatype and optionally a unit of measurement as an IRI or literal with xsd:anyURI
datatype. Units are expressed using the qudt
vocabulary as recommended in the GeoSPARQL standard. If no unit is given, the function returns the distance in kilometers. QLever also supports the geof:metricDistance(?a, ?b)
function, which takes two points like geof:distance
but always returns meters.
Currently, both functions only support POINT
literals. They will be extended to other geometry types in the near future.
For a fast distance-based search, please also see GeoSPARQL Maximum Distance Search below.
- Meters:
http://qudt.org/vocab/unit/M
- Kilometers:
http://qudt.org/vocab/unit/KiloM
- Land miles:
http://qudt.org/vocab/unit/MI
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
PREFIX unit: <http://qudt.org/vocab/unit/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT * WHERE {
BIND ("POINT(7.8412948 47.9977308)"^^geo:wktLiteral AS ?a) # Freiburg Central Railway Station
BIND ("POINT(7.8450491 47.9946000)"^^geo:wktLiteral AS ?b) # Freiburg University Library
BIND (geof:distance(?a, ?b, unit:M) AS ?d_meters) # 446.363 m
BIND (geof:distance(?a, ?b, "http://qudt.org/vocab/unit/M"^^xsd:anyURI) AS ?d_meters2) # 446.363 m
BIND (geof:metricDistance(?a, ?b) AS ?d_meters3) # 446.363 m
BIND (geof:distance(?a, ?b, unit:KiloM) AS ?d_kilometers) # 0.446363 km
BIND (geof:distance(?a, ?b, "http://qudt.org/vocab/unit/KiloM"^^xsd:anyURI) AS ?d_kilometers2) # 0.446363 km
BIND (geof:distance(?a, ?b, unit:MI) AS ?d_miles) # 0.277357 mi
BIND (geof:distance(?a, ?b, "http://qudt.org/vocab/unit/MI"^^xsd:anyURI) AS ?d_miles2) # 0.277357 mi
# For backward-compatibility
BIND (geof:distance(?a, ?b) AS ?d_kilometers3) # 0.446363 km
}
The functions geof:latitude(?x)
and geof:longitude(?x)
extract the latitude or longitude coordinate from a valid coordinate point with geo:wktLiteral
datatype.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
SELECT * WHERE {
BIND ("POINT(7.8412948 47.9977308)"^^geo:wktLiteral AS ?a) # Freiburg Central Railway Station
BIND (geof:latitude(?a) AS ?lat) # 47.9977
BIND (geof:longitude(?a) AS ?lng) # 7.84129
}
The function geof:centroid
provides the centroid of a WKT literal, as a POINT
WKT literal.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
SELECT * WHERE {
BIND(geof:centroid("POLYGON((2 4, 4 4, 4 2, 2 2, 2 4))"^^geo:wktLiteral) AS ?c)
# "POINT(3 3)"^^geo:wktLiteral
}
The function geof:envelope
provides the bounding box of a WKT literal, as a POLYGON
WKT literal.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
SELECT * {
BIND(geof:envelope("LINESTRING(2 4, 8 6)"^^geo:wktLiteral) AS ?envelope)
# Result: "POLYGON((2 4,8 4,8 6,2 6,2 4))"^^geo:wktLiteral
}
The function geof:geometryType
returns an xsd:anyURI
literal containing the identifier of the given WKT literal. For a geometry of a supported type, the type identifier will be one of the following.
http://www.opengis.net/ont/sf#Point
http://www.opengis.net/ont/sf#LineString
http://www.opengis.net/ont/sf#Polygon
http://www.opengis.net/ont/sf#MultiPoint
http://www.opengis.net/ont/sf#MultiLineString
http://www.opengis.net/ont/sf#MultiPolygon
http://www.opengis.net/ont/sf#GeometryCollection
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT * {
BIND(geof:geometryType("LINESTRING(2 4, 8 6)"^^geo:wktLiteral) AS ?geometryType)
# Result: "http://www.opengis.net/ont/sf#LineString"^^xsd:anyURI
}
The functions geof:minX
, geof:minY
, geof:maxX
and geof:maxY
return the bounding coordinates of the geometry represented by the given WKT literal.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
SELECT * {
BIND("LINESTRING(2 4, 3 3, 6 8)"^^geo:wktLiteral AS ?geometry)
BIND (geof:minX(?geometry) AS ?minX) # Result: 2
BIND (geof:minY(?geometry) AS ?minY) # Result: 3
BIND (geof:maxX(?geometry) AS ?maxX) # Result: 6
BIND (geof:maxY(?geometry) AS ?maxY) # Result: 8
}
QLever can preprocess geometries to accelerate various queries. This can be requested via the option --vocabulary-type on-disk-compressed-geo-split
of IndexBuilderMain
and qlever index
. If this option is used, QLever will currently precompute centroid, bounding boxes and geometry types of all WKT literals in the input dataset. These can be used for the respective GeoSPARQL functions, but also for further optimizations currently in development. If you use this option, please expect that you have to rebuild your index multiple times in the coming weeks and months while QLever is being updated to support more GeoSPARQL features efficiently. The server will report an error during startup if an index rebuild is necessary.
QLever supports each of the following query patterns for a fast maximum distance search:
FILTER(geof:distance(?geom1, ?geom2) <= constant)
FILTER(geof:metricDistance(?geom1, ?geom2) <= constant)
FILTER(geof:distance(?geom1, ?geom2, <some-supported-unit-iri>) <= constant)
FILTER(geof:distance(?geom1, ?geom2, "some-supported-unit-iri"^^xsd:anyURI) <= constant)
In the Analysis view of the QLever UI you can see Spatial Join instead of Cartesian Product Join, when the optimization is in effect. The GeoSPARQL maximum distance search is a standard syntax method for using the QLever Spatial Search. The custom feature provides more options, for example nearest neighbor search.
The implementation currently has to parse WKT geometries for all geometry types except points. This is being worked on, so you may expect a performance improvement in the future.
Current quirk: The maximum distance search (each of the FILTER
patterns above) supports the WKT geometry types POINT
, LINESTRING
, POLYGON
, MULTIPOINT
, MULTILINESTRING
, MULTIPOLYGON
and GEOMETRYCOLLECTION
, while the non-optimized geof:distance
and geof:metricDistance
implementation only supports POINT
so far.
All pairs of restaurants within 50 meters of public transport stops:
SELECT ?restaurant ?stop ?restaurant_geometry WHERE {
?restaurant osmkey:amenity "restaurant" ;
geo:hasGeometry/geo:asWKT ?restaurant_geometry .
?stop osmkey:public_transport "stop_position" ;
geo:hasGeometry/geo:asWKT ?stop_geometry .
FILTER (geof:metricDistance(?restaurant_geometry,?stop_geometry) <= 50)
}
QLever supports each of the following query patterns for a GeoSPARQL geometric relation functions:
FILTER (geof:sfIntersects(?geom1, ?geom2))
FILTER (geof:sfContains(?geom1, ?geom2))
FILTER (geof:sfCovers(?geom1, ?geom2))
FILTER (geof:sfCrosses(?geom1, ?geom2))
FILTER (geof:sfTouches(?geom1, ?geom2))
FILTER (geof:sfEquals(?geom1, ?geom2))
FILTER (geof:sfOverlaps(?geom1, ?geom2))
FILTER (geof:sfWithin(?geom1, ?geom2))
These GeoSPARQL-compliant filters are a standard syntax method for using the QLever Spatial Search with qlss:algorithm
set to qlss:libspatialjoin
and qlss:joinType
set appropriately.
Note: Currently, the functions stated above are only supported in FILTER
s between two different variables. Also there may not be multiple filters on the same pair of variables. Otherwise the query processing will return an error. This will be fixed in the near future. Also, the implementation currently has to parse WKT geometries for all geometry types except points. This is being worked on, so you may expect a performance improvement in the future.
This query finds all railway lines crossing a river.
SELECT ?river ?rail ?rail_geometry WHERE {
?river osmkey:waterway "river" ;
geo:hasGeometry/geo:asWKT ?river_geometry .
?rail osmkey:railway "rail" ;
geo:hasGeometry/geo:asWKT ?rail_geometry .
FILTER (geof:sfIntersects(?rail_geometry,?river_geometry))
}
Try it: https://qlever.cs.uni-freiburg.de/osm-planet/FKzeVH
For OpenStreetMap data, geometric relations can be precomputed as part of the dataset (e.g. ogc:sfContains
, ogc:sfIntersects
, ... triples) using osm2rdf
. Geometries from osm2rdf
are represented as geo:wktLiteral
s, which can be addressed by geo:hasGeometry/geo:asWKT
. osm2rdf
also provides centroids of objects via geo:hasCentroid/geo:asWKT
and more, if requested. Please note that the geometric relations are given as triples between the OpenStreetMap entities, not the geometries.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
SELECT ?osm_id ?hasgeometry WHERE {
osmrel:62768 ogc:sfContains ?osm_id .
?osm_id geo:hasGeometry/geo:asWKT ?hasgeometry .
?osm_id osmkey:building [] .
}
Try it: https://qlever.cs.uni-freiburg.de/osm-planet/7cxklb
QLever supports a custom fast spatial search operation for geometries from literals with geo:wktLiteral
datatype. It can be invoked using a SERVICE
operation to the IRI <https://qlever.cs.uni-freiburg.de/spatialSearch/>
. Note that this address is not contacted but only used to activate the feature locally.
A spatial query has the following form:
PREFIX qlss: <https://qlever.cs.uni-freiburg.de/spatialSearch/>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT * WHERE {
# Arbitrary operations that select ?left_geometry
?some_entity geo:hasCentroid/geo:asWKT ?left_geometry .
SERVICE qlss: {
_:config qlss:algorithm qlss:s2 ;
qlss:left ?left_geometry ;
qlss:right ?right_geometry ;
qlss:numNearestNeighbors 2 ;
qlss:maxDistance 500 ;
qlss:bindDistance ?dist_left_right ;
qlss:payload ?payloadA , ?payloadB .
{
# Any subquery, that selects ?right_geometry, ?payloadA and ?payloadB
?some_other_entity geo:hasCentroid/geo:asWKT ?right_geometry .
# ...
}
}
}
The SERVICE
must include the configuration triples and exactly one group graph pattern that selects the right geometry. If numNearestNeighbors
is not used, the right geometry may also be provided outside of the SERVICE
definition.
The following configuration parameters are provided in the SERVICE
as triples with arbitrary subject. The predicate must be an IRI of the form <parameter>
or qlss:parameter
. The parameters left
and right
are mandatory. Additionally you must provide search instructions, either numNearestNeighbors
or maxDistance
or joinType
. The remaining parameters are optional.
Parameter | Domain | Description |
---|---|---|
algorithm |
<baseline> , <s2> , <boundingBox> , <libspatialjoin>
|
The algorithm to use. |
left |
variable | The left join table: "for every [left] geometry ...". Must refer to a column with literals of geo:wktLiteral datatype. |
right |
variable | The right join table: "... find the closest/all intersecting/... [right] geometries". Must refer to a column with literals of geo:wktLiteral datatype. |
numNearestNeighbors |
integer | The maximum number of nearest neighbor points from right for every point from left . Only supported by the baseline and s2 algorithms. |
maxDistance |
integer | The maximum distance in meters between points from left and right to be included in the result. |
bindDistance |
variable | An otherwise unbound variable name which will be used to give the distance in kilometers between the result point pairs. |
payload |
variable or IRI <all>
|
Variable from the group graph pattern inside the SERVICE to be included in the result. right is automatically included. This parameter may be repeated to include multiple variables. For all variables use <all> . If right is given outside of the SERVICE do not use this parameter. |
joinType |
<intersects> , <covers> , <contains> , <touches> , <crosses> , <overlaps> , <equals> , <within-dist>
|
The geometric relation to compute between the left and right geometries. If within-dist is chosen, the maxDistance parameter is required. Mandatory when using the libspatialjoin algorithm and illegal for all other algorithms. |
Note: The individual algorithms support different subsets of all valid literals of geo:wktLiteral
datatype. The libspatialjoin
algorithm supports POINT
, LINESTRING
, POLYGON
, MULTIPOINT
, MULTILINESTRING
, MULTIPOLYGON
and GEOMETRYCOLLECTION
. The baseline
and boundingBox
algorithms support the same literals except GEOMETRYCOLLECTION
. The s2
algorithm currently only works with POINT
literals.
Note: Geometries except for points currently need to be parsed for every query leading to longer running times. We are working on it.
This example query calculates the three closest supermarkets to every railway station.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX qlss: <https://qlever.cs.uni-freiburg.de/spatialSearch/>
SELECT * WHERE {
?station osmkey:railway "station" ;
osmkey:name ?name ;
geo:hasCentroid/geo:asWKT ?station_geometry .
SERVICE qlss: {
_:config qlss:left ?station_geometry ;
qlss:right ?supermarket_geometry ;
qlss:numNearestNeighbors 3 .
{
?supermarket osmkey:shop "supermarket" ;
geo:hasCentroid/geo:asWKT ?supermarket_geometry .
}
}
}
Try it: https://qlever.cs.uni-freiburg.de/osm-planet/OXupEH
This query returns all railway line segments in Germany intersecting rivers (no matter whether by bridge, tunnel, etc.).
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX qlss: <https://qlever.cs.uni-freiburg.de/spatialSearch/>
SELECT DISTINCT ?rail ?rail_geometry WHERE {
osmrel:51477 ogc:sfIntersects ?river .
?river osmkey:water "river" .
?river geo:hasGeometry/geo:asWKT ?river_geometry .
SERVICE qlss: {
_:config qlss:algorithm qlss:libspatialjoin ;
qlss:left ?river_geometry ;
qlss:right ?rail_geometry ;
qlss:payload ?rail ;
qlss:joinType qlss:intersects .
{
osmrel:51477 ogc:sfContains ?rail .
?rail osmkey:railway "rail" .
?rail geo:hasGeometry/geo:asWKT ?rail_geometry .
}
}
}
Try it: https://qlever.cs.uni-freiburg.de/osm-planet/5QvF74
As a shortcut, a special predicate <max-distance-in-meters:m>
is also supported. The parameter m
refers to the maximum search radius in meters. It may be used as a triple with the left join variable as subject and the right join variable as object.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT * WHERE {
?a geo:hasCentroid/geo:asWKT ?left_geometry .
?left_geometry <max-distance-in-meters:300> ?right_geometry .
?b geo:hasCentroid/geo:asWKT ?right_geometry .
}
This feature is deprecated and will produce a warning, due to confusing semantics. Please use the SERVICE
syntax instead.
A spatial search for nearest neighbors can be realized using ?left <nearest-neighbors:k:m> ?right
. Please replace k
and m
with integers as follows:
- For each point
?left
QLever will output thek
nearest points from?right
. Of course, the sets?left
and?right
can each be limited using further statements. - Using the optional integer value
m
a maximum distance in meters can be given that restricts the search radius.
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT * WHERE {
?a geo:hasCentroid/geo:asWKT ?left_geometry .
?left_geometry <nearest-neighbors:2:1000> ?right_geometry .
?b geo:hasCentroid/geo:asWKT ?right_geometry .
}