Vendor the MapToPoster pipeline with AXP themes and an AXP Runde font hook - #12
Vendor the MapToPoster pipeline with AXP themes and an AXP Runde font hook#12maceip wants to merge 1 commit into
Conversation
… hook tools/poster is a copy of originalankur/maptoposter (MIT) that renders city map posters from OpenStreetMap data, for marketing images that look like they come from our world. Two placeholder themes (axp_outdoor, axp_indoor) mirror today's workspace tokens and will be redone once the design language is locked; the font loader takes AXP Runde (or any bold/regular/light set) from tools/poster/fonts and falls back to matplotlib's DejaVu Sans, so Roboto is not vendored. `axp-poster CITY COUNTRY [outdoor|indoor] [metres]` wraps the CLI through uv with the pinned lockfile. Verified end to end: both Portland renders are in docs/design/posters as review-size JPEGs; full renders and the Python environment are ignored.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6cbaebbc7b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ) as pbar: | ||
| # 1. Fetch Street Network | ||
| pbar.set_description("Downloading street network") | ||
| compensated_dist = dist * (max(height, width) / min(height, width)) / 4 # To compensate for viewport crop |
There was a problem hiding this comment.
Preserve the requested map radius
For every normal poster size, dividing by 4 makes the rendered area substantially smaller than the documented --distance radius. With the documented 12×16 layout and --distance 6000, this fetches and crops around only 2,000 m vertically and 1,500 m horizontally, so the advertised downtown/whole-city framing is incorrect; compensate only for the dimension that would otherwise be cropped while retaining the requested radius.
Useful? React with 👍 / 👎.
| # Extract URL (prefer woff2, fallback to ttf) | ||
| url_match = re.search(r"url\((https://[^)]+\.(woff2|ttf))\)", block) | ||
| if url_match: | ||
| weight_url_map[weight] = url_match.group(1) |
There was a problem hiding this comment.
Retain every Unicode subset for each font weight
When Google Fonts returns multiple @font-face blocks for the same weight, as it commonly does for CJK and other large multilingual families, each assignment overwrites the preceding subset URL. Only the final subset is downloaded, so display text whose glyphs belong to earlier subsets renders as missing-glyph boxes despite --font-family; retain/download all URLs for a weight or obtain a complete font file.
Useful? React with 👍 / 👎.
| "bold": ["AXPRunde-Bold.otf", "AXPRunde-Bold.ttf", "Roboto-Bold.ttf"], | ||
| "regular": ["AXPRunde-Medium.otf", "AXPRunde-Regular.otf", "Roboto-Regular.ttf"], | ||
| "light": ["AXPRunde-Regular.otf", "Roboto-Light.ttf"], |
There was a problem hiding this comment.
Accept the advertised arbitrary local font sets
The README says $AXP_POSTER_FONT_DIR may contain any bold/regular/light set, but the loader recognizes only exact AXP Runde and Roboto filenames. A directory containing files such as Brand-Bold.otf, Brand-Regular.otf, and Brand-Light.otf is therefore rejected and silently falls back to DejaVu; either discover files by weight or require and document explicit filenames.
Useful? React with 👍 / 👎.
| tools/poster/posters/ | ||
| tools/poster/fonts/*.otf | ||
| tools/poster/fonts/*.ttf | ||
| tools/poster/.venv/ |
There was a problem hiding this comment.
After an uncached city or distance is rendered, cache_set creates new .pkl files under tools/poster/cache, and normal Python execution can also create tools/poster/__pycache__; neither path is ignored here. The documented workflow consequently leaves generated binary artifacts as untracked files after routine use, so these cache directories should be added alongside the other poster runtime outputs.
Useful? React with 👍 / 👎.
| with open(path, "rb") as f: | ||
| return pickle.load(f) | ||
| except Exception as e: | ||
| raise CacheError(f"Cache read failed: {e}") from e |
There was a problem hiding this comment.
Recover from unreadable cache entries
If a process is interrupted while cache_set writes directly to a cache file, the resulting truncated pickle makes this pickle.load raise CacheError; every caller invokes cache_get before its network-fetch try, so all later runs with the same city and distance fail permanently instead of refreshing the data. Treat an unreadable entry as a cache miss and remove it, or write entries through an atomic temporary-file replacement.
Useful? React with 👍 / 👎.
| if args.latitude and args.longitude: | ||
| lat = parse(args.latitude) | ||
| lon = parse(args.longitude) | ||
| coords = [lat, lon] | ||
| print(f"✓ Coordinates: {', '.join([str(i) for i in coords])}") | ||
| else: | ||
| coords = get_coordinates(args.city, args.country) |
There was a problem hiding this comment.
Reject incomplete coordinate overrides
When a user supplies only --latitude or only --longitude, this condition falls through to geocoding and silently ignores the provided coordinate. A typo or omitted half of the documented coordinate pair can therefore generate a poster centered on an entirely different location; validate that the two options are supplied together and fail before fetching data when only one is present.
Useful? React with 👍 / 👎.
| f"{lat:.4f}° N / {lon:.4f}° E" | ||
| if lat >= 0 | ||
| else f"{abs(lat):.4f}° S / {lon:.4f}° E" |
There was a problem hiding this comment.
Remove the minus sign from western coordinates
For every location west of Greenwich, the formatted longitude retains its negative numeric sign and then changes the hemisphere suffix from E to W, producing labels such as -122.6742° W. That expresses the direction twice and is not a valid conventional coordinate label; format the longitude with abs(lon) whenever the W suffix is used.
Useful? React with 👍 / 👎.
| try: | ||
| water_polys = ox.projection.project_gdf(water_polys) | ||
| except Exception: | ||
| water_polys = water_polys.to_crs(g_proj.graph['crs']) |
There was a problem hiding this comment.
Project feature layers into the graph CRS explicitly
For features whose bounds cause project_gdf to select a different projected CRS than project_graph—for example, a large water or park geometry near a UTM-zone boundary—this call succeeds but yields coordinates from the other zone, so the layer is displaced or disappears from the graph axes and the fallback never runs. Pass g_proj.graph['crs'] explicitly for both water and parks rather than independently auto-selecting their CRSs.
Useful? React with 👍 / 👎.
| bbox_inches="tight", | ||
| pad_inches=0.05, |
There was a problem hiding this comment.
Preserve the requested raster dimensions
For PNG output, figsize and 300 DPI would produce the exact resolutions documented in the resolution guide, but bbox_inches="tight" recomputes the saved bounding box and pad_inches=0.05 changes it again. Requests such as 3.6×3.6 inches therefore do not reliably yield 1080×1080 pixels; save the full figure canvas without tight-bbox cropping when fixed output dimensions are requested.
Useful? React with 👍 / 👎.
What
tools/postervendors originalankur/maptoposter (MIT), which renders city map posters from OpenStreetMap data, for marketing images that look like they come from our world.axp_outdoorandaxp_indoor, mirror today's workspace tokens and will be redone once the design language is locked (the theme schema is eleven colours)tools/poster/fonts, falls back to matplotlib's DejaVu Sans; Roboto is not vendoredtools/poster/axp-poster CITY COUNTRY [outdoor|indoor] [metres]wraps the CLI throughuvwith the pinned lockfiledocs/design/posters/; full renders and the Python environment are ignoredLook at
docs/design/posters.md(renders inline)tools/poster/README.mdVerification
npm run checkpasses (the tool is outside the Node toolchain;uv runinstalls its own environment).Landing
No conflicts with siblings.