CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-geoalchemy2

Using SQLAlchemy with Spatial Databases

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

shapely-integration.mddocs/

Shapely Integration

Integration utilities for converting between GeoAlchemy2 spatial elements and Shapely geometries, enabling advanced geometric operations and analysis with the popular Python geometry library.

Installation

Shapely integration requires the optional dependency:

pip install GeoAlchemy2[shapely]
# or
pip install GeoAlchemy2 shapely

Capabilities

Element to Shapely Conversion

Convert GeoAlchemy2 spatial elements to Shapely geometry objects for advanced geometric operations.

def to_shape(element):
    """
    Convert a GeoAlchemy2 spatial element to a Shapely geometry.
    
    Parameters:
    - element: Union[WKBElement, WKTElement], spatial element to convert
    
    Returns:
    Shapely geometry object (Point, LineString, Polygon, etc.)
    
    Raises:
    ImportError: if Shapely is not installed
    """

Usage examples:

from geoalchemy2.shape import to_shape
from geoalchemy2 import WKTElement

# Convert WKT element to Shapely
wkt_elem = WKTElement('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', srid=4326)
shapely_geom = to_shape(wkt_elem)

# Query database and convert result
lake = session.query(Lake).first()
shapely_lake = to_shape(lake.geom)  # lake.geom is WKBElement

# Use Shapely operations
print(f"Area: {shapely_lake.area}")
print(f"Centroid: {shapely_lake.centroid}")
print(f"Bounds: {shapely_lake.bounds}")

# Advanced Shapely operations
simplified = shapely_lake.simplify(0.1)
buffered = shapely_lake.buffer(100)
convex_hull = shapely_lake.convex_hull

# Check geometric properties
print(f"Is valid: {shapely_lake.is_valid}")
print(f"Is simple: {shapely_lake.is_simple}")
print(f"Geometry type: {shapely_lake.geom_type}")

Shapely to Element Conversion

Convert Shapely geometry objects back to GeoAlchemy2 spatial elements for database storage.

def from_shape(shape, srid: int = -1, extended: bool = None):
    """
    Convert a Shapely geometry to a GeoAlchemy2 spatial element.
    
    Parameters:
    - shape: Shapely geometry object
    - srid: int, spatial reference system identifier (default: -1)
    - extended: bool, use extended WKB format (default: None)
    
    Returns:
    WKBElement representing the Shapely geometry
    
    Raises:
    ImportError: if Shapely is not installed
    """

Usage examples:

from geoalchemy2.shape import from_shape
from shapely.geometry import Point, Polygon, LineString
from shapely import wkt

# Create Shapely geometries
point = Point(1, 2)
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
line = LineString([(0, 0), (1, 1), (2, 0)])

# Convert to GeoAlchemy2 elements
point_elem = from_shape(point, srid=4326)
polygon_elem = from_shape(polygon, srid=4326)
line_elem = from_shape(line, srid=4326)

# Store in database
new_lake = Lake(name='Shapely Lake', geom=polygon_elem)
session.add(new_lake)
session.commit()

# Load from WKT and convert
shapely_from_wkt = wkt.loads('MULTIPOINT((0 0), (1 1), (2 2))')
multipoint_elem = from_shape(shapely_from_wkt, srid=4326)

Dependency Management

Handle optional Shapely dependency gracefully with built-in availability checking.

HAS_SHAPELY: bool
"""Boolean indicating whether Shapely is available."""

def check_shapely():
    """
    Context manager to verify Shapely availability.
    
    Raises:
    ImportError: if Shapely is not installed with helpful message
    """

Usage examples:

from geoalchemy2.shape import HAS_SHAPELY, check_shapely

# Check availability before using
if HAS_SHAPELY:
    from geoalchemy2.shape import to_shape, from_shape
    # Use Shapely functions
else:
    print("Shapely not available")

# Use context manager for safe operations
try:
    with check_shapely():
        shapely_geom = to_shape(spatial_element)
        # Perform Shapely operations
except ImportError as e:
    print(f"Shapely required: {e}")

Integration Workflows

Database Query to Shapely Analysis

Complete workflow from database query to Shapely-based analysis:

from geoalchemy2.shape import to_shape
from shapely.ops import unary_union, cascaded_union
from shapely.geometry import Point

# Query spatial data from database
buildings = session.query(Building).filter(
    Building.district_id == district_id
).all()

# Convert all geometries to Shapely
shapely_buildings = [to_shape(b.geom) for b in buildings]

# Perform complex Shapely operations
total_footprint = unary_union(shapely_buildings)
building_centroids = [geom.centroid for geom in shapely_buildings]

# Analysis with Shapely
total_area = total_footprint.area
avg_building_area = sum(g.area for g in shapely_buildings) / len(shapely_buildings)

# Find buildings near a point
query_point = Point(x, y)
nearby_buildings = [
    building for building, geom in zip(buildings, shapely_buildings)
    if geom.distance(query_point) < 100
]

Shapely Processing to Database Storage

Workflow from Shapely geometric processing back to database storage:

from geoalchemy2.shape import from_shape
from shapely.geometry import Polygon
from shapely.ops import transform
import pyproj

# Create complex geometry with Shapely
exterior = [(0, 0), (10, 0), (10, 10), (5, 15), (0, 10)]
holes = [[(2, 2), (8, 2), (8, 8), (2, 8)]]
complex_polygon = Polygon(exterior, holes)

# Perform geometric operations
simplified = complex_polygon.simplify(0.5)
buffered = simplified.buffer(1.0)

# Transform coordinate system using pyproj
transformer = pyproj.Transformer.from_crs('EPSG:4326', 'EPSG:3857', always_xy=True)
transformed = transform(transformer.transform, buffered)

# Convert back to GeoAlchemy2 and store
geom_elem = from_shape(transformed, srid=3857)
new_feature = SpatialFeature(name='Processed Area', geom=geom_elem)
session.add(new_feature)
session.commit()

Hybrid Spatial Operations

Combine GeoAlchemy2 database functions with Shapely processing:

from geoalchemy2.shape import to_shape, from_shape
from geoalchemy2.functions import ST_Transform, ST_Buffer
from shapely.geometry import Point

# Start with database operations for efficiency
query_point = Point(lon, lat)
query_elem = from_shape(query_point, srid=4326)

# Use database for initial filtering (more efficient)
candidate_areas = session.query(ProtectedArea).filter(
    ST_DWithin(
        ST_Transform(ProtectedArea.geom, 4326),
        query_elem,
        0.01  # ~1km in degrees
    )
).all()

# Convert to Shapely for complex analysis
shapely_areas = [to_shape(area.geom) for area in candidate_areas]
query_shapely = to_shape(query_elem)

# Perform complex Shapely operations
overlapping_areas = []
for area, shapely_area in zip(candidate_areas, shapely_areas):
    if shapely_area.contains(query_shapely):
        # Calculate overlap with 500m buffer
        buffer_zone = query_shapely.buffer(0.005)  # ~500m in degrees
        overlap = shapely_area.intersection(buffer_zone)
        if overlap.area > 0:
            overlapping_areas.append((area, overlap.area))

# Sort by overlap area
overlapping_areas.sort(key=lambda x: x[1], reverse=True)

Error Handling

Handle common integration errors gracefully:

from geoalchemy2.shape import to_shape, from_shape, check_shapely
from shapely.errors import ShapelyError

try:
    with check_shapely():
        # Convert database geometry
        shapely_geom = to_shape(db_element)
        
        # Validate geometry
        if not shapely_geom.is_valid:
            # Fix invalid geometry
            shapely_geom = shapely_geom.buffer(0)
        
        # Perform operations
        processed = shapely_geom.simplify(tolerance)
        
        # Convert back
        result_elem = from_shape(processed, srid=target_srid)
        
except ImportError:
    # Shapely not available - use database functions instead
    result_elem = db_element.ST_Simplify(tolerance)
    
except ShapelyError as e:
    # Handle Shapely-specific errors
    print(f"Geometric operation failed: {e}")
    result_elem = db_element  # Use original

Performance Considerations

When to Use Shapely vs Database Functions

Use Shapely for:

  • Complex geometric algorithms not available in database
  • Client-side geometry validation and processing
  • Integration with other Python scientific libraries
  • Batch processing of geometries in memory

Use Database Functions for:

  • Large-scale spatial queries and filtering
  • Coordinate system transformations
  • Simple geometric operations on large datasets
  • Operations that can leverage spatial indexes

Optimization Strategies

# Efficient: Filter in database first, then use Shapely
candidates = session.query(Feature).filter(
    func.ST_DWithin(Feature.geom, query_point, 1000)
).all()
shapely_results = [to_shape(f.geom) for f in candidates]

# Inefficient: Load all data then filter with Shapely
all_features = session.query(Feature).all()
shapely_all = [to_shape(f.geom) for f in all_features]
filtered = [g for g in shapely_all if g.distance(query_shapely) < 1000]

Install with Tessl CLI

npx tessl i tessl/pypi-geoalchemy2

docs

database-administration.md

index.md

migration-support.md

shapely-integration.md

spatial-elements.md

spatial-functions.md

spatial-types.md

tile.json