A world map, and clicking a country to select it - #82
Merged
Conversation
ui.worldMap({ onSelect: country => console.log(country?.name) });
The map is the canvas doing what it already does. Once it draws in the
caller's own coordinates, longitude and latitude are just another pair of
axes, so the world is 258 polylines in degrees and needs no projection of its
own beyond choosing which window on the globe to show.
The half worth building is the other direction. A click arrives as a terminal
cell, and a country is a polygon, so answering "what did they click" means
turning the cell back into degrees and ray-casting it against the outlines.
Doing it with bounding boxes would be far cheaper and consistently wrong:
Russia's box covers most of the northern hemisphere and Chile's covers
Argentina. The lookup is exact, and open water comes back as nothing rather
than as whichever box happened to be first.
The inverse has to agree with the drawing or every click lands somewhere the
user did not point at -- and nothing about the map would look wrong while it
did. So `degreesAt` takes the cell centre through the same 2x4 Braille
denominators the canvas projects with, and a test round-trips all 2600 cells
of a 100x26 map back to themselves.
The data is Natural Earth 1:110m Admin 0, which is public domain, simplified
by Douglas-Peucker to one degree. A world map across eighty columns is about
160 Braille pixels wide, so a pixel is two degrees and one degree of tolerance
is already finer than the terminal can show; it keeps 171 of the 177 countries
in 1982 points. The six it drops are island states smaller than a pixel, which
could be neither seen nor clicked. `scripts/generate-world.ts` fetches and
regenerates it, so the 36KB of coordinates is derived rather than pasted.
Rings are drawn closed, so a coastline has no seam where the ring began.
TypeScript only. The shape and the lookup port cleanly, but the dataset would
be a seventh copy in every port, and that is a call about repo weight rather
than about the feature -- so it is not in the widget gallery either, which
promises a runnable example in all eleven languages.
`bun examples/world.ts` for the clickable version: hover, click, z to zoom to
the selection, r to go back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
The generator now emits the dataset for every port from the one Natural Earth source, so the six copies cannot drift: regenerating rewrites all of them or none of them. Roughly 230KB in total, 32-44KB per language, all derived. C++ gets the coordinates as a flat pool with index tables rather than nested initialiser lists. The nested form is the same data and minutes of compile time, and C++ is the one language where that bites; the pool is turned into the shape the rest of the code wants once, on first use. Agreement is checked rather than assumed. Every port ships a `world-probe` that prints what the lookup answers for thirteen places -- nine capitals, four patches of open ocean -- plus four cell clicks and three raw projections, and all five ports diff clean against the TypeScript reference. That catches what the render fixtures cannot: a country lookup can be wrong in a way that still draws a perfectly good-looking map. Three fixtures pin the drawing itself: the whole globe, a window zoomed on Japan, and two highlighted countries. 93 widget scenes now, up from 90. Verified: 353 TS tests under bun and node; 93 widget scenes matching in Rust, Go, Python, Zig and C++; five world probes diffing clean against TypeScript; 11/11 ctest; every gallery; the site and the benchmark. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
bun examples/world.tsfor the clickable version — hover, click,zto zoom to the selection,rto go back.Drawing it is nearly free
Once the canvas draws in the caller's own coordinates (#80), longitude and latitude are just another pair of axes. The world is 258 polylines in degrees and needs no projection of its own beyond choosing which window on the globe to show.
Clicking it is the part worth building
A click arrives as a terminal cell; a country is a polygon. Answering "what did they click" means turning the cell back into degrees and ray-casting it against the outlines.
Bounding boxes would be far cheaper and consistently wrong: Russia's box covers most of the northern hemisphere and Chile's covers Argentina. The lookup is exact, and open water comes back as nothing rather than as whichever box happened to be first.
The inverse has to agree with the drawing, or every click lands somewhere the user did not point at — and nothing about the map would look wrong while it did. So
degreesAttakes the cell centre through the same 2×4 Braille denominators the canvas projects with, and a test round-trips all 2,600 cells of a 100×26 map back to themselves.The data
Natural Earth 1:110m Admin 0 countries, which is public domain, simplified by Douglas-Peucker to one degree. A world map across eighty columns is about 160 Braille pixels wide, so one pixel is two degrees — one degree of tolerance is already finer than the terminal can show. That keeps 171 of the 177 countries in 1,982 points; the six it drops are island states smaller than a pixel, which could be neither seen nor clicked.
scripts/generate-world.tsemits the dataset for every port from that one source, so the six copies cannot drift: regenerating rewrites all of them or none. Roughly 230KB in total, 32–44KB per language, all derived rather than pasted.C++ gets the coordinates as a flat pool with index tables rather than nested initialiser lists — the nested form is the same data and minutes of compile time, and C++ is the one language where that bites.
Rings are drawn closed, so a coastline has no seam where the ring began.
Agreement is checked, not assumed
Every port ships a
world-probethat prints what the lookup answers for thirteen places — nine capitals, four patches of open ocean — plus four cell clicks and three raw projections. All five native ports diff clean against the TypeScript reference.That catches what the render fixtures cannot: a country lookup can be wrong in a way that still draws a perfectly good-looking map.
Three fixtures pin the drawing itself — the whole globe, a window zoomed on Japan, and two highlighted countries.
Verified
bun testandnode --test, typecheck cleannext build, the benchmarkNot in the website widget gallery: that promises a runnable example in all eleven languages, and the four bridge languages reach the library through a narrower core.
🤖 Generated with Claude Code
https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy