or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

database-administration.mdindex.mdmigration-support.mdshapely-integration.mdspatial-elements.mdspatial-functions.mdspatial-types.md

shapely-integration.mddocs/

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

```