diff --git a/geospatial.py b/geospatial.py index 36c8992..003370b 100644 --- a/geospatial.py +++ b/geospatial.py @@ -1,4 +1,5 @@ -from shapely import MultiPolygon, Polygon +from shapely import MultiPolygon, Polygon, Geometry, to_wkb, from_wkb +from sqlalchemy.types import TypeDecorator, LargeBinary def convert_outage_geometry(event) -> MultiPolygon: @@ -9,3 +10,21 @@ def convert_outage_geometry(event) -> MultiPolygon: for ring in event["polygons"]["rings"]: polygon_list.append(Polygon(ring)) return MultiPolygon(polygon_list) + + +class DBGeometry(TypeDecorator): + impl = LargeBinary + cache_ok = True + + def process_bind_param(self, value, dialect): + if isinstance(value, Geometry): + value = to_wkb(value) + return value + + def process_result_value(self, value, dialect): + if value is None: + return value + else: + if not isinstance(value, Geometry): + value = from_wkb(value) + return value diff --git a/scl.py b/scl.py index 72363ca..abe74a1 100644 --- a/scl.py +++ b/scl.py @@ -5,17 +5,16 @@ from typing import Optional import mastodon import requests import shapely -import sqlalchemy.types as types import staticmap import yaml from mastodon import Mastodon from PIL import Image, ImageDraw, ImageFont -from shapely import Geometry, MultiPolygon, Point, Polygon, from_wkb, to_wkb +from shapely import Geometry from sqlalchemy import create_engine, select from sqlalchemy.exc import NoResultFound from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column -from geospatial import convert_outage_geometry +from geospatial import DBGeometry, convert_outage_geometry post_datetime_format = "%b %e %l:%M %p" @@ -118,16 +117,6 @@ def get_hashtag_string(event) -> str: return hashtag_string -def convert_outage_geometry(event) -> MultiPolygon: - assert event["polygons"]["type"] == "polygon" - assert event["polygons"]["hasZ"] is False - assert event["polygons"]["hasM"] is False - polygon_list = [] - for ring in event["polygons"]["rings"]: - polygon_list.append(Polygon(ring)) - return MultiPolygon(polygon_list) - - def do_initial_post( event, event_class, @@ -272,31 +261,21 @@ Cause: {} ) ) - post_result = mastodon_client.status_post( - status=post_text, - media_ids=map_media_post, - visibility="public", - language="en", - ) + post_result = {"id": "0123"} + # post_result = mastodon_client.status_post( + # status=post_text, + # media_ids=map_media_post, + # visibility="public", + # language="en", + # ) post_id = post_result["id"] return {"post_id": post_id, "map_media_post_id": map_media_post_id} -class GeometryWkb(types.TypeDecorator): - impl = types.LargeBinary - cache_ok = True - - def process_bind_param(self, value, dialect): - return to_wkb(value) - - def process_result_value(self, value, dialect): - return from_wkb(value) - - class Base(DeclarativeBase): type_annotation_map = { - Geometry: GeometryWkb, + Geometry: DBGeometry, } @@ -317,10 +296,11 @@ class SclOutage(Base): max_num_people: Mapped[int] = mapped_column() neighborhood: Mapped[Optional[str]] = mapped_column() city: Mapped[Optional[str]] = mapped_column() - outage_geometries: Mapped[Geometry] = mapped_column() + outage_geometries: Mapped[Optional[Geometry]] = mapped_column() + geometries_modified: Mapped[Optional[bool]] = mapped_column() def __repr__(self) -> str: - return f"SclOutage(scl_outage_id={self.scl_outage_id!r}, most_recent_post_id={self.most_recent_post_id!r}, initial_post_id={self.initial_post_id!r}, map_media_post_id={self.map_media_post_id!r}, last_updated_time={self.last_updated_time!r}, no_longer_in_response_time={self.no_longer_in_response_time!r}, start_time={self.start_time!r}, num_people={self.num_people!r}, max_num_people={self.max_num_people!r}, neighborhood={self.neighborhood!r}, city={self.city!r}, outage_geometries={self.outage_geometries!r})" + return f"SclOutage(scl_outage_id={self.scl_outage_id!r}, most_recent_post_id={self.most_recent_post_id!r}, initial_post_id={self.initial_post_id!r}, map_media_post_id={self.map_media_post_id!r}, last_updated_time={self.last_updated_time!r}, no_longer_in_response_time={self.no_longer_in_response_time!r}, start_time={self.start_time!r}, num_people={self.num_people!r}, max_num_people={self.max_num_people!r}, neighborhood={self.neighborhood!r}, city={self.city!r}, outage_geometries={self.outage_geometries!r}, geometries_modified={self.geometries_modified!r})" engine = create_engine("sqlite:///scl.db") @@ -393,9 +373,9 @@ with Session(engine) as session: existing_record.max_num_people = event["numPeople"] max_event_class = classify_event_size(existing_record.max_num_people) if existing_record.outage_geometries != outage_geometries: - print("updating geometries") + print("Geometries modified") existing_record.outage_geometries = outage_geometries - + existing_record.geometries_modified = True if updated_properties: updated_properties.sort()