Spatial Relationships and Join#
Spatial Predicates#
Spatial predicates are functions that define the relationships between geometric objects.
They help answer questions like: Do these features intersect? Is one contained within another? Do they just touch at the boundary?
These relationships are essential for spatial analysis, especially when working with spatial joins, where attributes from one layer are assigned to features in another based on how they relate spatially.
Common Spatial Predicates#
Spatial predicates are functions that evaluate geometric relationships between two shapes (points, lines, polygons).
They are the foundation for spatial queries, filtering, and joins.
Here’s a table of the most commonly used binary predicates, with explanations:
Predicate |
Description |
---|---|
intersects |
Returns |
disjoint |
Returns |
within |
Returns |
contains |
Returns |
touches |
Returns |
overlaps |
Returns |
crosses |
Returns |
Understanding spatial predicates helps you write more precise spatial queries and perform meaningful spatial joins and filtering based on how features relate to each other in space.
НАРИСОВАТЬ СВОИ КАРТИНКИ?
Spatial Join#
A spatial join combines two layers by attaching the attributes of one layer to another, based on their spatial relationship — for example, assigning each point to the district it falls within, or checking whether a feature lies inside a buffer zone.
But a spatial join is more than just attaching data where geometries intersect.
To make these joins meaningful and accurate, you need to understand the type of spatial relationship between features:
Are they overlapping? One inside another? Do they only touch at the edge?
That’s where spatial predicates come in — they’re the core tools that let us describe and test topological relationships between geometries.
Прочитаем данные о ДТП
accidents = gpd.read_file('data/spb_dtp.gpkg')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[1], line 1
----> 1 accidents = gpd.read_file('data/spb_dtp.gpkg')
NameError: name 'gpd' is not defined
Пересечем данные о ДТП с зонами доступности школ
accidents_utm = accidents.to_crs(target_crs)
accidents_in_school_zones = gpd.sjoin(
accidents_utm,
schools_buffer,
how="inner",
predicate="intersects"
)
accidents_count = accidents_in_school_zones.groupby('osmid').size()
accidents_count = accidents_count.reset_index()
accidents_count.columns = ['osmid', 'acc_count']
schools_utm_acc = schools_utm.merge(accidents_count, on='osmid', how='left')
schools_utm_acc.explore(column='acc_count',tiles='cartodbpositron')
Spatial Filtering#
One of the most common tasks in spatial analysis is selecting features that meet a specific spatial condition.
For example, you might want to select all points that fall within a given polygon — this is known as spatial filtering, or location-based filtering.
It allows you to narrow down your data based on spatial relationships, such as proximity, containment, or intersection.
Let’s try it in practice:
We’ll filter all cafés in Saint Petersburg that are located within 500 meters of a metro station.
Подготовим данные
#Прочитаем данные о станциях метро
metro = gpd.read_file('data/spb_metro.geojson')
Построим буферы 500-метровой доступности вокруг станций (предварительно проверим систему координат и перепроецируем, если нужно)
metro.crs
<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World.
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich
Данные в географической системе координат, нам это не подходит, поэтому перепроецируем их перед тем как строить буферы
utm_crs = metro.estimate_utm_crs() # автоматически определяем UTM-зону
metro_utm = metro.to_crs(utm_crs)
Построим буферы
metro_buff_500 = metro_utm.buffer(500)
metro_buff_500.explore(tiles='CartoDB positron')
Выгрузим информацию о кафе из OSM, отфильтруем только точки и перепроецируем
tags = {"amenity": "cafe"}
cafes = ox.features_from_place("Санкт-Петербург, Россия", tags)
#фильтрация точек
cafes = cafes[cafes.geometry.type == 'Point']
#перепроецирование
cafes_utm = cafes.to_crs(utm_crs)
cafes_utm.explore(tiles='CartoDB positron')