From 39831e61c5dcfb4af6a6ccca3fc1d5320461e9bc Mon Sep 17 00:00:00 2001 From: Rahul Parundekar Date: Tue, 7 Jul 2026 23:51:28 +0530 Subject: [PATCH 1/3] fix(create-chapter): add Shanghai pixel override for slide-5 map dot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shanghai (~121°E) sits in the map's distorted western-Pacific zone where the separable lon/lat projection can't place cities (same reason Seoul, Sydney, and Melbourne already have overrides). The linear formula lands it ~10px out to sea; pin it to the Chinese coast at pixel (833, 330). Verified against image18.png by rendering the projected dot on the map. Co-Authored-By: Claude Opus 4.8 --- skills/aaif-create-chapter/scripts/create_chapter.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/skills/aaif-create-chapter/scripts/create_chapter.py b/skills/aaif-create-chapter/scripts/create_chapter.py index f938203..69dd4f2 100755 --- a/skills/aaif-create-chapter/scripts/create_chapter.py +++ b/skills/aaif-create-chapter/scripts/create_chapter.py @@ -215,8 +215,11 @@ 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.) Shanghai (~121°E) lands ~10px out to sea by the linear formula — +# nudged west onto the Chinese coast. 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 From d03691c5431908b95b9ecc4ee8b287dea385795b Mon Sep 17 00:00:00 2001 From: Rahul Parundekar Date: Wed, 8 Jul 2026 00:00:52 +0530 Subject: [PATCH 2/3] fix: address self-review findings - Correct the Shanghai comment (~10px -> ~8px offshore) and set it apart from the gross Oceania distortion it was spliced into - Print a Note when a city uses a fixed pixel override, so the reported Coords line isn't mistaken for where the dot lands - Add a table-wide test that every PIXEL_OVERRIDES pixel is within the map Co-Authored-By: Claude Opus 4.8 --- skills/aaif-create-chapter/scripts/create_chapter.py | 11 ++++++++--- .../scripts/test_create_chapter.py | 8 ++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/skills/aaif-create-chapter/scripts/create_chapter.py b/skills/aaif-create-chapter/scripts/create_chapter.py index 69dd4f2..4172ab7 100755 --- a/skills/aaif-create-chapter/scripts/create_chapter.py +++ b/skills/aaif-create-chapter/scripts/create_chapter.py @@ -215,9 +215,10 @@ 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.) Shanghai (~121°E) lands ~10px out to sea by the linear formula — -# nudged west onto the Chinese coast. Keep a per-city pixel override table for the -# cities that need it. +# 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)} @@ -486,6 +487,10 @@ def main(): if 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 name in PIXEL_OVERRIDES: + print("Note: %s has a fixed pixel override for the slide-5 map dot; the " + "coordinates above do NOT position it (and --lat/--lon are ignored " + "for placement)." % name) else: print("Coords: --") print("WARNING: could not resolve coordinates for %r; the slide-5 map dot " diff --git a/skills/aaif-create-chapter/scripts/test_create_chapter.py b/skills/aaif-create-chapter/scripts/test_create_chapter.py index dd6641d..3a39bae 100644 --- a/skills/aaif-create-chapter/scripts/test_create_chapter.py +++ b/skills/aaif-create-chapter/scripts/test_create_chapter.py @@ -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) From 5ee3fe694fc50736e29d2e760c63293ab2b3ceb3 Mon Sep 17 00:00:00 2001 From: Rahul Parundekar Date: Wed, 8 Jul 2026 00:14:24 +0530 Subject: [PATCH 3/3] fix: address PR review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot: fixed-pixel-override cities were gated on geocoding succeeding, so an override city (e.g. Shanghai) would fall back to San Francisco if Nominatim was unreachable — even though its dot is placed by name and never needs coordinates. - Add map_dot_latlon(): returns a sentinel for override cities when no explicit --lat/--lon is given, so they're placeable without a network lookup and skip the "stays at San Francisco" warning - main() uses it and prints a clear "fixed pixel override" coords line - Cover the new helper (placeable-without-geocode, non-override still None, explicit coords win) Co-Authored-By: Claude Opus 4.8 --- .../scripts/create_chapter.py | 27 ++++++++++++++----- .../scripts/test_create_chapter.py | 27 +++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/skills/aaif-create-chapter/scripts/create_chapter.py b/skills/aaif-create-chapter/scripts/create_chapter.py index 4172ab7..7d1974a 100755 --- a/skills/aaif-create-chapter/scripts/create_chapter.py +++ b/skills/aaif-create-chapter/scripts/create_chapter.py @@ -342,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) # ---------------------------------------------------------------------------- @@ -482,15 +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 name in PIXEL_OVERRIDES: + if pixel_override: print("Note: %s has a fixed pixel override for the slide-5 map dot; the " - "coordinates above do NOT position it (and --lat/--lon are ignored " - "for placement)." % name) + "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 " diff --git a/skills/aaif-create-chapter/scripts/test_create_chapter.py b/skills/aaif-create-chapter/scripts/test_create_chapter.py index 3a39bae..3eef8c8 100644 --- a/skills/aaif-create-chapter/scripts/test_create_chapter.py +++ b/skills/aaif-create-chapter/scripts/test_create_chapter.py @@ -238,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