GeoAlchemy2 Documentation

repository·main·Indexed 20 days ago

https://github.com/geoalchemy/geoalchemy2

A spatial extension for SQLAlchemy that enables the use of spatial databases. It provides support for Geometry, Geography, and Raster types, including integration with PostGIS and SpatiaLite. The library includes tools for defining spatial tables, performing spatial relationship queries using concise patterns, and configuring Alembic migrations via geoalchemy2.alembic_helpers to handle spatial operations and internal tables.

Tokens
12.1K
Snippets
42
Records
50
Agent score
72%

What's inside GeoAlchemy2

  1. Overview of GeoAlchemy 2

    main
    GeoAlchemy 2 is a Python toolkit designed for working with spatial databases. It is built on top of SQLAlchemy, extending its capabilities to support geographic and geometry data types and spatial functions within the SQLAlchemy ecosystem.
  2. Supported Spatial Database Dialects

    main

    GeoAlchemy 2 provides extensions for SQLAlchemy to work with various spatial databases. While it focuses on PostGIS (supporting PostGIS 2 and 3), it also supports the following dialects:

    • SpatiaLite >= 4.3.0 (Note: Alembic helpers require SpatiaLite >= 5)
    • MySQL >= 8
    • MariaDB >= 5.3.3 (experimental)
    • GeoPackage
    • MSSQL (experimental, tested with SQL Server 2022 CU24)

    Note: Using non-PostGIS dialects may require specific application-side configuration and may not be optimal for performance.

  3. Perform spatial relationship queries

    main

    GeoAlchemy2 allows you to perform spatial relationship queries (like ST_Contains, ST_Intersects, etc.) using two patterns:

    1. Standard SQLAlchemy func pattern: func.ST_Contains(column, geometry).
    2. GeoAlchemy2 concise pattern: column.ST_Contains(geometry). This pattern applies the function directly to the column object and is more readable.

    Note that GeoAlchemy2 operator functions (like .intersects()) do not include the ST_ prefix, whereas the standard PostGIS functions do.

    from sqlalchemy import select, func
    
    # Using func.ST_Contains
    s = select(lake_table).where(
        func.ST_Contains(lake_table.c.geom, 'POINT(4 1)')
    )
    
    # Using GeoAlchemy2 concise syntax
    s = select(lake_table).where(
        lake_table.c.geom.ST_Contains('POINT(4 1)')
    )
    
    # Using an operator function (no ST_ prefix)
    s = select(lake_table).where(
        lake_table.c.geom.intersects('LINESTRING(2 1,4 1)')
    )
  4. Use WKTElement and WKBElement for spatial data

    main

    GeoAlchemy 2 replaces several legacy spatial element classes with two primary classes: WKTElement and WKBElement.

    WKTElement

    Use geoalchemy2.elements.WKTElement in expressions when you need to specify a geometry using a Well-Known Text (WKT) string with a specific SRID. If no SRID is required, you can pass a WKT string directly to the spatial function.

    Example with SRID:

    from geoalchemy2.elements import WKTElement
    Lake.geom.ST_Touches(WKTElement('POINT(1 1)', srid=4326))

    Example without SRID:

    Lake.geom.ST_Touches('POINT(1 1)')

    WKBElement

    geoalchemy2.elements.WKBElement is the type used for geometry values read from the database. When you load an object from the database, its geometry attributes will be instances of WKBElement. This replaces the legacy PersistentSpatialElement and PGPersistentSpatialElement classes.

    from geoalchemy2.elements import WKTElement
    
    # Using WKTElement with a specific SRID
    Lake.geom.ST_Touches(WKTElement('POINT(1 1)', srid=4326))
    
    # Using a raw WKT string when SRID is not needed
    Lake.geom.ST_Touches('POINT(1 1)')
  5. Coordinate order for MSSQL (SQL Server) spatial types

    main

    GeoAlchemy 2 uses the standard spatial coordinate order of X Y (for geographic coordinates, this is longitude latitude) for most APIs.

    However, SQL Server's native geography::Point constructor expects latitude before longitude (geography::Point(latitude, longitude, srid)).

    GeoAlchemy handles this discrepancy automatically in specific scenarios (like computed columns), but when writing manual SQL or using certain functions, you must be aware of this difference.

  6. Core Concepts and Features of GeoAlchemy 2

    main

    GeoAlchemy 2 is designed to be a simpler, more maintainable extension for SQLAlchemy. Key features include:

    • Type Support: Supports PostGIS geometry, geography, and raster types.
    • Standardized Function Calls: Spatial functions are called using SQLAlchemy's standard func syntax (e.g., func.ST_GeomFromText(...)) rather than a separate namespace.
    • ORM and Core Compatibility: Works seamlessly with both SQLAlchemy's Object Relational Mapping (ORM) and the SQLAlchemy Core (SQL Expression Language).
    • Reflection: Supports reflection of geometry and geography columns.
    • Shapely Integration: Provides to_shape and from_shape functions for improved integration with the Shapely library.
  7. Set spatial relationships in SQLAlchemy models

    main

    You can define spatial relationships (e.g., finding all 'Treasures' contained within a 'Lake') using SQLAlchemy's relationship() function.

    Because spatial relationships rely on SQL functions (like ST_Contains) rather than simple equality, you must use the .as_comparison(1, 2) method on the function in the primaryjoin argument. This is required for SQLAlchemy to correctly handle the function in a join condition.

    Key parameters for spatial relationships:

    • primaryjoin: The SQL condition for the join, using func.ST_... and .as_comparison().
    • viewonly=True: Recommended for spatial relationships to ensure they are used for loading data and not for attempting to persist changes via the relationship.
    • backref: Provides the inverse relationship on the related model.
    from sqlalchemy.orm import relationship, backref
    from sqlalchemy import func
    
    class Lake(Base):
        __tablename__ = 'lake'
        id = Column(Integer, primary_key=True)
        geom = Column(Geometry('POLYGON'))
        
        treasures = relationship(
            'Treasure',
            primaryjoin='func.ST_Contains(foreign(Lake.geom), Treasure.geom).as_comparison(1, 2)',
            backref=backref('lake', uselist=False),
            viewonly=True,
            uselist=True,
        )
  8. Install system dependencies for Linux (Ubuntu 22.04)

    main

    To run tests directly on a Linux host, you must install the necessary system-level packages for PostgreSQL/PostGIS, Python development, MSSQL (via ODBC), SpatiaLite, and MySQL.

    # PostgreSQL and PostGIS
    $ sudo apt-get install postgresql postgresql-14-postgis-3 postgresql-14-postgis-3-scripts
    
    # Python and PostgreSQL development
    $ sudo apt-get install python3-dev libpq-dev libgeos-dev
    
    # SpatiaLite
    $ sudo apt-get install libsqlite3-mod-spatialite
    
    # MySQL
    $ sudo apt-get install mysql-client mysql-server default-libmysqlclient-dev
    
    # Python dependencies
    $ pip install -r requirements.txt -r requirements-mypy.txt
    $ pip install psycopg2-binary pyodbc "Shapely>=1.3.0"
  9. Configure Alembic helpers for GeoAlchemy 2

    main

    To ensure Alembic's --autogenerate feature works correctly with GeoAlchemy 2 (handling imports, spatial operations, and ignoring internal spatial tables), you should configure the env.py file with geoalchemy2.alembic_helpers.

    Pass the following three functions to context.configure() in both run_migrations_offline() and run_migrations_online():

    1. include_object=alembic_helpers.include_object: Ignores internal tables managed by spatial extensions.
    2. process_revision_directives=alembic_helpers.writer: Adds specific spatial operations (like create_geospatial_table, add_geospatial_column, etc.) to the migration scripts.
    3. render_item=alembic_helpers.render_item: Automatically adds geoalchemy2 imports to the generated migration scripts.
    from geoalchemy2 import alembic_helpers
    
    # In run_migrations_offline and run_migrations_online:
    def run_migrations_online():
        # ...
        context.configure(
            # ...
            include_object=alembic_helpers.include_object,
            process_revision_directives=alembic_helpers.writer,
            render_item=alembic_helpers.render_item,
        )
  10. Configure MSSQL computed columns for Geography

    main

    When defining MSSQL computed columns for Geography types, you can use the standard ST_POINT(longitude, latitude) syntax. GeoAlchemy will automatically rewrite this to the correct SQL Server constructor syntax geography::Point(latitude, longitude, srid) during DDL generation.

    from sqlalchemy import Column, Computed, Float, Integer, MetaData, Table
    from geoalchemy2 import Geography
    
    metadata = MetaData()
    
    places = Table(
        "places",
        metadata,
        Column("id", Integer, primary_key=True),
        Column("longitude", Float, nullable=False),
        Column("latitude", Float, nullable=False),
        Column(
            "geog",
            Geography(geometry_type="POINT", srid=4326),
            Computed("ST_POINT(longitude, latitude)", persisted=True),
        ),
    )
  11. Add custom type imports to Alembic migrations

    main

    If you use custom SQLAlchemy types (e.g., TheCustomType defined in my_package.custom_types), Alembic's autogenerate won't automatically import them. You can extend alembic_helpers.render_item to handle this.

    Create a custom render_item function that calls the default alembic_helpers.render_item first, then checks for your custom type to inject the necessary import statement into autogen_context.imports.

    from geoalchemy2 import alembic_helpers
    from my_package.custom_types import TheCustomType
    
    def render_item(obj_type, obj, autogen_context):
        # Try default spatial rendering first
        spatial_type = alembic_helpers.render_item(obj_type, obj, autogen_context)
        if spatial_type:
            return spatial_type
    
        # Handle custom type imports
        if obj_type == 'type' and isinstance(obj, TheCustomType):
            import_name = obj.__class__.__name__
            autogen_context.imports.add(f"from my_package.custom_types import {import_name}")
            return "%r" % obj
    
        return False
    
    # Use this in context.configure(render_item=render_item, ...)
  12. Use spatial functions with Raster types

    main

    Certain spatial functions (such as ST_Transform(), ST_Union(), and ST_SnapToGrid()) are compatible with both Geometry and Raster types.

    Because GeoAlchemy2 defines these functions on the Geometry type to avoid ambiguity, you must explicitly specify the type when using them on a Raster column. Pass the type_=Raster argument to the function call to enforce the correct type.

    from geoalchemy2.types import Raster
    
    # Use ST_Transform on a Raster column by specifying type_
    query = session.query(Lake.raster.ST_Transform(2154, type_=Raster))