From aba715dd105f075fb044d3c2114d6a0e9babcc3b Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Wed, 11 Mar 2026 17:10:53 -0500 Subject: [PATCH 1/4] pass other map ids to georeference interface --- .../src/components/Georeferencer.svelte | 1 + ohmg/georeference/views.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/ohmg/frontend/svelte_components/src/components/Georeferencer.svelte b/ohmg/frontend/svelte_components/src/components/Georeferencer.svelte index 59f904b6..bd362238 100644 --- a/ohmg/frontend/svelte_components/src/components/Georeferencer.svelte +++ b/ohmg/frontend/svelte_components/src/components/Georeferencer.svelte @@ -58,6 +58,7 @@ export let MAP; export let MAIN_LAYERSET; export let KEYMAP_LAYERSET; + export let OTHER_MAPS; export let PARCEL_LAYER; let previewMode = 'n/a'; diff --git a/ohmg/georeference/views.py b/ohmg/georeference/views.py index 045577ba..49f88c2e 100644 --- a/ohmg/georeference/views.py +++ b/ohmg/georeference/views.py @@ -22,6 +22,7 @@ ) from ohmg.core.models import ( Document, + Map, Region, ) from ohmg.core.storages import get_file_url @@ -210,12 +211,23 @@ def get(self, request, docid): parcel_layer = region.map.get_locale().get_parcels() parcel_json = parcel_layer.serialize() if parcel_layer else None + place = region.map.locales.first() + other_maps = [] + if place: + other_maps = list( + Map.objects.filter(locales__id__exact=place.id, hidden=False) + .exclude(pk=region.map.pk) + .values_list("identifier", "title") + ) + other_maps = [{"identifier": i[0], "title": i[1]} for i in other_maps] + georeference_params = { "CONTEXT": generate_ohmg_context(request), "REGION": region_json, "MAP": map_json, "MAIN_LAYERSET": main_layerset, "KEYMAP_LAYERSET": keymap_layerset, + "OTHER_MAPS": other_maps, "PARCEL_LAYER": parcel_json, } From 4360d4bbd25354007ee4eb650abfa513eeb1060f Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Wed, 11 Mar 2026 17:17:24 -0500 Subject: [PATCH 2/4] refactor get layers from layerset --- ohmg/api/schemas.py | 6 ++++-- ohmg/core/models.py | 16 +++++++++------- ohmg/core/signals.py | 2 +- ohmg/extensions/views.py | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ohmg/api/schemas.py b/ohmg/api/schemas.py index 428ba8a7..84d7083e 100644 --- a/ohmg/api/schemas.py +++ b/ohmg/api/schemas.py @@ -497,7 +497,7 @@ def resolve_id(obj): @staticmethod def resolve_layers(obj): - return natsorted(obj.layer_set.all(), key=lambda k: k.title) + return natsorted(obj.get_layers(), key=lambda k: k.title) @staticmethod def resolve_name(obj): @@ -651,7 +651,9 @@ def resolve_loaded_by(obj): @staticmethod def resolve_locks(obj): locks = [ - i for i in SessionLock.objects.all().prefetch_related() if i.target and i.target.map.pk == obj.pk + i + for i in SessionLock.objects.all().prefetch_related() + if i.target and i.target.map.pk == obj.pk ] return locks diff --git a/ohmg/core/models.py b/ohmg/core/models.py index be2e2325..8e6dabd5 100644 --- a/ohmg/core/models.py +++ b/ohmg/core/models.py @@ -4,7 +4,7 @@ import urllib.parse from datetime import datetime from pathlib import Path -from typing import Union +from typing import Iterable, Union from django.conf import settings from django.contrib.auth import get_user_model @@ -284,7 +284,7 @@ def stats(self): main_layerset = self.get_layerset("main-content") if main_layerset: - main_lyrs_ct = main_layerset.layer_set.count() + main_lyrs_ct = main_layerset.get_layers().count() else: main_lyrs_ct = 0 mm_ct, mm_todo, mm_percent = 0, 0, 0 @@ -441,7 +441,7 @@ def update_item_lookup(self): multimask_ct, multimask_rank = 0, 0 main_layerset = self.get_layerset("main-content") if main_layerset: - main_lyrs_ct = main_layerset.layer_set.count() + main_lyrs_ct = main_layerset.get_layers().count() else: main_lyrs_ct = 0 @@ -922,7 +922,7 @@ def set_layerset(self, layerset): logger.info( f"Layer {self.pk} removed from existing multimask in LayerSet {existing_obj.pk}" ) - if existing_obj.layer_set.all().count() == 1: + if existing_obj.get_layers().count() == 1: delete_existing = True self.layerset2 = layerset self.save(update_fields=["layerset2"]) @@ -1050,13 +1050,15 @@ def __str__(self): def layer_display_list(self): """For display in the admin interface only.""" li = [ - f"
  • {i}
  • " - for i in self.layer_set.all() + f"
  • {i}
  • " for i in self.get_layers() ] return mark_safe("") layer_display_list.short_description = "Layers" + def get_layers(self) -> Iterable[Layer]: + return self.layer_set.all() + @cached_property def centroid(self): return Polygon.from_bbox(self.extent).centroid @@ -1139,7 +1141,7 @@ def update_multimask_from_geojson(self, multimask_geojson): def save(self, set_tilejson: bool = False, *args, **kwargs): if self._state.adding is False: - extents = self.layer_set.all().values_list("extent", flat=True) + extents = self.get_layers().values_list("extent", flat=True) layer_extents = [] for extent in extents: if extent: diff --git a/ohmg/core/signals.py b/ohmg/core/signals.py index 32998936..00bd17c1 100644 --- a/ohmg/core/signals.py +++ b/ohmg/core/signals.py @@ -61,5 +61,5 @@ def handle_layer_deletion(sender, instance, **kwargs): # remove layerset if this was the last layer attached to it if instance.layerset2: - if instance.layerset2.layer_set.all().count() == 0: + if instance.layerset2.get_layers().count() == 0: instance.layerset2.delete() diff --git a/ohmg/extensions/views.py b/ohmg/extensions/views.py index aa5b1898..81741a38 100644 --- a/ohmg/extensions/views.py +++ b/ohmg/extensions/views.py @@ -41,7 +41,7 @@ def get(self, request, mapid, layerset_category): "label": f"Mosaic of {ls.category.display_name.lower()}, {ls.map}", "items": [ IIIFResource(i.pk, trimmed=trim, extended=extended).get_annotation() - for i in [k for k in ls.layer_set.all()] + for i in [k for k in ls.get_layers()] ], } ) From 5b460aeea7ffa7942f6e6f406f64903f5150938a Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Wed, 11 Mar 2026 17:37:39 -0500 Subject: [PATCH 3/4] add layer mask property, oneoff migrate cmd --- ohmg/core/management/commands/oneoffs.py | 17 +++++++++++++++++ ohmg/core/migrations/0009_layer_mask.py | 19 +++++++++++++++++++ ohmg/core/models.py | 1 + 3 files changed, 37 insertions(+) create mode 100644 ohmg/core/migrations/0009_layer_mask.py diff --git a/ohmg/core/management/commands/oneoffs.py b/ohmg/core/management/commands/oneoffs.py index db1a0cc4..b9c77160 100644 --- a/ohmg/core/management/commands/oneoffs.py +++ b/ohmg/core/management/commands/oneoffs.py @@ -26,6 +26,7 @@ def add_arguments(self, parser): "fix-full-region-files", "set-tilejson", "delete-duplicate-regions", + "add-masks-to-layers", ], help="Choose what operation to run.", ) @@ -408,3 +409,19 @@ def old_ds_to_new(file_set): if not dry_run: delete_ps.delete() print("deleted") + + ## Mar 11th, 2026 + elif operation == "add-masks-to-layers": + from django.contrib.gis.geos import GEOSGeometry + + from ohmg.core.models import Layer, LayerSet + + for ls in LayerSet.objects.all(): + print(ls) + if ls.multimask: + for k, v in ls.multimask.items(): + layer = Layer.objects.get(slug=k, region__document__map=ls.map) + print(k, end=", ") + layer.mask = GEOSGeometry(json.dumps(v["geometry"])) + layer.save(skip_map_lookup_update=True) + print("") diff --git a/ohmg/core/migrations/0009_layer_mask.py b/ohmg/core/migrations/0009_layer_mask.py new file mode 100644 index 00000000..420aa891 --- /dev/null +++ b/ohmg/core/migrations/0009_layer_mask.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.27 on 2026-03-11 17:19 + +import django.contrib.gis.db.models.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_auto_20250915_1937'), + ] + + operations = [ + migrations.AddField( + model_name='layer', + name='mask', + field=django.contrib.gis.db.models.fields.PolygonField(blank=True, null=True, srid=4326), + ), + ] diff --git a/ohmg/core/models.py b/ohmg/core/models.py index 8e6dabd5..a9b7e97d 100644 --- a/ohmg/core/models.py +++ b/ohmg/core/models.py @@ -833,6 +833,7 @@ class Meta: null=True, blank=True, ) + mask = models.PolygonField(blank=True, null=True) file = models.FileField( upload_to="layers", null=True, From cb54a0b8c8d8f2aaaf3b8a5533e37b59ecf045f4 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Thu, 12 Mar 2026 12:28:20 +0000 Subject: [PATCH 4/4] make oneoff command more flexible --- ohmg/core/management/commands/oneoffs.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ohmg/core/management/commands/oneoffs.py b/ohmg/core/management/commands/oneoffs.py index b9c77160..93af59f4 100644 --- a/ohmg/core/management/commands/oneoffs.py +++ b/ohmg/core/management/commands/oneoffs.py @@ -420,8 +420,13 @@ def old_ds_to_new(file_set): print(ls) if ls.multimask: for k, v in ls.multimask.items(): - layer = Layer.objects.get(slug=k, region__document__map=ls.map) - print(k, end=", ") - layer.mask = GEOSGeometry(json.dumps(v["geometry"])) - layer.save(skip_map_lookup_update=True) + print(k) + ## there SHOULD only be one layer here, but per ticket + ## #308 some regions got duplicated, and some of the + ## duplicates got georeferenced :(. Can't clean all of + ## that up now, so... using filter here + layers = Layer.objects.filter(slug=k, region__document__map=ls.map) + for layer in layers: + layer.mask = GEOSGeometry(json.dumps(v["geometry"])) + layer.save(skip_map_lookup_update=True) print("")