From be5ba0d80455034da0e637a76d0b60794246ee4b Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Thu, 28 May 2026 08:10:46 -0500 Subject: [PATCH 1/5] add indicator for presence of xyz tileset --- ohmg/api/schemas.py | 1 + ohmg/core/models/map.py | 8 ++++++++ .../src/components/tables/Maps.svelte | 10 +++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ohmg/api/schemas.py b/ohmg/api/schemas.py index fa1f9eff..9a4d3c58 100644 --- a/ohmg/api/schemas.py +++ b/ohmg/api/schemas.py @@ -89,6 +89,7 @@ class MapListSchema2(Schema): sheet_ct: int volume_number: Optional[str] gt_exists: bool + xyz_tiles_exists: bool featured: bool hidden: bool document_ct: int diff --git a/ohmg/core/models/map.py b/ohmg/core/models/map.py index 50345a0b..498741f2 100644 --- a/ohmg/core/models/map.py +++ b/ohmg/core/models/map.py @@ -207,6 +207,14 @@ def gt_exists(self): else False ) + @property + def xyz_tiles_exists(self): + return ( + True + if self.get_layerset("main-content") and self.get_layerset("main-content").xyz_tiles_url + else False + ) + @property def mj_exists(self): return ( diff --git a/ohmg/frontend/svelte_components/src/components/tables/Maps.svelte b/ohmg/frontend/svelte_components/src/components/tables/Maps.svelte index 10cb3810..be2403a3 100644 --- a/ohmg/frontend/svelte_components/src/components/tables/Maps.svelte +++ b/ohmg/frontend/svelte_components/src/components/tables/Maps.svelte @@ -209,7 +209,8 @@ bind:offset /> - + + @@ -257,6 +258,13 @@ x {/if} + {#if s.xyz_tiles_exists} + + {:else} + x + {/if} {/each} From 5e62ebd1621607a2679247c9ccc3795f1b094e2a Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Thu, 28 May 2026 08:50:29 -0500 Subject: [PATCH 2/5] add topojson python library --- pyproject.toml | 1 + uv.lock | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index fcc71af3..d9b78972 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ # for celery beat "pytz", "rio-tiler>=5.0.3", + "topojson>=1.10", ] [project.optional-dependencies] diff --git a/uv.lock b/uv.lock index e27f95c0..3a4cb75b 100644 --- a/uv.lock +++ b/uv.lock @@ -823,6 +823,7 @@ dependencies = [ { name = "rio-tiler" }, { name = "setuptools" }, { name = "sorl-thumbnail" }, + { name = "topojson" }, ] [package.optional-dependencies] @@ -883,6 +884,7 @@ requires-dist = [ { name = "ruff", marker = "extra == 'dev'" }, { name = "setuptools" }, { name = "sorl-thumbnail", specifier = "==12.8.0" }, + { name = "topojson", specifier = ">=1.10" }, { name = "uwsgi", marker = "extra == 'prod'" }, { name = "zensical", marker = "extra == 'docs'", specifier = ">=0.0.23" }, ] @@ -1276,6 +1278,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/65/3f0dba35760d902849d39d38c0a72767794b1963227b69a587f8a336d08c/setuptools-75.3.2-py3-none-any.whl", hash = "sha256:90ab613b6583fc02d5369cbca13ea26ea0e182d1df2d943ee9cbe81d4c61add9", size = 1251198, upload-time = "2025-03-12T00:02:17.554Z" }, ] +[[package]] +name = "shapely" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/89/c3548aa9b9812a5d143986764dededfa48d817714e947398bdda87c77a72/shapely-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ae48c236c0324b4e139bea88a306a04ca630f49be66741b340729d380d8f52f", size = 1825959, upload-time = "2025-09-24T13:50:00.682Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8a/7ebc947080442edd614ceebe0ce2cdbd00c25e832c240e1d1de61d0e6b38/shapely-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eba6710407f1daa8e7602c347dfc94adc02205ec27ed956346190d66579eb9ea", size = 1629196, upload-time = "2025-09-24T13:50:03.447Z" }, + { url = "https://files.pythonhosted.org/packages/c8/86/c9c27881c20d00fc409e7e059de569d5ed0abfcec9c49548b124ebddea51/shapely-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef4a456cc8b7b3d50ccec29642aa4aeda959e9da2fe9540a92754770d5f0cf1f", size = 2951065, upload-time = "2025-09-24T13:50:05.266Z" }, + { url = "https://files.pythonhosted.org/packages/50/8a/0ab1f7433a2a85d9e9aea5b1fbb333f3b09b309e7817309250b4b7b2cc7a/shapely-2.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e38a190442aacc67ff9f75ce60aec04893041f16f97d242209106d502486a142", size = 3058666, upload-time = "2025-09-24T13:50:06.872Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c6/5a30ffac9c4f3ffd5b7113a7f5299ccec4713acd5ee44039778a7698224e/shapely-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:40d784101f5d06a1fd30b55fc11ea58a61be23f930d934d86f19a180909908a4", size = 3966905, upload-time = "2025-09-24T13:50:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/e92f3035ba43e53959007f928315a68fbcf2eeb4e5ededb6f0dc7ff1ecc3/shapely-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f6f6cd5819c50d9bcf921882784586aab34a4bd53e7553e175dece6db513a6f0", size = 4129260, upload-time = "2025-09-24T13:50:11.183Z" }, + { url = "https://files.pythonhosted.org/packages/42/24/605901b73a3d9f65fa958e63c9211f4be23d584da8a1a7487382fac7fdc5/shapely-2.1.2-cp310-cp310-win32.whl", hash = "sha256:fe9627c39c59e553c90f5bc3128252cb85dc3b3be8189710666d2f8bc3a5503e", size = 1544301, upload-time = "2025-09-24T13:50:12.521Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/6db795b8dd3919851856bd2ddd13ce434a748072f6fdee42ff30cbd3afa3/shapely-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:1d0bfb4b8f661b3b4ec3565fa36c340bfb1cda82087199711f86a88647d26b2f", size = 1722074, upload-time = "2025-09-24T13:50:13.909Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -1330,6 +1351,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] +[[package]] +name = "topojson" +version = "1.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/f3/74330050d8f7e05e140e6043302f71524e159f9a83bbee9681c86282f8fd/topojson-1.10.tar.gz", hash = "sha256:a7f53406324061a0310bec46740a6609147c24daeb354596c68345b9527b38c1", size = 25444042, upload-time = "2025-07-22T20:27:31.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/7a/2a8ea3b2b6e50cbec74c53082b69617d71f31e1247e35e65bf9a6edc44cf/topojson-1.10-py3-none-any.whl", hash = "sha256:0879d727c7798939e3268e8969fa87c2cd23274189fe3d8038a0fb11ff263925", size = 83318, upload-time = "2025-07-22T20:27:28.374Z" }, +] + [[package]] name = "typing-extensions" version = "4.13.2" From 734b3345d73b48d63ac5f56329309dda65bb98c1 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Thu, 28 May 2026 09:10:05 -0500 Subject: [PATCH 3/5] return topojson for footprints, adjust detroit output (special case) --- ohmg/extensions/views.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ohmg/extensions/views.py b/ohmg/extensions/views.py index 4dbc9efb..56a7941c 100644 --- a/ohmg/extensions/views.py +++ b/ohmg/extensions/views.py @@ -1,6 +1,10 @@ +import json + +import topojson from django.http import JsonResponse from django.views import View +from ohmg.conf.http import JsonResponseNotFound from ohmg.core.models import Map from ohmg.core.utils import full_reverse @@ -62,7 +66,16 @@ def get(self, request, place, operation): "name": f"{place.slug}-volume-extents", "features": features, } - return JsonResponse(feature_collection) + topo = topojson.Topology(feature_collection) + topo_json = json.loads(topo.to_json()) + + ## extra key needed for atlascope detroit + if place.slug == "detroit-mi": + topo_json["objects"]["detroit-volume-extents"] = topo_json["objects"]["data"] + return JsonResponse(topo_json) elif operation == "coverages": return JsonResponse([{"name": str(place), "center": place.get_center()}], safe=False) + + else: + return JsonResponseNotFound("invalid operation. must be 'footprints' or 'coverages'") From 68100d6f4745a29b956012df89124c5f9bf8235a Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Thu, 28 May 2026 09:38:47 -0500 Subject: [PATCH 4/5] show xyz tileset url if available --- ohmg/api/schemas.py | 1 + .../src/components/map/MosaicDownload.svelte | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/ohmg/api/schemas.py b/ohmg/api/schemas.py index 9a4d3c58..f4b5d916 100644 --- a/ohmg/api/schemas.py +++ b/ohmg/api/schemas.py @@ -486,6 +486,7 @@ class LayerSetSchema(Schema): extent: Optional[tuple] multimask_extent: Optional[tuple] mosaic_cog_url: Optional[str] + xyz_tiles_url: Optional[str] @staticmethod def resolve_id(obj): diff --git a/ohmg/frontend/svelte_components/src/components/map/MosaicDownload.svelte b/ohmg/frontend/svelte_components/src/components/map/MosaicDownload.svelte index 99625ea9..399b5b4a 100644 --- a/ohmg/frontend/svelte_components/src/components/map/MosaicDownload.svelte +++ b/ohmg/frontend/svelte_components/src/components/map/MosaicDownload.svelte @@ -75,6 +75,12 @@ n/a {/if} + {#if ls.xyz_tiles_url} +
XYZ tileset
+
+
{`${ls.xyz_tiles_url}/{z}/{x}/{y}.png`}
+
+ {/if}
IIIF Georef AnnotationPage (JSON)
Date: Fri, 29 May 2026 02:51:07 +0000 Subject: [PATCH 5/5] fix import error --- ohmg/core/utils/s3.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ohmg/core/utils/s3.py b/ohmg/core/utils/s3.py index 01536439..743a6b59 100644 --- a/ohmg/core/utils/s3.py +++ b/ohmg/core/utils/s3.py @@ -1,3 +1,5 @@ +from django.conf import settings + def get_boto3_s3_client(): import boto3