Spatial relations
A spatial relation (or spatial predicate) is a yes-or-no question about two geometries: does A contain B? Do they touch? Selection by location, summary by location and every join by location depend on one. The relations are defined in the OGC Simple Features standard, and the same set appears, with nearly the same names, in most GIS software: as tick boxes in QGIS, as functions in spatial SQL (ST_Intersects, ST_Contains, … in PostGIS and in GeoPackage/SpatiaLite), as predicate= in GeoPandas. ArcGIS’s Select Layer By Location uses its own names and adds some, such as have their center in.
Interior, boundary and exterior
Each relation is defined by how the interior, boundary and exterior of A and B meet (the dimensionally extended nine-intersection model, DE-9IM).
| Geometry | Interior | Boundary |
|---|---|---|
| Point | the point itself | none |
| Line | the line without its two end points | the two end points |
| Polygon | the area inside the outline | the outline |
Everything else is the exterior. The boundary causes most surprises: a point lying exactly on a polygon’s outline is not inside it.
The relations
| QGIS | SQL | ”A … B” is true when | Direction |
|---|---|---|---|
| intersect | ST_Intersects | A and B have at least one point in common, interior or boundary | symmetric |
| disjoint | ST_Disjoint | A and B have no point in common: the opposite of intersect | symmetric |
| contain | ST_Contains | no part of B lies outside A, and their interiors meet | A contains B = B is within A |
| are within | ST_Within | no part of A lies outside B, and their interiors meet | A is within B = B contains A |
| equal | ST_Equals | A and B are the same shape: each lies within the other | symmetric |
| touch | ST_Touches | A and B meet only at their boundaries; their interiors do not meet | symmetric |
| overlap | ST_Overlaps | A and B have the same dimension (two polygons, two lines), share part of their interiors, and each also has a part outside the other | symmetric |
| cross | ST_Crosses | their interiors meet, but in something of lower dimension: a line passing in and out of a polygon, two lines crossing at a point | symmetric |
Spatial SQL also has ST_Covers and ST_CoveredBy: like contain and within, but a B lying only on A’s boundary counts too. The QGIS dialogs do not offer them.
Reading the QGIS dialog
QGIS’s location tools read as a sentence: [features in the first layer] [relation] [features in the second layer]. In Join attributes by location (summary):
Join to features in roder — where the features contain — by comparing to boliger
asks for each rode which dwellings it contains. Ticking are within instead asks whether a rode lies within a dwelling point. That is never true, so every rode gets a count of 0, without any warning. Ticking several boxes means or: a feature is matched if any of the ticked relations holds.
| First layer | Second layer | Relation |
|---|---|---|
| zones | points | contain or intersect |
| points | zones | are within or intersect |
| zones | lines (roads, streams) | intersect: any part in the zone; contain: the whole line in the zone; cross: passes through the zone’s outline |
| zones | polygons (buildings, fields) | intersect: any part in the zone; contain: wholly in the zone; overlap: partly in the zone |
| polygons | polygons of the same layer | touch: the neighbours |
Points on a border
Two square zones, A and B, side by side, and four points: one inside A, one on the border between A and B, one on A’s outer edge and one outside both. In QGIS 4.2.2, Join attributes by location (summary) with the zones as the first layer gives:
| Point | intersect | contain | touch |
|---|---|---|---|
| inside A | A | A | — |
| on the border between A and B | A and B | neither | A and B |
| on A’s outer edge | A | none | A |
| outside both | — | — | — |
So intersect counts a point on a shared border in both zones, and contain counts it in none: either way the counts no longer add up to the number of points. Touch finds exactly the points on borders, which makes it a check.
With real coordinates, exact border positions are rare. None of Copenhagen’s 349,743 dwelling address points lies on the border of a rode, kvarter or bydel, and intersect and contain give the same counts (example data). The choice matters when points lie on lines by construction: points snapped to a road centre line or a plot boundary, or coordinates rounded to whole metres counted in a grid whose edges lie on whole metres.
Polygons across zone borders
A building or field that crosses a zone border intersects and overlaps both zones and is within neither. Counted with intersect it is counted twice; with contain it is not counted at all. The choices:
- assign it to one zone: the zone it overlaps most (Join attributes by location, one-to-one, largest overlap), or the zone that contains a point placed inside it;
- split it at the border and share its values by area (overlay, then area-weighting);
- count it in every zone it touches, and say that the totals add up to more than the whole.
Coordinates must match exactly
Equal and touch hold only when coordinates match exactly. Two layers digitised separately rarely share their boundaries exactly. Neighbours that should touch either overlap by a few centimetres or leave a thin gap, so touch finds nothing and overlap finds pairs that are really neighbours. A tolerance helps: in SQL ST_DWithin, in QGIS a small buffer or a distance-based tool.
Related: selection by location, summary by location, Modifiable areal unit problem.