import math import requests from PIL import Image, ImageDraw, ImageFont from staticmap import Polygon, StaticMap class AttribStaticMap(StaticMap, object): def __init__(self, *args, **kwargs): self.attribution = "© Stadia Maps © OpenMapTiles © OpenStreetMap" super(AttribStaticMap, self).__init__(*args, **kwargs) def _draw_features(self, image): super(AttribStaticMap, self)._draw_features(image) txt = Image.new("RGBA", image.size, (255, 255, 255, 0)) # get a font # fnt = ImageFont.truetype('FreeMono.ttf', 12) fnt = ImageFont.load_default() # get a drawing context d = ImageDraw.Draw(txt) 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)), ( textSize[2] + textPosition[0] + (2 * offset), textSize[3] + textPosition[1] + (2 * offset), ), ], **options ) # draw text, full opacity d.text( (textPosition[0] - offset, textPosition[1] - offset), self.attribution, font=fnt, fill="black", ) image.paste(txt, (0, 0), txt) scl_events_url = "https://utilisocial.io/datacapable/v2/p/scl/map/events" scl_events_response = requests.get(scl_events_url) try: scl_events = scl_events_response.json() except requests.JSONDecodeError: print("JSON could not be loaded from SCL API") raise with open("stadiamaps_api_key.secret", "r+") as stadiamaps_api_key_file: # Reading from a file stadiamaps_api_key = stadiamaps_api_key_file.read() map = AttribStaticMap( 512, 512, url_template="https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}.png?api_key=" + stadiamaps_api_key, ) for ring in scl_events[0]["polygons"]["rings"]: polygon = Polygon(ring, "#F973167F", "#F97316", simplify=True) map.add_polygon(polygon) image = map.render() image.save("map.png", "PNG") try: def num2deg(xtile, ytile, zoom): n = 1 << zoom lon_deg = xtile / n * 360.0 - 180.0 lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) lat_deg = math.degrees(lat_rad) return lat_deg, lon_deg center_lat_lon = num2deg(map.x_center, map.y_center, map.zoom) geocode_url = "http://gruezi-skyros.srv.gruezi.net:6664/reverse?lat={lat}&lon={lon}&format=geocodejson".format( lat=center_lat_lon[0], lon=center_lat_lon[1] ) geocode_headers = {"User-Agent": "seattlecitylight-mastodon-bot"} geocode_response = requests.get(geocode_url, headers=geocode_headers) try: geocode = geocode_response.json() except requests.JSONDecodeError: print("JSON could not be loaded from nominatim API") raise if geocode["features"][0]["properties"]["geocoding"]["city"] != "Seattle": city_not_seattle_text = " of {}".format( geocode["features"][0]["properties"]["geocoding"]["city"] ) else: city_not_seattle_text = "" if "locality" in geocode["features"][0]["properties"]["geocoding"]: locality = geocode["features"][0]["properties"]["geocoding"] if locality == "Uptown": locality = "Lower Queen Anne" alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format( geocode["features"][0]["properties"]["geocoding"]["name"], locality, city_not_seattle_text, ) elif "district" in geocode["features"][0]["properties"]["geocoding"]: alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format( geocode["features"][0]["properties"]["geocoding"]["name"], geocode["features"][0]["properties"]["geocoding"]["district"], city_not_seattle_text, ) else: alt_text = "A map showing the location of the outage, centered around {} in {}.".format( geocode["features"][0]["properties"]["geocoding"]["name"], geocode["features"][0]["properties"]["geocoding"]["city"], ) except Exception: alt_text = "A map showing the location of the outage." print(alt_text)