Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions skills/aaif-create-chapter/scripts/create_chapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,12 @@ def lat2y(lat):
# are dragged WEST to nearly Tokyo's x, so a separable lon/lat projection can't
# place East-Asia/Oceania. (Tokyo itself IS placed correctly by the linear
# formula and needs no override — it's only the landmark showing how far west the
# others land.) Keep a per-city pixel override table for the cities that need it.
PIXEL_OVERRIDES = {"Seoul": (822, 305), "Sydney": (870, 512), "Melbourne": (836, 543)}
# others land.) Separately, Shanghai (~121°E) IS placed almost correctly by the
# linear formula but lands ~8px offshore; its override only nudges it west onto the
# Chinese coast — a small cosmetic fix, not the gross Oceania distortion above.
# Keep a per-city pixel override table for the cities that need it.
PIXEL_OVERRIDES = {"Seoul": (822, 305), "Sydney": (870, 512), "Melbourne": (836, 543),
"Shanghai": (833, 330)}

def project_city(name, lat, lon):
"""Map a city to (x, y) pixels on image18.png. Overridden cities take their
Expand Down Expand Up @@ -338,6 +342,16 @@ def resolve_latlon(name, lat, lon):
"value and geocoding %r instead." % name)
return geocode_city(name)

def map_dot_latlon(name, lat, lon):
"""Coordinates to feed the slide-5 map-dot placement, or None to leave the dot
at San Francisco. Fixed-pixel-override cities are placed by NAME (project_city
ignores lat/lon for them), so they're placeable WITHOUT geocoding: return a
sentinel instead of gating them on a network lookup that can't change where the
dot lands. Explicit --lat/--lon still short-circuit to resolve_latlon."""
if name in PIXEL_OVERRIDES and not (lat is not None and lon is not None):
return (0.0, 0.0) # value unused by project_city; just marks the dot placeable
return resolve_latlon(name, lat, lon)

# ----------------------------------------------------------------------------
# Drive helpers (via the gws CLI)
# ----------------------------------------------------------------------------
Expand Down Expand Up @@ -478,11 +492,20 @@ def main():
print("Upper: %s" % upper)
print("Slug : aaif-%s -> https://luma.com/aaif-%s" % (slug, slug))

# Coordinates for the slide-5 network-map dot (override -> geocode -> none).
latlon = resolve_latlon(name, a.lat, a.lon)
if latlon:
# Coordinates for the slide-5 network-map dot (explicit -> pixel-override
# sentinel -> geocode -> none). Fixed-pixel cities place by name, so they never
# get the "stays at San Francisco" warning just because geocoding was skipped.
pixel_override = name in PIXEL_OVERRIDES
latlon = map_dot_latlon(name, a.lat, a.lon)
if pixel_override and not (a.lat is not None and a.lon is not None):
print("Coords: -- (fixed pixel override for %s; lat/lon not needed)" % name)
elif latlon:
src = "override" if (a.lat is not None and a.lon is not None) else "geocoded"
print("Coords: %.4f, %.4f (%s)" % (latlon[0], latlon[1], src))
if pixel_override:
print("Note: %s has a fixed pixel override for the slide-5 map dot; the "
"coordinates above do NOT position it (--lat/--lon ignored for "
"placement)." % name)
else:
print("Coords: --")
print("WARNING: could not resolve coordinates for %r; the slide-5 map dot "
Expand Down
35 changes: 35 additions & 0 deletions skills/aaif-create-chapter/scripts/test_create_chapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ def test_pixel_override_wins_over_formula(self):
self.assertEqual(cc.project_city("Seoul", 0.0, 0.0), (822, 305))
self.assertEqual(cc.project_city("Sydney", 99.0, 99.0), (870, 512))

def test_every_override_pixel_is_within_the_map(self):
# A transposed or fat-fingered override (e.g. (3833, 330)) would place the
# dot off-canvas and be placed silently. Guard the whole table at once.
w, h = cc.MAP_PX
for city, (x, y) in cc.PIXEL_OVERRIDES.items():
self.assertTrue(0 <= x <= w, "%s x=%d out of 0..%d" % (city, x, w))
self.assertTrue(0 <= y <= h, "%s y=%d out of 0..%d" % (city, y, h))

def test_lat2y_hits_every_anchor_exactly(self):
for lat, y in cc.LAT_ANCHORS:
self.assertAlmostEqual(cc.lat2y(lat), y, places=6, msg="anchor %s" % lat)
Expand Down Expand Up @@ -230,6 +238,33 @@ def test_ungeocodable_returns_none(self):
cc.geocode_city = orig


class TestMapDotLatlon(unittest.TestCase):
def test_override_city_is_placeable_without_geocoding(self):
# An override city (placed by name) must stay placeable even if geocoding
# would fail — and must NOT hit the network to decide that.
calls = []
orig = cc.geocode_city
cc.geocode_city = lambda name, **kw: calls.append(name) or None
try:
self.assertIsNotNone(cc.map_dot_latlon("Shanghai", None, None))
self.assertEqual(calls, []) # no geocode call for a fixed-pixel city
finally:
cc.geocode_city = orig

def test_non_override_ungeocodable_still_none(self):
orig = cc.geocode_city
cc.geocode_city = lambda name, **kw: None
try:
self.assertIsNone(cc.map_dot_latlon("Tatooine", None, None))
finally:
cc.geocode_city = orig

def test_explicit_latlon_wins_even_for_override_city(self):
# Passing both coords short-circuits to resolve_latlon (no network), so the
# user's values flow through unchanged (project_city still ignores them).
self.assertEqual(cc.map_dot_latlon("Shanghai", 31.23, 121.47), (31.23, 121.47))


class _FakeResp:
def __init__(self, body):
self._body = body.encode("utf-8") if isinstance(body, str) else body
Expand Down
Loading