Spatial Operations¶
Spatial queries are also available in Cypher via
distance(),contains(),intersects(),centroid(),area(),perimeter(), andpoint(). See the Cypher reference for details.
Spatial Types¶
Declare spatial properties via column_types when loading data. This enables auto-resolution in Cypher queries and fluent API methods.
Type |
Cardinality |
Purpose |
|---|---|---|
|
0..1 per type |
Primary lat/lon coordinate |
|
0..1 per type |
Primary WKT geometry |
|
0..N |
Named lat/lon coordinates |
|
0..N |
Named WKT geometries |
graph.add_nodes(df, 'Field', 'id', 'name', column_types={
'latitude': 'location.lat',
'longitude': 'location.lon',
'wkt_polygon': 'geometry',
})
With spatial types declared, queries become simpler:
# Auto-resolves location fields — no lat_field/lon_field needed
graph.select('Field').near_point_m(center_lat=60.5, center_lon=3.2, max_distance_m=50000.0)
# Cypher distance between nodes — resolves via location, falls back to geometry centroid
graph.cypher("""
MATCH (a:Field {name:'Troll'}), (b:Field {name:'Draugen'})
RETURN distance(a, b) AS dist_m
""")
# Node-aware spatial functions — auto-resolve geometry from spatial config
graph.cypher("MATCH (c:City), (a:Area) WHERE contains(a, c) RETURN c.name, a.name")
graph.cypher("MATCH (n:Field) RETURN n.name, area(n) AS m2, centroid(n) AS center")
graph.cypher("MATCH (a:Field), (b:Field) WHERE intersects(a, b) RETURN a.name, b.name")
# Virtual properties
graph.cypher("MATCH (n:Field) RETURN n.name, n.location, n.geometry")
Multiple Named Points and Shapes¶
graph.add_nodes(df, 'Well', 'id', 'name', column_types={
'surface_lat': 'location.lat',
'surface_lon': 'location.lon',
'bh_lat': 'point.bottom_hole.lat',
'bh_lon': 'point.bottom_hole.lon',
'boundary_wkt': 'shape.boundary',
})
# Distance between named points
graph.cypher("... RETURN distance(a.bottom_hole, b.bottom_hole)")
Retroactive Configuration¶
graph.set_spatial('Field',
location=('latitude', 'longitude'),
geometry='wkt_polygon',
)
Bounding Box¶
# With spatial config — field names auto-resolved
graph.select('Discovery').within_bounds(
min_lat=58.0, max_lat=62.0, min_lon=1.0, max_lon=5.0
)
# Without spatial config — explicit field names
graph.select('Discovery').within_bounds(
lat_field='latitude', lon_field='longitude',
min_lat=58.0, max_lat=62.0, min_lon=1.0, max_lon=5.0
)
Distance Queries (Geodesic)¶
graph.select('Wellbore').near_point_m(
center_lat=60.5, center_lon=3.2, max_distance_m=50000.0
)
WKT Geometry Intersection¶
graph.select('Field').intersects_geometry(
'POLYGON((1 58, 5 58, 5 62, 1 62, 1 58))'
)
Accepts WKT strings or shapely geometry objects:
from shapely.geometry import box
graph.select('Field').intersects_geometry(box(1, 58, 5, 62))
Point-in-Polygon¶
graph.select('Block').contains_point(lat=60.5, lon=3.2)
Constructive geometry (Cypher)¶
Beyond the predicates (distance/contains/intersects) and measures
(area/perimeter/centroid), Cypher exposes constructive operators that
build new geometry from existing geometry. They return WKT, so results chain
into other spatial functions or land in a GeoDataFrame.
Function |
Returns |
Use |
|---|---|---|
|
MultiPolygon |
safety/exclusion zone around a point or shape |
|
MultiPolygon |
merge overlapping areas into one footprint |
|
MultiPolygon |
the overlap between two areas |
|
MultiPolygon |
|
|
Polygon |
tightest hull over a set of points/shapes |
# A 5 km exclusion zone around a platform
graph.cypher("RETURN geom_buffer('POINT(10.7 59.9)', 5000) AS zone")
# Merge two licence areas into a single operating footprint
graph.cypher("""
MATCH (a:Licence {id:'A'}), (b:Licence {id:'B'})
RETURN geom_union(a.geometry, b.geometry) AS footprint
""")
# Catchment hull over every well in a field
graph.cypher("""
MATCH (w:Wellbore)-[:IN_FIELD]->(:Field {name:'Troll'})
WITH collect(w.geometry) AS shapes
RETURN geom_convex_hull(shapes) AS catchment
""")
geom_buffer builds a planar buffer at the geometry’s centroid latitude
(accurate locally; it degrades far from the centroid). geom_convex_hull
also accepts variadic arguments, not just a list.
GeoDataFrame Export¶
Convert query results with WKT columns to geopandas GeoDataFrames:
rv = graph.cypher("MATCH (n:Field) RETURN n.name, n.geometry")
gdf = rv.to_gdf(geometry_column='n.geometry', crs='EPSG:4326')