Skip to content

Commit bd53520

Browse files
authored
Fix Alaska map bbox and prevent recurrence on reload (#639)
1 parent 52f0622 commit bd53520

4 files changed

Lines changed: 155 additions & 0 deletions

File tree

backend/app/utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,36 @@ def add_extent_to_districtrmap(
483483
rec.parent_layer
484484
) INTO layer_extent;
485485
486+
-- A >180-degree-wide bbox is impossible for a legitimate single-state
487+
-- layer. It means the geometry straddles the antimeridian (e.g.
488+
-- Alaska's Aleutian islands, recorded with longitudes on both the
489+
-- -179.x and +179.x side), and the naive ST_XMin/ST_XMax above
490+
-- produced a near-global span instead of the true, much narrower,
491+
-- extent. Recompute using ST_ShiftLongitude, which maps negative
492+
-- longitudes into the 0..360 range so the data becomes contiguous;
493+
-- the resulting bbox may have coordinates outside +/-180, which
494+
-- MapLibre's fitBounds handles correctly for antimeridian-crossing
495+
-- regions.
496+
IF (ST_XMax(layer_extent) - ST_XMin(layer_extent)) > 180 THEN
497+
EXECUTE format('
498+
SELECT ST_Extent(
499+
ST_ShiftLongitude(
500+
ST_Transform(
501+
ST_SetSRID(
502+
geometry,
503+
(SELECT ST_SRID(geometry) FROM gerrydb.%I WHERE geometry IS NOT NULL LIMIT 1)
504+
),
505+
4326
506+
)
507+
)
508+
)
509+
FROM gerrydb.%I
510+
WHERE geometry IS NOT NULL',
511+
rec.parent_layer,
512+
rec.parent_layer
513+
) INTO layer_extent;
514+
END IF;
515+
486516
UPDATE districtrmap
487517
SET extent = ARRAY[
488518
ST_XMin(layer_extent),

backend/tests/conftest.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,49 @@ def simple_child_geos_gerrydb_fixture(session: Session, simple_child_geos):
406406
session.close()
407407

408408

409+
@pytest.fixture(name="antimeridian_geos")
410+
def antimeridian_geos_fixture(session: Session):
411+
"""Two small polygons straddling the antimeridian, mirroring Alaska's Aleutian
412+
islands, so extent computation can be tested for that failure mode."""
413+
layer = "antimeridian_geos"
414+
result = subprocess.run(
415+
args=[
416+
"ogr2ogr",
417+
"-f",
418+
"PostgreSQL",
419+
OGR2OGR_PG_CONNECTION_STRING,
420+
FIXTURES_PATH / "gerrydb" / f"{layer}.geojson",
421+
"-lco",
422+
"OVERWRITE=yes",
423+
"-nln",
424+
f"{GERRY_DB_SCHEMA}.{layer}",
425+
"-nlt",
426+
"MULTIPOLYGON",
427+
"-lco",
428+
"GEOMETRY_NAME=geometry",
429+
],
430+
)
431+
432+
if result.returncode != 0:
433+
print(f"ogr2ogr failed. Got {result}")
434+
raise ValueError(f"ogr2ogr failed with return code {result.returncode}")
435+
436+
437+
@pytest.fixture(name="antimeridian_geos_gerrydb")
438+
def antimeridian_geos_gerrydb_fixture(session: Session, antimeridian_geos):
439+
upsert_query = text("""
440+
INSERT INTO gerrydbtable (uuid, name, updated_at)
441+
VALUES (gen_random_uuid(), :name, now())
442+
ON CONFLICT (name)
443+
DO UPDATE SET
444+
updated_at = now()
445+
""")
446+
session.begin()
447+
session.execute(upsert_query, {"name": "antimeridian_geos"})
448+
session.commit()
449+
session.close()
450+
451+
409452
@pytest.fixture(name="gerrydb_simple_geos_view")
410453
def gerrydb_simple_geos_view_fixture(
411454
session: Session, simple_parent_geos_gerrydb, simple_child_geos_gerrydb
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"type": "FeatureCollection",
3+
"name": "antimeridian_geos",
4+
"crs": {
5+
"type": "name",
6+
"properties": {
7+
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
8+
}
9+
},
10+
"features": [
11+
{
12+
"type": "Feature",
13+
"properties": {
14+
"path": "vtd:000020000001",
15+
"total_pop_20": 100,
16+
"white_pop_20": 90,
17+
"black_pop_20": 5
18+
},
19+
"geometry": {
20+
"type": "MultiPolygon",
21+
"coordinates": [
22+
[
23+
[
24+
[179.5, 51.0],
25+
[179.9, 51.0],
26+
[179.9, 51.5],
27+
[179.5, 51.5],
28+
[179.5, 51.0]
29+
]
30+
]
31+
]
32+
}
33+
},
34+
{
35+
"type": "Feature",
36+
"properties": {
37+
"path": "vtd:000020000002",
38+
"total_pop_20": 100,
39+
"white_pop_20": 90,
40+
"black_pop_20": 5
41+
},
42+
"geometry": {
43+
"type": "MultiPolygon",
44+
"coordinates": [
45+
[
46+
[
47+
[-179.9, 51.0],
48+
[-179.5, 51.0],
49+
[-179.5, 51.5],
50+
[-179.9, 51.5],
51+
[-179.9, 51.0]
52+
]
53+
]
54+
]
55+
}
56+
}
57+
]
58+
}

backend/tests/test_utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,30 @@ def test_add_districtr_map_already_in_group_to_group(
245245
assert result == 2
246246

247247

248+
def test_add_extent_to_districtrmap_antimeridian(
249+
session: Session, antimeridian_geos_gerrydb
250+
):
251+
"""Regression test for the Alaska bbox bug: geometry straddling the antimeridian
252+
(vertices on both the -179.x and +179.x side) must not produce a bogus
253+
~360-degree-wide extent -- the true angular width here is ~1 degree."""
254+
inserted_districtr_map = create_districtr_map(
255+
session,
256+
name="Antimeridian layer",
257+
districtr_map_slug="antimeridian_geos_test",
258+
gerrydb_table_name="antimeridian_geos",
259+
parent_layer="antimeridian_geos",
260+
)
261+
add_extent_to_districtrmap(
262+
session=session, districtr_map_uuid=inserted_districtr_map
263+
)
264+
extent = session.execute(
265+
text("SELECT extent FROM districtrmap WHERE uuid = :uuid"),
266+
{"uuid": inserted_districtr_map},
267+
).scalar_one()
268+
x_min, y_min, x_max, y_max = extent
269+
assert x_max - x_min < 10, f"Expected a narrow bbox, got extent={extent}"
270+
271+
248272
def test_add_extent_to_districtrmap_manual_bounds(
249273
session: Session, simple_parent_geos_gerrydb
250274
):

0 commit comments

Comments
 (0)