0
# Shapely Integration
1
2
Integration utilities for converting between GeoAlchemy2 spatial elements and Shapely geometries, enabling advanced geometric operations and analysis with the popular Python geometry library.
3
4
## Installation
5
6
Shapely integration requires the optional dependency:
7
8
```bash
9
pip install GeoAlchemy2[shapely]
10
# or
11
pip install GeoAlchemy2 shapely
12
```
13
14
## Capabilities
15
16
### Element to Shapely Conversion
17
18
Convert GeoAlchemy2 spatial elements to Shapely geometry objects for advanced geometric operations.
19
20
```python { .api }
21
def to_shape(element):
22
"""
23
Convert a GeoAlchemy2 spatial element to a Shapely geometry.
24
25
Parameters:
26
- element: Union[WKBElement, WKTElement], spatial element to convert
27
28
Returns:
29
Shapely geometry object (Point, LineString, Polygon, etc.)
30
31
Raises:
32
ImportError: if Shapely is not installed
33
"""
34
```
35
36
Usage examples:
37
38
```python
39
from geoalchemy2.shape import to_shape
40
from geoalchemy2 import WKTElement
41
42
# Convert WKT element to Shapely
43
wkt_elem = WKTElement('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', srid=4326)
44
shapely_geom = to_shape(wkt_elem)
45
46
# Query database and convert result
47
lake = session.query(Lake).first()
48
shapely_lake = to_shape(lake.geom) # lake.geom is WKBElement
49
50
# Use Shapely operations
51
print(f"Area: {shapely_lake.area}")
52
print(f"Centroid: {shapely_lake.centroid}")
53
print(f"Bounds: {shapely_lake.bounds}")
54
55
# Advanced Shapely operations
56
simplified = shapely_lake.simplify(0.1)
57
buffered = shapely_lake.buffer(100)
58
convex_hull = shapely_lake.convex_hull
59
60
# Check geometric properties
61
print(f"Is valid: {shapely_lake.is_valid}")
62
print(f"Is simple: {shapely_lake.is_simple}")
63
print(f"Geometry type: {shapely_lake.geom_type}")
64
```
65
66
### Shapely to Element Conversion
67
68
Convert Shapely geometry objects back to GeoAlchemy2 spatial elements for database storage.
69
70
```python { .api }
71
def from_shape(shape, srid: int = -1, extended: bool = None):
72
"""
73
Convert a Shapely geometry to a GeoAlchemy2 spatial element.
74
75
Parameters:
76
- shape: Shapely geometry object
77
- srid: int, spatial reference system identifier (default: -1)
78
- extended: bool, use extended WKB format (default: None)
79
80
Returns:
81
WKBElement representing the Shapely geometry
82
83
Raises:
84
ImportError: if Shapely is not installed
85
"""
86
```
87
88
Usage examples:
89
90
```python
91
from geoalchemy2.shape import from_shape
92
from shapely.geometry import Point, Polygon, LineString
93
from shapely import wkt
94
95
# Create Shapely geometries
96
point = Point(1, 2)
97
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
98
line = LineString([(0, 0), (1, 1), (2, 0)])
99
100
# Convert to GeoAlchemy2 elements
101
point_elem = from_shape(point, srid=4326)
102
polygon_elem = from_shape(polygon, srid=4326)
103
line_elem = from_shape(line, srid=4326)
104
105
# Store in database
106
new_lake = Lake(name='Shapely Lake', geom=polygon_elem)
107
session.add(new_lake)
108
session.commit()
109
110
# Load from WKT and convert
111
shapely_from_wkt = wkt.loads('MULTIPOINT((0 0), (1 1), (2 2))')
112
multipoint_elem = from_shape(shapely_from_wkt, srid=4326)
113
```
114
115
### Dependency Management
116
117
Handle optional Shapely dependency gracefully with built-in availability checking.
118
119
```python { .api }
120
HAS_SHAPELY: bool
121
"""Boolean indicating whether Shapely is available."""
122
123
def check_shapely():
124
"""
125
Context manager to verify Shapely availability.
126
127
Raises:
128
ImportError: if Shapely is not installed with helpful message
129
"""
130
```
131
132
Usage examples:
133
134
```python
135
from geoalchemy2.shape import HAS_SHAPELY, check_shapely
136
137
# Check availability before using
138
if HAS_SHAPELY:
139
from geoalchemy2.shape import to_shape, from_shape
140
# Use Shapely functions
141
else:
142
print("Shapely not available")
143
144
# Use context manager for safe operations
145
try:
146
with check_shapely():
147
shapely_geom = to_shape(spatial_element)
148
# Perform Shapely operations
149
except ImportError as e:
150
print(f"Shapely required: {e}")
151
```
152
153
## Integration Workflows
154
155
### Database Query to Shapely Analysis
156
157
Complete workflow from database query to Shapely-based analysis:
158
159
```python
160
from geoalchemy2.shape import to_shape
161
from shapely.ops import unary_union, cascaded_union
162
from shapely.geometry import Point
163
164
# Query spatial data from database
165
buildings = session.query(Building).filter(
166
Building.district_id == district_id
167
).all()
168
169
# Convert all geometries to Shapely
170
shapely_buildings = [to_shape(b.geom) for b in buildings]
171
172
# Perform complex Shapely operations
173
total_footprint = unary_union(shapely_buildings)
174
building_centroids = [geom.centroid for geom in shapely_buildings]
175
176
# Analysis with Shapely
177
total_area = total_footprint.area
178
avg_building_area = sum(g.area for g in shapely_buildings) / len(shapely_buildings)
179
180
# Find buildings near a point
181
query_point = Point(x, y)
182
nearby_buildings = [
183
building for building, geom in zip(buildings, shapely_buildings)
184
if geom.distance(query_point) < 100
185
]
186
```
187
188
### Shapely Processing to Database Storage
189
190
Workflow from Shapely geometric processing back to database storage:
191
192
```python
193
from geoalchemy2.shape import from_shape
194
from shapely.geometry import Polygon
195
from shapely.ops import transform
196
import pyproj
197
198
# Create complex geometry with Shapely
199
exterior = [(0, 0), (10, 0), (10, 10), (5, 15), (0, 10)]
200
holes = [[(2, 2), (8, 2), (8, 8), (2, 8)]]
201
complex_polygon = Polygon(exterior, holes)
202
203
# Perform geometric operations
204
simplified = complex_polygon.simplify(0.5)
205
buffered = simplified.buffer(1.0)
206
207
# Transform coordinate system using pyproj
208
transformer = pyproj.Transformer.from_crs('EPSG:4326', 'EPSG:3857', always_xy=True)
209
transformed = transform(transformer.transform, buffered)
210
211
# Convert back to GeoAlchemy2 and store
212
geom_elem = from_shape(transformed, srid=3857)
213
new_feature = SpatialFeature(name='Processed Area', geom=geom_elem)
214
session.add(new_feature)
215
session.commit()
216
```
217
218
### Hybrid Spatial Operations
219
220
Combine GeoAlchemy2 database functions with Shapely processing:
221
222
```python
223
from geoalchemy2.shape import to_shape, from_shape
224
from geoalchemy2.functions import ST_Transform, ST_Buffer
225
from shapely.geometry import Point
226
227
# Start with database operations for efficiency
228
query_point = Point(lon, lat)
229
query_elem = from_shape(query_point, srid=4326)
230
231
# Use database for initial filtering (more efficient)
232
candidate_areas = session.query(ProtectedArea).filter(
233
ST_DWithin(
234
ST_Transform(ProtectedArea.geom, 4326),
235
query_elem,
236
0.01 # ~1km in degrees
237
)
238
).all()
239
240
# Convert to Shapely for complex analysis
241
shapely_areas = [to_shape(area.geom) for area in candidate_areas]
242
query_shapely = to_shape(query_elem)
243
244
# Perform complex Shapely operations
245
overlapping_areas = []
246
for area, shapely_area in zip(candidate_areas, shapely_areas):
247
if shapely_area.contains(query_shapely):
248
# Calculate overlap with 500m buffer
249
buffer_zone = query_shapely.buffer(0.005) # ~500m in degrees
250
overlap = shapely_area.intersection(buffer_zone)
251
if overlap.area > 0:
252
overlapping_areas.append((area, overlap.area))
253
254
# Sort by overlap area
255
overlapping_areas.sort(key=lambda x: x[1], reverse=True)
256
```
257
258
## Error Handling
259
260
Handle common integration errors gracefully:
261
262
```python
263
from geoalchemy2.shape import to_shape, from_shape, check_shapely
264
from shapely.errors import ShapelyError
265
266
try:
267
with check_shapely():
268
# Convert database geometry
269
shapely_geom = to_shape(db_element)
270
271
# Validate geometry
272
if not shapely_geom.is_valid:
273
# Fix invalid geometry
274
shapely_geom = shapely_geom.buffer(0)
275
276
# Perform operations
277
processed = shapely_geom.simplify(tolerance)
278
279
# Convert back
280
result_elem = from_shape(processed, srid=target_srid)
281
282
except ImportError:
283
# Shapely not available - use database functions instead
284
result_elem = db_element.ST_Simplify(tolerance)
285
286
except ShapelyError as e:
287
# Handle Shapely-specific errors
288
print(f"Geometric operation failed: {e}")
289
result_elem = db_element # Use original
290
```
291
292
## Performance Considerations
293
294
### When to Use Shapely vs Database Functions
295
296
**Use Shapely for:**
297
- Complex geometric algorithms not available in database
298
- Client-side geometry validation and processing
299
- Integration with other Python scientific libraries
300
- Batch processing of geometries in memory
301
302
**Use Database Functions for:**
303
- Large-scale spatial queries and filtering
304
- Coordinate system transformations
305
- Simple geometric operations on large datasets
306
- Operations that can leverage spatial indexes
307
308
### Optimization Strategies
309
310
```python
311
# Efficient: Filter in database first, then use Shapely
312
candidates = session.query(Feature).filter(
313
func.ST_DWithin(Feature.geom, query_point, 1000)
314
).all()
315
shapely_results = [to_shape(f.geom) for f in candidates]
316
317
# Inefficient: Load all data then filter with Shapely
318
all_features = session.query(Feature).all()
319
shapely_all = [to_shape(f.geom) for f in all_features]
320
filtered = [g for g in shapely_all if g.distance(query_shapely) < 1000]
321
```