From 7f651c7a0dee0f448c276a246a7af8e0038b78e0 Mon Sep 17 00:00:00 2001 From: Sym Roe Date: Tue, 3 May 2022 14:17:53 +0100 Subject: [PATCH 1/2] Making silly maps for fun and profit --- .../management/commands/create_svg_thumbs.py | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 every_election/apps/organisations/management/commands/create_svg_thumbs.py diff --git a/every_election/apps/organisations/management/commands/create_svg_thumbs.py b/every_election/apps/organisations/management/commands/create_svg_thumbs.py new file mode 100644 index 000000000..459e024c1 --- /dev/null +++ b/every_election/apps/organisations/management/commands/create_svg_thumbs.py @@ -0,0 +1,205 @@ +import re + +from django.core.management import BaseCommand +from django.db import connection + + +class SVGGenerator: + def __init__(self, object_id, object_type="divisiongeography"): + self.object_type = object_type + self.object_id = object_id + self.cursor = connection.cursor() + + self.svg_layers = [] + + self.object_svg = self.get_object_svg() + self.touching_svg = self.get_touching_svg() + + self.svg_layers.append(self.style_touching(self.touching_svg)) + self.svg_layers.append(self.style_division(self.object_svg)) + self.svg_layers += self.get_buildings() + self.svg_layers += self.get_roads() + + + def style_touching(self, touching): + return "\n".join( + [ + f"""""" + for path in touching + ] + ) + + def style_division(self, division): + return "\n".join( + [ + f"""""" + for path in division + ] + ) + + def get_viewbox(self): + def _float_if_float(input): + try: + return float(input) + except ValueError: + return None + + x_max = -1000 + x_min = 1000 + y_max = -1000 + y_min = 1000 + for svg in self.svg_layers: + cleaned = re.sub("[A-Za-z]", "", svg) + cleaned = re.sub("[\s]+", " ", cleaned).strip() + x_list = [] + y_list = [] + for i, part in enumerate(cleaned.split(" ")): + floated = _float_if_float(part) + if not floated: + continue + if i % 2: + add_to = y_list + else: + add_to = x_list + add_to.append(floated) + + x_max = max( + x_max, max([_float_if_float(x) for x in x_list if + _float_if_float(x)]) + ) + x_min = min( + x_min, min([_float_if_float(x) for x in x_list if + _float_if_float(x)]) + ) + y_max = max( + y_max, max([_float_if_float(x) for x in y_list if + _float_if_float(x)]) + ) + y_min = min( + y_min, min([_float_if_float(x) for x in y_list if + _float_if_float(x)]) + ) + + # print("-0.38996970653533936 -53.20250701904297 0.2129676789045334 0.2129669189453125") + self.width = x_max - x_min + self.height = y_max - y_min + self.viewport = f"{x_min} {y_min} {self.width} {self.height}" + # print(viewport) + return self.viewport + # import sys + # sys.exit() + # return + + def write_svg(self): + viewbox = self.get_viewbox() + paths = "\n".join(path.format(width=self.width * 0.004) for path in + self.svg_layers) + print( + f""" + + {paths} + + """ + ) + + def get_buildings(self): + sql = """ + SELECT st_assvg(geom) + FROM buildings + WHERE st_coveredby( + geom, + (SELECT dg.geography + FROM organisations_divisiongeography dg + WHERE division_id = %s) + ) + """ % (self.object_id, ) + self.cursor.execute(sql) + svg_list = [ + f"""""" + for row in self.cursor.fetchall()] + + return svg_list + + def get_roads(self): + sql = """ + SELECT st_assvg(geom) + FROM roads + WHERE st_coveredby( + geom, + (SELECT st_envelope( + ST_MinimumBoundingCircle( + st_envelope( + dg.geography + ) + ) + ) + FROM organisations_divisiongeography dg + WHERE division_id = %s) + ) + """ % (self.object_id, ) + self.cursor.execute(sql) + svg_list = [ + f"""""" + for row in self.cursor.fetchall()] + + return svg_list + + def get_touching_svg(self): + sql = """ + SELECT + st_assvg(st_intersection( + ( + SELECT + st_envelope( + ST_MinimumBoundingCircle( + st_envelope( + dg.geography + ) + ) + ) as gg + FROM organisations_divisiongeography dg + WHERE division_id = %s + ), + dg.geography + )) as gg + FROM organisations_divisiongeography dg + WHERE ST_Overlaps( + ( + SELECT + st_envelope( + ST_MinimumBoundingCircle( + st_envelope( + dg.geography + ) + ) + ) as gg + FROM organisations_divisiongeography dg + WHERE division_id = %s + ) + , + dg.geography + + ) + """ % (self.object_id, self.object_id) + + self.cursor.execute(sql) + svg_list = [row[0] for row in self.cursor.fetchall()] + return svg_list + + def get_object_svg(self): + sql = """ + SELECT + st_assvg(dg.geography) + FROM organisations_divisiongeography dg + WHERE division_id = %s + """ % self.object_id + + self.cursor.execute(sql) + svg_list = [row[0] for row in self.cursor.fetchall()] + return svg_list + + +class Command(BaseCommand): + def handle(self, *args, **options): + svg = SVGGenerator(21888) + svg.write_svg() From 62b4ea41d9e928eb5c652081c7bdb49891ba48fa Mon Sep 17 00:00:00 2001 From: Sym Roe Date: Sun, 26 Mar 2023 18:38:22 +0100 Subject: [PATCH 2/2] Make the silly maps look nicer --- .../templates/elections/election_summary.html | 3 + .../apps/elections/views/general.py | 3 + every_election/apps/og_images/__init__.py | 0 every_election/apps/og_images/admin.py | 3 + every_election/apps/og_images/apps.py | 6 + .../apps/og_images/management/__init__.py | 0 .../og_images/management/commands/__init__.py | 0 .../management/commands/create_svg_thumbs.py | 9 + .../commands/og_images_create_layers.py | 104 ++++++ .../apps/og_images/migrations/__init__.py | 0 every_election/apps/og_images/models.py | 3 + every_election/apps/og_images/svg_maker.py | 319 ++++++++++++++++++ every_election/apps/og_images/tests.py | 3 + every_election/apps/og_images/views.py | 3 + .../management/commands/create_svg_thumbs.py | 205 ----------- every_election/settings/base.py | 1 + 16 files changed, 457 insertions(+), 205 deletions(-) create mode 100644 every_election/apps/og_images/__init__.py create mode 100644 every_election/apps/og_images/admin.py create mode 100644 every_election/apps/og_images/apps.py create mode 100644 every_election/apps/og_images/management/__init__.py create mode 100644 every_election/apps/og_images/management/commands/__init__.py create mode 100644 every_election/apps/og_images/management/commands/create_svg_thumbs.py create mode 100644 every_election/apps/og_images/management/commands/og_images_create_layers.py create mode 100644 every_election/apps/og_images/migrations/__init__.py create mode 100644 every_election/apps/og_images/models.py create mode 100644 every_election/apps/og_images/svg_maker.py create mode 100644 every_election/apps/og_images/tests.py create mode 100644 every_election/apps/og_images/views.py delete mode 100644 every_election/apps/organisations/management/commands/create_svg_thumbs.py diff --git a/every_election/apps/elections/templates/elections/election_summary.html b/every_election/apps/elections/templates/elections/election_summary.html index 696bc1141..ee63d151d 100644 --- a/every_election/apps/elections/templates/elections/election_summary.html +++ b/every_election/apps/elections/templates/elections/election_summary.html @@ -18,6 +18,9 @@

{{ object.election_subtype }}

{% include "./division_map.html" %} {% endif %} + {{ svg|safe }} + +
ID
diff --git a/every_election/apps/elections/views/general.py b/every_election/apps/elections/views/general.py index f1558f6b4..6d1a50bdc 100644 --- a/every_election/apps/elections/views/general.py +++ b/every_election/apps/elections/views/general.py @@ -8,6 +8,7 @@ from elections.constants import ELECTION_TYPES from elections.forms import NoticeOfElectionForm from elections.models import ElectionType, Election, Document +from og_images.svg_maker import SVGGenerator class ElectionTypesView(ListView): @@ -117,6 +118,8 @@ def get_context_data(self, **kwargs): ) context["form"] = form context["user_can_upload_docs"] = user_is_moderator(self.request.user) + if not self.object.group_type: + context["svg"] = SVGGenerator(self.object).svg() return context def post(self, *args, **kwargs): diff --git a/every_election/apps/og_images/__init__.py b/every_election/apps/og_images/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/every_election/apps/og_images/admin.py b/every_election/apps/og_images/admin.py new file mode 100644 index 000000000..8c38f3f3d --- /dev/null +++ b/every_election/apps/og_images/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/every_election/apps/og_images/apps.py b/every_election/apps/og_images/apps.py new file mode 100644 index 000000000..3d5d2995e --- /dev/null +++ b/every_election/apps/og_images/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class OgImagesConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "og_images" diff --git a/every_election/apps/og_images/management/__init__.py b/every_election/apps/og_images/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/every_election/apps/og_images/management/commands/__init__.py b/every_election/apps/og_images/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/every_election/apps/og_images/management/commands/create_svg_thumbs.py b/every_election/apps/og_images/management/commands/create_svg_thumbs.py new file mode 100644 index 000000000..7ad8c7568 --- /dev/null +++ b/every_election/apps/og_images/management/commands/create_svg_thumbs.py @@ -0,0 +1,9 @@ +from django.core.management import BaseCommand + +from og_images.svg_maker import SVGGenerator + + +class Command(BaseCommand): + def handle(self, *args, **options): + svg = SVGGenerator(21888) + svg.write_svg() diff --git a/every_election/apps/og_images/management/commands/og_images_create_layers.py b/every_election/apps/og_images/management/commands/og_images_create_layers.py new file mode 100644 index 000000000..73e6065a8 --- /dev/null +++ b/every_election/apps/og_images/management/commands/og_images_create_layers.py @@ -0,0 +1,104 @@ +import subprocess +from pathlib import Path + +from django.conf import settings +from django.core.management.base import BaseCommand +from django.db import connection + +layers = { + "openmap-local": { + "buildings": {"file_glob": "*_Building.shp"}, + "roads": {"file_glob": "*_Road.shp"}, + "surface_water": {"file_glob": "*_SurfaceWater_Area.shp"}, + "tidal": {"file_glob": "*_TidalWater.shp"}, + "stations": {"file_glob": "*_RailwayStation.shp"}, + "railway_track": {"file_glob": "*_RailwayTrack.shp"}, + "railway_tunnel": {"file_glob": "*_RailwayTunnel.shp"}, + "roundabout": {"file_glob": "*_Roundabout.shp"}, + }, + "greenspaces": {"greenspaces": {"file_glob": "*_GreenspaceSite.shp"}}, +} + + +class Command(BaseCommand): + help = "My shiny new management command." + + def add_arguments(self, parser): + parser.add_argument( + "data_dir", + action="store", + help="Path to OS Open Map Local data directory", + type=Path, + ) + parser.add_argument( + "--product", default="openmap-local", action="store", choices=layers.keys() + ) + + def handle(self, *args, **options): + self.cursor = connection.cursor() + + self.data_dir = options["data_dir"] + + for layer, layer_data in layers[options["product"]].items(): + self.create_table(layer, layer_data) + + def table_name_from_layer_name(self, layer): + return f"og_images_layer_{layer}".replace("-", "_") + + def layer_files(self, layer, layer_data): + return list(self.data_dir.glob(f"**/{layer_data['file_glob']}")) + + def create_table(self, layer, layer_data): + """ + Drop the old table + Generate layer SQL + Create the new table + """ + self.cursor.execute( + f""" + DROP TABLE IF EXISTS {self.table_name_from_layer_name(layer)} + """, + ) + layer_files = self.layer_files(layer, layer_data) + result = subprocess.run( + [ + "shp2pgsql", + "-p", + "-I", + layer_files[0], + self.table_name_from_layer_name(layer), + ], + stdout=subprocess.PIPE, + ) + self.cursor.execute(result.stdout) + + import tempfile + + with tempfile.TemporaryDirectory() as tmpdir: + for i, filename in enumerate(layer_files): + result = subprocess.run( + [ + "shp2pgsql", + "-D", # Dump format + "-s", # Transform the SRID + "27700:4326", # From:to + "-a", # Append data, don't create table + filename, + self.table_name_from_layer_name(layer), + ], + stdout=subprocess.PIPE, + ) + temp_path = Path(tmpdir) / f"{i}.dump" + with open(temp_path, "wb") as f: + f.write(result.stdout) + database = settings.DATABASES["default"] + connection_string = f"postgresql://{database['USER']}:{database['PASSWORD']}@{database['HOST']}/{database['NAME']}" + subprocess.run( + [ + "psql", + connection_string, + "-f", # File + temp_path, + ], + stdout=subprocess.DEVNULL, + ) diff --git a/every_election/apps/og_images/migrations/__init__.py b/every_election/apps/og_images/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/every_election/apps/og_images/models.py b/every_election/apps/og_images/models.py new file mode 100644 index 000000000..71a836239 --- /dev/null +++ b/every_election/apps/og_images/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/every_election/apps/og_images/svg_maker.py b/every_election/apps/og_images/svg_maker.py new file mode 100644 index 000000000..daebc6273 --- /dev/null +++ b/every_election/apps/og_images/svg_maker.py @@ -0,0 +1,319 @@ +from django.db import connection + +from elections.models import Election + + +class SVGGenerator: + def __init__(self, ballot: Election): + if ballot.group_type: + raise ValueError( + f"Can only make a SVG for a ballot not {ballot.group_type=}" + ) + self.ballot = ballot + if self.ballot.division: + self.geography_object_type = "organisations_divisiongeography" + self.area_object = self.ballot.division + else: + self.geography_object_type = "organisations_organisationeography" + self.area_object = self.ballot.organisation + + self.divisionset_id = self.area_object.divisionset_id + + self.cursor = connection.cursor() + self.bounding_box = self.get_bounding_box_geom() + + self.svg_layers = [] + + self.object_svg = self.get_object_svg() + self.svg_layers.append(self.style_division(self.object_svg)) + + self.touching_svg = self.get_touching_svg() + self.svg_layers.insert(0, self.style_touching(self.touching_svg)) + self.svg_layers += self.get_buildings() + self.svg_layers += self.get_roads() + self.svg_layers += self.get_roundabout() + self.svg_layers += self.get_surface_water() + self.svg_layers += self.get_tidal() + self.svg_layers += self.get_greenspace() + self.svg_layers += self.get_railway() + self.svg_layers += self.get_railway_tunnel() + self.svg_layers += self.get_stations() + + def style_touching(self, touching): + return "\n".join( + [ + f"""""" + for path in touching + ] + ) + + def style_division(self, division): + return "\n".join( + [ + f"""""" + for path in division + ] + ) + + def get_viewbox(self): + self.cursor.execute( + """ + select + ST_XMin(extent) , + -ST_YMax(extent) , + ST_XMax(extent) - ST_XMin(extent) as w, + ST_YMax(extent) - ST_YMin(extent) as h + FROM ( + SELECT ST_Extent(ST_TRansform(%s::geometry, 27700) + ) AS extent) + AS bounding_box + + """, + [self.bounding_box], + ) + + extent = self.cursor.fetchone() + self.width = extent[2] + return " ".join([str(x) for x in extent]) + + def svg(self): + viewbox = self.get_viewbox() + paths = "\n".join( + path.format(width=self.width * 0.001) for path in self.svg_layers + ) + return f""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + {paths} + + """ + + def get_bounding_box_geom(self): + sql_str = f""" + SELECT st_transform( + st_envelope( + ST_MinimumBoundingCircle( + st_envelope( + ST_Transform(geog_table.geography, 27700) + ) + ) + ) + , 4326) + FROM {self.geography_object_type} geog_table + WHERE division_id = %s; + """ + + self.cursor.execute( + sql_str, + [ + self.area_object.pk, + ], + ) + return self.cursor.fetchone()[0] + + def get_touching_svg(self): + sql = f""" + SELECT + st_assvg( + ST_Transform( + st_intersection( + %s::geometry, + geom_table.geography) + , 27700) + ) AS gg + FROM {self.geography_object_type} geom_table + WHERE ST_Overlaps( + %s::geometry, + geom_table.geography + ) + """ + self.cursor.execute( + sql, + [ + self.bounding_box, + self.bounding_box, + ], + ) + svg_list = [row[0] for row in self.cursor.fetchall()] + return svg_list + + def get_object_svg(self): + sql_str = f""" + SELECT + st_assvg(st_transform(geom_table.geography, 27700)) + FROM {self.geography_object_type} geom_table + WHERE division_id = %s + """ + + self.cursor.execute(sql_str, [self.area_object.pk]) + svg_list = [row[0] for row in self.cursor.fetchall()] + return svg_list + + def get_buildings(self): + sql = f""" + SELECT st_assvg(ST_Transform(geom, 27700)) + FROM og_images_layer_buildings + WHERE + st_coveredby(geom::geometry, + %s::geometry) AND + st_coveredby( + geom, + (SELECT geog_table.geography + FROM {self.geography_object_type} geog_table + WHERE + + division_id = %s) + ) + """ + self.cursor.execute( + sql, + [ + self.bounding_box, + self.area_object.pk, + ], + ) + svg_list = [ + f"""""" + for row in self.cursor.fetchall() + ] + + return svg_list + + def get_roads(self): + sql = f""" + SELECT + drawlevel, st_assvg( + ST_Transform( +-- st_intersection( +-- %s::geometry, + geom +-- ) + , 27700) + ) AS gg + FROM og_images_layer_roads + WHERE + st_intersects( + geom::geometry, + %s::geometry + + ) + """ + self.cursor.execute( + sql, + [ + self.bounding_box, + self.bounding_box, + ], + ) + svg_list = [] + for row in self.cursor.fetchall(): + scale = int(row[0]) + 1 + scale = pow(scale, 2) + svg_list.append( + f"""""" + ) + return svg_list + + def get_surface_water(self): + attrs = """ + fill="rgba(0,206,209,0.2)" style="stroke-width:0.001;stroke:#000;stroke-opacity:0.9" + """ + return self.add_bb_layer("surface_water", attrs) + + def get_tidal(self): + attrs = """ + fill="rgba(0,206,209,0.2)" style="stroke-width:0.001;stroke:#000;stroke-opacity:0.9" + """ + return self.add_bb_layer("tidal", attrs) + + def get_greenspace(self): + attrs = """ + fill="rgba(0,100,0,0.2)" + style="stroke-width:0.001;stroke:#000;stroke-opacity:0.5" + """ + return self.add_bb_layer("greenspaces", attrs) + + def get_railway(self): + attrs = """ + fill="rgba(0,100,0,0.2)" + style="stroke-width:9;stroke:#000;stroke-opacity:0.5" + """ + return self.add_bb_layer("railway_track", attrs, covers_func="st_intersects") + + def get_railway_tunnel(self): + attrs = """ + fill="none" + style="stroke-width:11;stroke:#000;stroke-opacity:0.3" + """ + return self.add_bb_layer("railway_tunnel", attrs, covers_func="st_intersects") + + def get_stations(self): + attrs = """r="20" fill="rgba(255,0,0,0.6)" """ + layer = self.add_bb_layer("stations", attrs, tag_name="circle") + return layer + + def get_roundabout(self): + attrs = """r="10" fill="rgba(0,0,0,0.8)" """ + layer = self.add_bb_layer("roundabout", attrs, tag_name="circle") + return layer + + def add_bb_layer( + self, layer_name, attrs, tag_name="path", covers_func="st_coveredby" + ): + """ + Adds a layer that will cover the entire bounding box + + """ + + sql = f""" + SELECT + st_assvg( + ST_Transform(geom, 27700) + ) AS gg + FROM og_images_layer_{layer_name} + WHERE {covers_func}( + geom::geometry, + %s::geometry + ) + """ + self.cursor.execute( + sql, + [ + self.bounding_box, + ], + ) + + svg_list = [] + for row in self.cursor.fetchall(): + if tag_name == "path": + data = f"""d="{row[0]}" """ + else: + data = f"""{row[0]}""" + + svg_list.append(f"""<{tag_name} {data} {attrs} />""") + return svg_list diff --git a/every_election/apps/og_images/tests.py b/every_election/apps/og_images/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/every_election/apps/og_images/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/every_election/apps/og_images/views.py b/every_election/apps/og_images/views.py new file mode 100644 index 000000000..91ea44a21 --- /dev/null +++ b/every_election/apps/og_images/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/every_election/apps/organisations/management/commands/create_svg_thumbs.py b/every_election/apps/organisations/management/commands/create_svg_thumbs.py deleted file mode 100644 index 459e024c1..000000000 --- a/every_election/apps/organisations/management/commands/create_svg_thumbs.py +++ /dev/null @@ -1,205 +0,0 @@ -import re - -from django.core.management import BaseCommand -from django.db import connection - - -class SVGGenerator: - def __init__(self, object_id, object_type="divisiongeography"): - self.object_type = object_type - self.object_id = object_id - self.cursor = connection.cursor() - - self.svg_layers = [] - - self.object_svg = self.get_object_svg() - self.touching_svg = self.get_touching_svg() - - self.svg_layers.append(self.style_touching(self.touching_svg)) - self.svg_layers.append(self.style_division(self.object_svg)) - self.svg_layers += self.get_buildings() - self.svg_layers += self.get_roads() - - - def style_touching(self, touching): - return "\n".join( - [ - f"""""" - for path in touching - ] - ) - - def style_division(self, division): - return "\n".join( - [ - f"""""" - for path in division - ] - ) - - def get_viewbox(self): - def _float_if_float(input): - try: - return float(input) - except ValueError: - return None - - x_max = -1000 - x_min = 1000 - y_max = -1000 - y_min = 1000 - for svg in self.svg_layers: - cleaned = re.sub("[A-Za-z]", "", svg) - cleaned = re.sub("[\s]+", " ", cleaned).strip() - x_list = [] - y_list = [] - for i, part in enumerate(cleaned.split(" ")): - floated = _float_if_float(part) - if not floated: - continue - if i % 2: - add_to = y_list - else: - add_to = x_list - add_to.append(floated) - - x_max = max( - x_max, max([_float_if_float(x) for x in x_list if - _float_if_float(x)]) - ) - x_min = min( - x_min, min([_float_if_float(x) for x in x_list if - _float_if_float(x)]) - ) - y_max = max( - y_max, max([_float_if_float(x) for x in y_list if - _float_if_float(x)]) - ) - y_min = min( - y_min, min([_float_if_float(x) for x in y_list if - _float_if_float(x)]) - ) - - # print("-0.38996970653533936 -53.20250701904297 0.2129676789045334 0.2129669189453125") - self.width = x_max - x_min - self.height = y_max - y_min - self.viewport = f"{x_min} {y_min} {self.width} {self.height}" - # print(viewport) - return self.viewport - # import sys - # sys.exit() - # return - - def write_svg(self): - viewbox = self.get_viewbox() - paths = "\n".join(path.format(width=self.width * 0.004) for path in - self.svg_layers) - print( - f""" - - {paths} - - """ - ) - - def get_buildings(self): - sql = """ - SELECT st_assvg(geom) - FROM buildings - WHERE st_coveredby( - geom, - (SELECT dg.geography - FROM organisations_divisiongeography dg - WHERE division_id = %s) - ) - """ % (self.object_id, ) - self.cursor.execute(sql) - svg_list = [ - f"""""" - for row in self.cursor.fetchall()] - - return svg_list - - def get_roads(self): - sql = """ - SELECT st_assvg(geom) - FROM roads - WHERE st_coveredby( - geom, - (SELECT st_envelope( - ST_MinimumBoundingCircle( - st_envelope( - dg.geography - ) - ) - ) - FROM organisations_divisiongeography dg - WHERE division_id = %s) - ) - """ % (self.object_id, ) - self.cursor.execute(sql) - svg_list = [ - f"""""" - for row in self.cursor.fetchall()] - - return svg_list - - def get_touching_svg(self): - sql = """ - SELECT - st_assvg(st_intersection( - ( - SELECT - st_envelope( - ST_MinimumBoundingCircle( - st_envelope( - dg.geography - ) - ) - ) as gg - FROM organisations_divisiongeography dg - WHERE division_id = %s - ), - dg.geography - )) as gg - FROM organisations_divisiongeography dg - WHERE ST_Overlaps( - ( - SELECT - st_envelope( - ST_MinimumBoundingCircle( - st_envelope( - dg.geography - ) - ) - ) as gg - FROM organisations_divisiongeography dg - WHERE division_id = %s - ) - , - dg.geography - - ) - """ % (self.object_id, self.object_id) - - self.cursor.execute(sql) - svg_list = [row[0] for row in self.cursor.fetchall()] - return svg_list - - def get_object_svg(self): - sql = """ - SELECT - st_assvg(dg.geography) - FROM organisations_divisiongeography dg - WHERE division_id = %s - """ % self.object_id - - self.cursor.execute(sql) - svg_list = [row[0] for row in self.cursor.fetchall()] - return svg_list - - -class Command(BaseCommand): - def handle(self, *args, **options): - svg = SVGGenerator(21888) - svg.write_svg() diff --git a/every_election/settings/base.py b/every_election/settings/base.py index 2745daafd..cab5ca4c4 100644 --- a/every_election/settings/base.py +++ b/every_election/settings/base.py @@ -74,6 +74,7 @@ def str_bool_to_bool(str_bool): "django_extensions", "election_snooper", "dc_utils", + "og_images", ] INSTALLED_APPS += PROJECT_APPS