Nick Doiron / Oct 14 2019
Remix of Python by Nextjournal
PyCon India - GeoAlchemy2
import sys; sys.version.split()[0]
'3.6.8'
- Let's install SQLAlchemy and GeoAlchemy2 - note that '2' is part of the name, and not a version number:
pip install sqlalchemy geoalchemy2 psycopg2-binary
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from geoalchemy2 import Geography
Let's connect to this PostgreSQL database, where I have already installed PostGIS, and created a read-only user py:zim.
from sqlalchemy import create_engine engine = create_engine('postgresql://py:zim@gis.georeactor.com/pyindia', echo=True)
- With SQLAlchemy, we can use a class to represent each column of the table.
GeoAlchemy2 gives us the Geography type and additional functions.
Base = declarative_base() class State(Base): __tablename__ = 'states' ogc_fid = Column(Integer, primary_key=True) st_nm = Column(String) wkb_geometry = Column(Geography)
from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine) session = Session()
SELECT-ing the geo column
Here we call State.wkb_geometry, and it returns a spatial object
mystate = session.query(State.wkb_geometry).first() print(mystate)
- It's better to use a new function such as ST_AsText() or ST_AsGeoJSON() to return a usable object:
mystate = session.query(State.wkb_geometry.ST_AsText()).first() print(mystate)
Spatial + COUNT
class Tower(Base): __tablename__ = 'cell_towers' ID = Column(Integer, primary_key=True) point = Column(Geography) class Bengaluru(Base): __tablename__ = 'bengaluru' ogc_fid = Column(Integer, primary_key=True) wkb_geometry = Column(Geography)
session.query(Tower).count()
384086
session.query(Tower).filter(Tower.point.ST_Intersects(Bengaluru.wkb_geometry)).count()
17470
Spatial + JOIN
class Ward(Base): __tablename__ = 'chennai_wards' ogc_fid = Column(Integer, primary_key=True) wkb_geometry = Column(Geography) ward_no = Column(Integer) zone_no = Column(Integer)
class Parliament(Base): __tablename__ = 'parliament' ogc_fid = Column(Integer, primary_key=True) wkb_geometry = Column(Geography) pc_name = Column(String) pc_code = Column(Integer)
Here we do not do a JOIN outright, but SELECT from both tables and find all matches:
combined = session.query(Ward, Parliament).filter(Ward.wkb_geometry.ST_Intersects(Parliament.wkb_geometry)).order_by(Ward.ward_no).all() for w, p in combined: print ("Ward: {} in Parliament: {}".format(w.ward_no, p.pc_name))