Split map image generation into a new function
This commit is contained in:
parent
3c40e695a7
commit
000acf19e3
1 changed files with 109 additions and 100 deletions
209
scl.py
209
scl.py
|
@ -1,7 +1,7 @@
|
|||
import io
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import mastodon
|
||||
import requests
|
||||
|
@ -9,6 +9,7 @@ import shapely
|
|||
import staticmap
|
||||
import yaml
|
||||
from mastodon import Mastodon
|
||||
from mastodon.return_types import MediaAttachment
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from shapely import Geometry
|
||||
from sqlalchemy import create_engine, select
|
||||
|
@ -83,7 +84,6 @@ class AttribStaticMap(staticmap.StaticMap, object):
|
|||
textSize = fnt.getbbox(self.attribution)
|
||||
textPosition = (image.size[0] - textSize[2], image.size[1] - textSize[3])
|
||||
offset = 2
|
||||
options = {"fill": (255, 255, 255, 180)}
|
||||
d.rectangle(
|
||||
[
|
||||
(textPosition[0] - (2 * offset), textPosition[1] - (2 * offset)),
|
||||
|
@ -152,6 +152,108 @@ def get_hashtag_string(event) -> str:
|
|||
return hashtag_string
|
||||
|
||||
|
||||
def generate_post_map_image(
|
||||
event, event_class, outage_geometries: shapely.MultiPolygon
|
||||
) -> Tuple[MediaAttachment, str]:
|
||||
# Fallback location from the SCL API in case one couldn't be reverse geocoded
|
||||
area_text = event["city"]
|
||||
map = AttribStaticMap(
|
||||
1024,
|
||||
1024,
|
||||
url_template=osm_url_template,
|
||||
tile_size=512,
|
||||
)
|
||||
assert event["polygons"]["type"] == "polygon"
|
||||
for ring in event["polygons"]["rings"]:
|
||||
polygon = staticmap.Polygon(
|
||||
ring,
|
||||
# Appending 7F to the fill_color makes it 50% transparent
|
||||
fill_color="{}7F".format(event_class["outage_color"]),
|
||||
outline_color=event_class["outage_color"],
|
||||
simplify=True,
|
||||
)
|
||||
map.add_polygon(polygon)
|
||||
|
||||
try:
|
||||
outage_center: shapely.Point = outage_geometries.centroid
|
||||
|
||||
assert outage_center.geom_type == "Point"
|
||||
# Check to make sure the calculated lat and lon are sane enough
|
||||
# NW Corner
|
||||
assert outage_center.y < 48 and outage_center.x > -122.6
|
||||
# SE Corner
|
||||
assert outage_center.y > 47.2 and outage_center.x < -122
|
||||
|
||||
# Zoom level 17 ensures that we won't get any building/POI names, just street names
|
||||
geocode_url = "{nominatim_url}/reverse?lat={lat}&lon={lon}&format=geocodejson&zoom=17".format(
|
||||
nominatim_url=nominatim_url,
|
||||
lat=outage_center.y,
|
||||
lon=outage_center.x,
|
||||
)
|
||||
geocode_response = requests.get(geocode_url, headers=REQUESTS_HEADERS)
|
||||
try:
|
||||
geocode = geocode_response.json()
|
||||
except requests.JSONDecodeError:
|
||||
print("JSON could not be loaded from nominatim API")
|
||||
raise
|
||||
|
||||
city = geocode["features"][0]["properties"]["geocoding"]["city"]
|
||||
street = geocode["features"][0]["properties"]["geocoding"]["name"]
|
||||
event["geoloc_city"] = city
|
||||
|
||||
if city != "Seattle":
|
||||
city_not_seattle_text = " of {}".format(city)
|
||||
else:
|
||||
city_not_seattle_text = ""
|
||||
|
||||
if (
|
||||
"locality" in geocode["features"][0]["properties"]["geocoding"]
|
||||
and event_class["size"] != "Large"
|
||||
):
|
||||
locality = geocode["features"][0]["properties"]["geocoding"]["locality"]
|
||||
if locality == "Uptown":
|
||||
locality = "Lower Queen Anne"
|
||||
|
||||
alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format(
|
||||
street,
|
||||
locality,
|
||||
city_not_seattle_text,
|
||||
)
|
||||
area_text = "the {} area{}".format(locality, city_not_seattle_text)
|
||||
event["neighborhood"] = locality
|
||||
elif "district" in geocode["features"][0]["properties"]["geocoding"]:
|
||||
district = geocode["features"][0]["properties"]["geocoding"]["district"]
|
||||
alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format(
|
||||
street,
|
||||
district,
|
||||
city_not_seattle_text,
|
||||
)
|
||||
area_text = "the {} area{}".format(
|
||||
district,
|
||||
city_not_seattle_text,
|
||||
)
|
||||
event["neighborhood"] = district
|
||||
else:
|
||||
alt_text = "A map showing the location of the outage, centered around {} in {}.".format(
|
||||
street,
|
||||
city,
|
||||
)
|
||||
area_text = city
|
||||
except Exception:
|
||||
alt_text = "A map showing the location of the outage."
|
||||
|
||||
map_image = map.render()
|
||||
|
||||
with io.BytesIO() as map_image_file:
|
||||
map_image.save(map_image_file, format="WebP", method=6)
|
||||
map_media_post = mastodon_client.media_post(
|
||||
map_image_file,
|
||||
mime_type="image/webp",
|
||||
description=alt_text,
|
||||
)
|
||||
return (map_media_post, area_text)
|
||||
|
||||
|
||||
def do_initial_post(
|
||||
event,
|
||||
event_class,
|
||||
|
@ -160,112 +262,19 @@ def do_initial_post(
|
|||
outage_geometries: shapely.MultiPolygon,
|
||||
) -> dict[str, str | None]:
|
||||
post_id = None
|
||||
map_media_post_id = None
|
||||
# Fallback location from the SCL API in case one couldn't be reverse geocoded
|
||||
area_text = event["city"]
|
||||
map_media_post = None
|
||||
area_text = str()
|
||||
|
||||
try:
|
||||
map = AttribStaticMap(
|
||||
1024,
|
||||
1024,
|
||||
url_template=osm_url_template,
|
||||
tile_size=512,
|
||||
map_media_post, area_text = generate_post_map_image(
|
||||
event, event_class, outage_geometries
|
||||
)
|
||||
assert event["polygons"]["type"] == "polygon"
|
||||
for ring in event["polygons"]["rings"]:
|
||||
polygon = staticmap.Polygon(
|
||||
ring,
|
||||
# Appending 7F to the fill_color makes it 50% transparent
|
||||
fill_color="{}7F".format(event_class["outage_color"]),
|
||||
outline_color=event_class["outage_color"],
|
||||
simplify=True,
|
||||
)
|
||||
map.add_polygon(polygon)
|
||||
|
||||
try:
|
||||
outage_center: shapely.Point = outage_geometries.centroid
|
||||
|
||||
assert outage_center.geom_type == "Point"
|
||||
# Check to make sure the calculated lat and lon are sane enough
|
||||
# NW Corner
|
||||
assert outage_center.y < 48 and outage_center.x > -122.6
|
||||
# SE Corner
|
||||
assert outage_center.y > 47.2 and outage_center.x < -122
|
||||
|
||||
# Zoom level 17 ensures that we won't get any building/POI names, just street names
|
||||
geocode_url = "{nominatim_url}/reverse?lat={lat}&lon={lon}&format=geocodejson&zoom=17".format(
|
||||
nominatim_url=nominatim_url,
|
||||
lat=outage_center.y,
|
||||
lon=outage_center.x,
|
||||
)
|
||||
geocode_response = requests.get(geocode_url, headers=REQUESTS_HEADERS)
|
||||
try:
|
||||
geocode = geocode_response.json()
|
||||
except requests.JSONDecodeError:
|
||||
print("JSON could not be loaded from nominatim API")
|
||||
raise
|
||||
|
||||
city = geocode["features"][0]["properties"]["geocoding"]["city"]
|
||||
street = geocode["features"][0]["properties"]["geocoding"]["name"]
|
||||
event["geoloc_city"] = city
|
||||
|
||||
if city != "Seattle":
|
||||
city_not_seattle_text = " of {}".format(city)
|
||||
else:
|
||||
city_not_seattle_text = ""
|
||||
|
||||
if (
|
||||
"locality" in geocode["features"][0]["properties"]["geocoding"]
|
||||
and event_class["size"] != "Large"
|
||||
):
|
||||
locality = geocode["features"][0]["properties"]["geocoding"]["locality"]
|
||||
if locality == "Uptown":
|
||||
locality = "Lower Queen Anne"
|
||||
|
||||
alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format(
|
||||
street,
|
||||
locality,
|
||||
city_not_seattle_text,
|
||||
)
|
||||
area_text = "the {} area{}".format(locality, city_not_seattle_text)
|
||||
event["neighborhood"] = locality
|
||||
elif "district" in geocode["features"][0]["properties"]["geocoding"]:
|
||||
district = geocode["features"][0]["properties"]["geocoding"]["district"]
|
||||
alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format(
|
||||
street,
|
||||
district,
|
||||
city_not_seattle_text,
|
||||
)
|
||||
area_text = "the {} area{}".format(
|
||||
district,
|
||||
city_not_seattle_text,
|
||||
)
|
||||
event["neighborhood"] = district
|
||||
else:
|
||||
alt_text = "A map showing the location of the outage, centered around {} in {}.".format(
|
||||
street,
|
||||
city,
|
||||
)
|
||||
area_text = city
|
||||
except Exception:
|
||||
alt_text = "A map showing the location of the outage."
|
||||
|
||||
map_image = map.render()
|
||||
|
||||
with io.BytesIO() as map_image_file:
|
||||
map_image.save(map_image_file, format="WebP", method=6)
|
||||
map_media_post = mastodon_client.media_post(
|
||||
map_image_file.getvalue(),
|
||||
mime_type="image/webp",
|
||||
description=alt_text,
|
||||
)
|
||||
map_media_post_id = map_media_post["id"]
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(
|
||||
"Ran into an issue with generating/uploading the map. Will post without it."
|
||||
)
|
||||
map_media_post = None
|
||||
hashtag_string = get_hashtag_string(event)
|
||||
|
||||
est_restoration_post_text = str()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue