function applyInsetTransform
applyInsetTransform(point: readonly [number, number], transform: InsetTransform): [number, number]
The same move, applied to a point: a centroid, so a handle follows its region.
@johnmorrisdotca/chizu 1.0.0 · 6 entry points · 118 exports
@johnmorrisdotca/chizuapplyInsetTransform Bounds boxCentre boxIsWholeMap boxToViewBox CALLOUT_INSET CALLOUT_RADIUS_RATIO CALLOUT_ROOMY calloutFaults calloutGrid CalloutKeepOut CalloutLand CalloutObstacle CalloutPlace CalloutRequest calloutSpaces CalloutSpot CHIZU_STRINGS ChizuCountry ChizuInset ChizuLanguage chizuLanguageOf ChizuMap ChizuPlace ChizuProjection ChizuRegion chizuSay circleMeetsRing distanceToRing DISTRACTOR_SCORES DistractorOptions distractorScore findQuestion FindQuestion focusBox focusRegionFit HANDLE_CLEARANCE HANDLE_LAYOUTS HANDLE_RADIUS_RATIO HANDLE_SLOTS HandleBounds HandleLayout handleRadius HandleSpot insetFor insetTransform InsetTransform insetTransformAttribute isMapZoom landAnchor layoutCallouts mainlandBounds MAP_ZOOM_LEVELS MapBox mapDiagonal MapOutline mapOutlines MapPiece mapRegionPieces MapRing mapWrapsAround MapZoom MatchOptions nameOf nearestWrappedBox orderByPosition parseMapRings PastedPlaces pickDistractors placeCallouts placeHandles placesFromText pointInRing POLISH_CIRCLE_CLEAR_RADII POLISH_CLOSE_RADII projectPoint Random regionBox regionCentre Scorable seaAround seededRandom segmentCrossesBox segmentMeetsRing segmentsCross segmentsGap shapeGlyphBox shiftedOutlines shuffled splitPastedPlaces stepMapZoom unprojectPoint VERSION wholeMapBox wrapAcross wrapIntoBox wrapOffsets zoomBox zoomToFit
applyInsetTransform(point: readonly [number, number], transform: InsetTransform): [number, number]
The same move, applied to a point: a centroid, so a handle follows its region.
type Bounds = readonly [number, number, number, number];
The box round a shape: left, top, right, bottom.
boxCentre(box: MapBox): { x: number; y: number; }
Where a box is looking, which is what a zoom step keeps hold of.
boxIsWholeMap(map: Pick<ChizuMap, "width" | "height">, box: MapBox): boolean
True when the box shows the whole map rather than a zoomed-in window.
boxToViewBox(box: MapBox): string
A window as an SVG viewBox attribute.
CALLOUT_INSET: 1.6
How far in from the frame's edge the outermost circle's centre sits, in radii.
CALLOUT_RADIUS_RATIO: 0.03
The share of the window's width a circle's radius is, unless asked otherwise.
CALLOUT_ROOMY: 2
The sea a circle would like around it, in radii, and what having less costs it - so that open water wins over a bay with barely the room, without a bay being refused when it is the only water a region has.
A sheet whose numbers sat on the coast read as numbers on the land: they need to be well away from the shore, so the sea a circle has is weighed, up to this.
calloutFaults(spots: ReadonlyArray<{ x: number; y: number; hx: number; hy: number; }>, radius: number): { crossings: number; close: number; }
The two faults a printed sheet is held to, counted over its placed leaders: pairs that cross, and pairs closer than a radius - or than they start, where two regions start closer than that - counting a line through another number's circle as close too.
calloutGrid(radius: number, box: MapBox, keepOut: CalloutKeepOut, land: readonly CalloutLand[]): { places: CalloutPlace[]; cols: number; step: number; }
The same places, with the grid they were found on.
type CalloutKeepOut = { bottom: number; right: number };
The lengths of frame kept clear at the bottom-right corner, in the map's units: up the right edge before the corner and along the bottom edge after it, which is where the credit line is drawn.
type CalloutLand = { rings: readonly MapRing[] };
One drawn region, as the outlines it is actually drawn with.
A circle keeps out of the rings themselves, so the Seto Inland Sea, Tokyo Bay and the gap between Kyushu and Shikoku are all places a number can go. A leader is only measured against the boxes around those rings, because "cross minimal number of states" is a count of places rather than a question about a coastline, and the leaders are costed thousands of times a draw.
type CalloutObstacle = { minX: number; minY: number; maxX: number; maxY: number };
A box, which is what a leader is kept off and what the credit's corner is.
type CalloutPlace = { x: number; y: number; row: number; col: number; /** Water between the circle's edge and the nearest land, in radii, up to `CALLOUT_ROOMY`. */ sea: number; };
One place a circle could sit, and where it sits on the search grid.
The row and column are carried rather than recovered, so asking whether a place is too near a circle already put down is a look at the handful of cells around it instead of a walk through every circle on the map.
type CalloutRequest = { /** The regions to number. A region the window does not show is not numbered. */ codes: readonly string[]; /** The window the map is drawn in: circles are placed inside it. Default: the whole map. */ box?: MapBox; /** The circle's radius as a share of the window's width. Default 0.03. */ radiusRatio?: number; /** * Finish with the slow pass that clears every near-miss: no two leaders closer t…
calloutSpaces(radius: number, box: MapBox, keepOut: CalloutKeepOut, land: readonly CalloutLand[]): CalloutPlace[]
Every place in the frame where a circle fits clear of the land and of the credit's corner, on a grid fine enough to find the water beside a coast.
A frame with no room at all - a map zoomed until the land fills it - gets the grid unfiltered rather than nothing, because a number on land is still better than a number that is not drawn.
type CalloutSpot = { /** The region it names. */ code: string; /** Its number: where it was in the list asked for, from 1 (or its place from the west, with `numbering: "west-to-east"`). */ number: number; /** Where its leader starts: a point on the region's own land, as drawn. */ start: [number, number]; /** The circle's centre. */ circle: [number, number]; /** The circle's radius, in the map's units. */ radius: num…
One numbered circle placed in open water, and the leader line that joins it to its region.
CHIZU_STRINGS: Record<ChizuLanguage, Record<string, string>>
interface ChizuCountry { /** ISO 3166-1 alpha-2, or Natural Earth's own three letters where a territory has none. Lower case in a file's name. */ code: string; iso3: string; name: string; nameJa: string; nameShortJa?: string; reading?: string; /** The continent. */ group: string; /** Whether the coarse world map draws it (the smallest places are only in the country's own file). */ onWorld: boolean; /** Whether it ha…
One country in the table of every country: its names, and which of the maps has it.
interface ChizuInset { /** The region drawn in the box. */ code: string; /** Where the box is on the map's canvas. */ box: MapBox; /** Only the region's outlying islands go in the box: the pieces that begin below this line on the canvas. The rest stays where it is. */ outlyingBelow?: number; /** Whether the box may make what it holds bigger than life. Off, a region is only ever shrunk to fit. */ magnify?: boolean; }
A region the map draws in a box of its own, instead of where its projection put it (Alaska, Hawaii, Okinawa).
type ChizuLanguage = "en" | "ja";
THE WORDS chizu says itself, in English and Japanese: what a screen reader hears of a drawing, and the labels on a mounted map's buttons. Plain data, so a page can read them, replace a few or add a language of its own beside these two. {name} in a line is a value filled in.
chizuLanguageOf(tag: string | undefined | null): ChizuLanguage
A language this package has words for, from a tag such as ja-JP or en: Japanese for any ja, English otherwise.
interface ChizuMap { /** `world`, `country-fr` (France alone, from Natural Earth's countries) or `divisions-fr` (its départements). */ id: string; /** What its regions are: the whole `world`, a single `country`, or the `divisions` of one. */ kind: "world" | "country" | "divisions"; /** The map's name in English and Japanese. */ name: string; nameJa?: string; /** What a region is called here, singular and plural, in …
A map: a canvas and its regions. What every file of data holds, and what every function of the engine takes.
type ChizuPlace = Pick<ChizuRegion, "code" | "path" | "bbox" | "centroid" | "neighbors" | "group">;
The part of a region the engine reads, so a map of your own regions needs no more than these.
type ChizuProjection = | { kind: "miller"; centre: number; scale: number; translate: [number, number] } | { kind: "azimuthal-equal-area"; centre: [number, number]; scale: number; translate: [number, number] } | { kind: "other"; description: string };
How a map's canvas was made from the earth, enough to put a longitude and latitude on it (projectPoint): Miller's cylindrical projection round a centre longitude (the world), or a Lambert azimuthal equal-area projection round a centre (one country on its own).
interface ChizuRegion { /** Its code within its map: an ISO 3166-1 alpha-2 code on the world (`JP`), the postal or ISO 3166-2 part on a country (`ON`). */ code: string; /** Its English name. */ name: string; /** Its name in Japanese, where there is one. */ nameJa?: string; /** The everyday short name where it is not `nameJa` (アメリカ for アメリカ合衆国). */ nameShortJa?: string; /** How `nameShortJa ?? nameJa` is read, in kan…
One place on a map: a country on the world, a province or state on a country.
chizuSay(language: ChizuLanguage, key: string, values?: Record<string, string | number>): string
A line of the board's words with its {name} values filled in.
circleMeetsRing(x: number, y: number, radius: number, ring: MapRing): boolean
Whether a circle of this radius at this point touches the land inside a ring.
distanceToRing(x: number, y: number, ring: MapRing): number
How far a point is from a ring's outline, ignoring which side of it the point is on.
DISTRACTOR_SCORES: { readonly neighbor: 100; readonly group: 60; readonly proximity: 40; }
What makes one place on a map a convincing wrong answer for another.
Somewhere on the far side of the map is never tempting, so the choices are drawn from the target's own corner of it: land neighbours first, then its group (the continent, or the larger part of the country), then whatever is nearest. It also keeps a "find it" board legible, since the choices are places on one map and clustered ones stay readable. The rule is the same wherever the map is: Japan's prefectures, Brazil's states, the countries of the world.
type DistractorOptions = { /** How many wrong answers. Default 3. */ count?: number; /** * How many of the most tempting places the wrong answers are drawn from. Default `count + 3`: wider than `count`, so * that the same three look-alikes are not offered every time the question comes round. */ pool?: number; /** The stream the draw is made from: the same seed offers the same choices. Default: the best `count`, with…
distractorScore(target: Scorable, candidate: Scorable, diagonal: number): number
How tempting candidate is as a wrong answer for target, from 0 up to 200: 100 for a land neighbour, 60 for the same group, and up to 40 for being close, measured against diagonal, the corner-to-corner length of the map (each map is drawn on a canvas of its own, so a distance only means something relative to it).
findQuestion(map: Pick<ChizuMap, "width" | "height" | "regions">, targetCode: string | number, random: Random, options?: Omit<DistractorOptions, "random">): FindQuestion | null
Makes the question: the target, count of the most tempting wrong answers for it (pickDistractors), and all of them put in a random order. The same seed asks the same question. A map with fewer other places than count offers fewer choices rather than failing.
type FindQuestion = { /** The code of the place asked about. */ target: string; /** The codes to choose from, in an order a seed fixes, the target among them. */ choices: string[]; /** Where in `choices` the target is. */ answerIndex: number; };
A "which one is this?" question: the place asked about, the choices (it among its look-alikes) and where it is in them.
focusBox(map: Frameable, codes: ReadonlyArray<string | number>): MapBox
The window that frames these regions, or the whole map when none are given.
focusRegionFit(map: Frameable, code: string | number | null): { zoom: MapZoom; centre: { x: number; y: number; }; } | null
What choosing one place computes: zoom in to fit it, whatever zoom the reader was already at. null for a code the map does not hold, so a caller can tell "nothing to focus" from "fit the whole thing", which zoomToFit cannot.
HANDLE_CLEARANCE: 2.35
How far apart two handle centres must be, in radii.
Two circles of radius r touch at 2r and overlap below it, so the old rule of 1.9 declared a pair clear while they were already a tenth of a handle into each other - the "minor overlap" that showed up even when the fallback was working. Above 2 by enough to leave a visible gap rather than a tangent.
HANDLE_LAYOUTS: { readonly beside: "beside"; readonly around: "around"; }
How a map places its numbered handles: on each region, or in the space around it.
HANDLE_RADIUS_RATIO: 0.045
A handle's radius as a share of the framed width, so it holds up at any zoom.
HANDLE_SLOTS: readonly (readonly [number, number])[]
Positions a handle will try, in order, measured in radii from the centroid.
Straight above is the ordinary answer and straight below was the whole of the fallback, which is one position more than none: it was added for two small provinces whose handles landed in the same few pixels, and it fixed exactly that pair. A third crowded region found both taken and went back on top of the first.
Every vertical slot is tried before any sideways one, which is not about looks. The handles are numbered west to east so they read as a row, and a handle shoved sideways can cross the one it was supposed to follow - moving it up or down instead keeps the numbers in the order the player reads them. Sideways is still there for the cluster that runs out of column.
type HandleBounds = { x: number; y: number; width: number; height: number };
A box the handles should stay inside, so none is cropped at the frame edge.
type HandleLayout = (typeof HANDLE_LAYOUTS)[keyof typeof HANDLE_LAYOUTS];
handleRadius(box: { width: number; }, scale: number): number
A handle's radius in the map's own units, for a window this wide and a share of the usual size.
type HandleSpot = { /** The region's own centre, where the stem starts. */ x: number; y: number; /** Where the circle and its number sit. */ hx: number; hy: number; };
insetFor(map: Pick<ChizuMap, "insets">, code: string | number): ChizuInset | null
The inset a region is drawn in, or null when it is drawn where it is.
insetTransform(bbox: readonly [number, number, number, number], box: MapBox, magnify?: boolean): InsetTransform
What to do to a region's own geometry to seat it in its box: shrink it to fit if it is too big, centre it in the frame, and leave it at its own size unless the box says it may be magnified.
type InsetTransform = { scale: number; x: number; y: number };
What to do to a region's own geometry to seat it in its box: a scale, and a move.
insetTransformAttribute(transform: InsetTransform): string
The SVG transform attribute the move is written as.
isMapZoom(value: number): value is MapZoom
Whether a number is one of the zoom steps.
landAnchor(rings: readonly MapRing[]): [number, number] | null
A point on the region's own land, for a leader to end at.
region.map.centroid is the average of a whole outline, so it falls in open water whenever a place is a chain or a crescent: measured on 2026-09-18, 15 of the world's 173 countries - Indonesia, Japan, Malaysia, the Philippines, Vietnam, Israel, France, Croatia among them - and a leader drawn to it ends in the sea between the islands it is naming.
The biggest ring is the region as anyone would point at it, and inside it the chosen point is the one furthest from its coast: the pole of inaccessibility, found by quartering the ring's box and keeping the best square, which is a few hundred steps and exact enough for a circle's leader. A ring so thin that no sample lands inside it - a sandbar - falls back to the ring's own middle.
layoutCallouts(map: Pick<ChizuMap, "regions" | "insets" | "width" | "height" | "wraps">, request: CalloutRequest): CalloutSpot[]
Numbered circles in the open space around and between the regions of a map, each with a leader line to its region.
A circle sits in the water and not on the land, no two leaders cross, and a leader crosses as little land as it can (see placeCallouts for how the three are weighed). The result is geometry only: draw it with drawChizu's callouts option or with anything else that draws circles and lines. It is the same arrangement for the same map, window and list, every time, with no randomness in it that is not seeded.
On a map that wraps, a window that overhangs the cut shows land twice, so both copies keep the circles out, and a region is numbered where the window shows it.
mainlandBounds(rings: readonly MapRing[], reach?: number): [number, number, number, number] | null
The box around a region as anyone would picture it: its largest outline, and whatever else is close enough to belong in the same glance.
A region's own bounds are its whole reach, which is the wrong frame for a picture of it. Kagoshima's run from the foot of Kyushu to the Amami islands, three hundred kilometres south, so the prefecture drew at a quarter of the size it could with two specks under it.
reach is how far a ring may sit from the mainland and still be framed with it, as a share of the mainland's own longer side - so a near island comes along and a far one is left out of the picture, which is where the map itself now draws it anyway.
MAP_ZOOM_LEVELS: readonly [1, 2, 3, 4, 5]
How far in the map is drawn, as steps rather than a free zoom. A free zoom would offer a hundred framings of which a few are useful: the steps are those, plus the whole map to come back to. Five, because at three a country on the world is a dozen pixels across and the small ones of Europe and the Caribbean stay unpickable.
type MapBox = { x: number; y: number; width: number; height: number };
A window on a map, in the map's own units: what an SVG viewBox says.
mapDiagonal(map: Pick<ChizuMap, "width" | "height">): number
The corner-to-corner length of a map's canvas, which proximity is measured against.
type MapOutline = { rings: MapRing[]; /** Where a leader to this region should end: a point on the land, as drawn. */ anchor: [number, number] | null; };
A map's outlines, drawn where they are drawn: a region in a box carried into it, outlying islands in a box carried into theirs, everything else where the projection put it.
Kept against the map itself, so a page that redraws on every pick parses its paths once. Canada's are 76,000 points and cost 16ms to walk, a third of a frame, on a keystroke.
mapOutlines(map: Pick<ChizuMap, "regions" | "insets">): MapOutline[]
The outlines of every region of a map, in the map's order, with a point on each one's land.
type MapPiece = { d: string; transform: InsetTransform | null };
How one region is drawn: usually in one piece, where it is.
A region in a box of its own is that one piece moved into the box. A region with outlying islands in a box - a prefecture whose far islands belong down their own chain rather than in the sea off the mainland - is two: the region where it is, and the islands in their frame. Both carry the same region, so whichever piece is clicked is the same place.
mapRegionPieces(region: { path: string; bbox: readonly [number, number, number, number]; }, inset: ChizuInset | null): MapPiece[]
type MapRing = { /** The ring as the path draws it, so a part of a region can be redrawn on its own. */ d: string; points: readonly number[]; minX: number; minY: number; maxX: number; maxY: number; };
One closed outline: a region's mainland, or one of its islands.
mapWrapsAround(map: Pick<ChizuMap, "wraps">): boolean
Whether this map's east and west edges are the same edge.
type MapZoom = (typeof MAP_ZOOM_LEVELS)[number];
type MatchOptions = { /** * A trailing character a pasted name may leave off, as a pattern. A prefecture is written 東京都 on the map and 東京 * in a lesson plan: pass `/[県府都道]$/u`. Default: none. */ optionalEnding?: RegExp; };
A set of regions made from a list of place names somebody pasted.
A teacher with eight prefecture names in a lesson plan should not have to find each one on the map. Matching is deliberately forgiving in the ways a paste is messy and strict everywhere else: case and surrounding space never matter, a name may be the English, the Japanese, the short Japanese or the reading, and a line that matches nothing is reported rather than dropped. It is never fuzzy: "Tokyo" must not quietly become Tochigi.
nameOf(region: Pick<ChizuRegion, "name" | "nameJa" | "nameShortJa">, language: ChizuLanguage): string
What a region is called in a language: the English name, or in Japanese the everyday short name where there is one (アメリカ), else the full Japanese name, else the English.
nearestWrappedBox(from: MapBox, to: MapBox, width: number): MapBox
The copy of a window nearest another one. Travelling from Japan to Hawaii is a short hop east across the date line, and their stored coordinates are at opposite ends of the canvas, so easing between the two as they are stored sweeps the whole earth the wrong way. Sliding the destination onto the nearest copy makes the journey the short one.
orderByPosition<T>(options: readonly T[], centreOf: (option: T): readonly [number, number] | null | undefined) => T[]
The choices in the order they run across the map, west to east.
Handles numbered by the option's place in a shuffled list read 3 2 4 1 from left to right, and the number keys pointed at nothing you could see. Numbering by position makes the handles a row to read and makes 1 mean the leftmost one.
It gives nothing away. Where a place sits is the entire question being asked, so ordering the choices by it tells the player only what the map already shows them. Ties break north to south, and anything with no centre keeps its order at the end rather than disappearing. centreOf says where an option is: (option) => region.centroid.
parseMapRings(path: string, transform?: InsetTransform | null): MapRing[]
The rings of one region's path, in the order it draws them, each with its own box.
type PastedPlaces = { /** The region codes found, in the order they were written, each once. */ codes: string[]; /** The lines that matched nothing, said back rather than dropped. */ missing: string[]; };
pickDistractors(map: Pick<ChizuMap, "width" | "height" | "regions">, targetCode: string | number, options?: DistractorOptions): string[]
The wrong answers to "which one is this?": the codes of count places, drawn from the most tempting ones for the target, never the target itself. With random the pool is shuffled and cut, so the same target is asked with different company; without it the answer is simply the best count, the same every time.
placeCallouts<T>(entries: ReadonlyArray<{ item: T; centroid: readonly [number, number]; }>, radius: number, box: MapBox, keepOut?: CalloutKeepOut, land?: readonly CalloutLand[], options?: { polish?: boolean; }): Array<HandleSpot & { item: T; }>
Places every circle in the open space around the map.
The hardest region chooses first: the one whose nearest room is furthest away has nothing else to take, and a coastal region that picked early would leave an inland one nowhere to go. Then the arrangement is walked downhill - each circle offered a better place, each crossing pair offered each other's - until nothing improves.
placeHandles<T>(entries: ReadonlyArray<{ item: T; centroid: readonly [number, number]; }>, radius: number, bounds?: HandleBounds): Array<HandleSpot & { item: T; }>
Places every handle clear of the ones before it.
Greedy and order-dependent by design: the caller has already sorted the marks into the order they are numbered, and a handle that has found a home does not move to make room for a later one. With a handful of choices on a board that is enough, and it keeps the placement stable as tones change mid-question.
placesFromText(text: string, regions: readonly Named[], options?: MatchOptions): PastedPlaces
The regions a paste names. Order is the paste's, because somebody who wrote them in lesson order meant that order. A name written twice adds the place once.
pointInRing(x: number, y: number, ring: MapRing): boolean
Whether a point is inside a ring, by the crossing rule.
POLISH_CIRCLE_CLEAR_RADII: 1.2
How far a leader keeps from the middle of another number's circle, in radii: past the ring, with air.
POLISH_CLOSE_RADII: 1
How close two leaders may come, in radii.
projectPoint(map: Pick<ChizuMap, "projection">, lon: number, lat: number): [number, number] | null
Where a longitude and latitude (in degrees) fall on the map's canvas, as [x, y]; null for a map whose canvas cannot be reversed from here. On the world a point just east of the seam is at the far left of the canvas and one just west of it at the far right, and the canvas repeats either side, so a page that pans past the edge adds or takes away width (wrapAcross).
type Random = () => number;
A number in [0, 1), like Math.random, from a stream a seed fixes.
regionBox(map: Frameable, code: string | number, aspect?: number): MapBox
A window tight on one region, for drawing it on its own.
Two things decide it, and getting either wrong draws a country instead of a shape. The room around the region is a fraction of *that side* of it, not of its longest side. And the window takes the shape of the frame it will be drawn in (aspect, how much wider than tall), because an SVG scaled to fit keeps its window's proportions and fills the rest of the frame with whatever is next to it. With both right the region fills the side that limits it, and the room left over falls on the other side, where it does what the room was for: the neighbours show in outline, so it is a place rather than a blob. Framed where it is drawn, so a region in an inset is framed there.
regionCentre(map: Frameable, code: string | number | null): { x: number; y: number; } | null
The middle of a region, for zooming to whatever somebody just chose: where it is *drawn*, not where it is. Okinawa is drawn in a box off the south-west of the mainland and Alaska in one below the lower forty-eight, so centring on their true positions would take the reader to open sea.
type Scorable = { /** A code, text or a number: a map of your own places may key them by either, and neighbours are compared as text. */ code: string | number; group: string; centroid: readonly [number, number]; neighbors: ReadonlyArray<string | number>; };
The parts of a place this scoring reads, so a map of your own places needs no more than these.
seaAround(x: number, y: number, reach: number, ring: MapRing): number
How much water is around a point before this ring's land, never looking further than reach - a place far from every coast only has to be known to be far, and walking six thousand points to find out how far is the bill this runs up on every draw.
seededRandom(seed: number): Random
A stream of numbers in [0, 1) fixed by a seed. The seed is read as an unsigned 32-bit integer.
segmentCrossesBox(a: readonly [number, number], b: readonly [number, number], box: CalloutObstacle): boolean
Whether a straight leader from a to b passes through this box.
segmentMeetsRing(ax: number, ay: number, bx: number, by: number, ring: MapRing): boolean
Whether a straight line from one point to another passes over the land inside a ring.
segmentsCross(a: readonly [number, number], b: readonly [number, number], c: readonly [number, number], d: readonly [number, number]): boolean
Whether two leaders cross. Touching at an end is not crossing.
segmentsGap(a: readonly [number, number], b: readonly [number, number], c: readonly [number, number], d: readonly [number, number]): number
How close two leaders come, where they do not cross: the nearest any point of one gets to the other. Zero when they cross.
shapeGlyphBox(bbox: readonly [number, number, number, number]): MapBox
A square window on one region's own outline, for drawing it as an icon: every shape gets the same box and fills it, so a small region is as legible in a list as a large one.
shiftedOutlines(outlines: readonly MapOutline[], by: number): MapOutline[]
shuffled<T>(items: readonly T[], random: Random): T[]
A copy of the list in a random order (Fisher–Yates, from the end); the list given is left alone.
splitPastedPlaces(text: string): string[]
A paste split into its lines, on the separators a pasted list actually uses.
stepMapZoom(zoom: MapZoom, by: 1 | -1): MapZoom
One step in or out, clamped: the ends stay put rather than wrapping.
unprojectPoint(map: Pick<ChizuMap, "projection">, x: number, y: number): [number, number] | null
The longitude and latitude (in degrees) of a point on the map's canvas, as [lon, lat]; null where projectPoint is.
VERSION: "1.0.0"
The package's version, which a test holds equal to package.json's.
wholeMapBox(map: Pick<ChizuMap, "width" | "height">): MapBox
The whole canvas as a window.
wrapAcross(x: number, width: number): number
A coordinate brought back onto the canvas, for a pan that has gone round.
wrapIntoBox(x: number, box: MapBox, width: number): number | null
The copy of a point that this window shows, or null when it shows none. What a number and its leader are placed against: a country the window does not hold gets neither, and on a wrapped map "does the window hold it" is a question about every copy, not only the one the projection drew.
wrapOffsets(box: MapBox, width: number): number[]
The copies of the canvas this window sees, as x offsets in map units: [0] while the window is wholly on the canvas, which is most of the time. The second copy is only drawn once the reader has panned across the cut.
zoomBox(map: Frameable, zoom: MapZoom, centre: { x: number; y: number; }, clamp?: boolean): MapBox
The window a zoom step and a centre make.
Kept on the map: a centre near a coast would otherwise frame open sea. Clamping the window rather than the centre means a drag that runs off the edge simply stops there, which is what a reader expects of every other map they have used. On a map that wraps, east and west never stop, so only north and south are held. clamp is off for a fit: a region opened from its address asked for that region in the middle, and a clamp that pushes the window back onto the map puts it somewhere else.
zoomToFit(map: Frameable, codes: ReadonlyArray<string | number>): { zoom: MapZoom; centre: { x: number; y: number; }; }
The zoom step and centre that show all of these regions at once: for opening a region from its heading, which wants it filling the view and not the whole map with a few regions lit somewhere in it.
It picks the closest step whose window still holds everything, with the same room around it that a focus box leaves; a set that will not fit at 2× is shown at 1× rather than cut. Measured where the regions are *drawn*, so an inset's region is its box and not its true position out at sea. The room is a share of each side, and a set that cannot fit closer with the margin gets one more try at the bare shape before it is shown at 1×: the margin is what makes a fit comfortable, not what makes it a fit.
@johnmorrisdotca/chizu/drawCHIZU_STYLE ChizuDrawOptions drawChizu
CHIZU_STYLE: "\n.chizu {\n --cz-sea: #cfe3ee; --cz-land: #e9e1c8; --cz-line: #5d5a50; --cz-inset: #7a766a; --cz-ink: #1f2320;\n --cz-selected: #ffd23f; --cz-correct: #9bd6a8; --cz-wrong: #f0a99b; --cz-hint: #b7dcf4; --cz-muted: #d9d5c6; --cz-faint: #f3efe4;\n --cz-callout: #ffffff; --cz-callout-ink: #1f2320; --cz-leader: #3a3d38; --cz-halo: #f7f3e8;\n --cz-font: system-ui, -apple-system, \"Segoe UI\", \"Hiragino San…
THE STYLE a chizu drawing wears: the colours of its land and sea as custom properties, and the one rule that matters for a map pressed with fingers: nothing in the drawing can be selected, dragged or double-tapped.
drawChizu only writes classes, data attributes and a few custom properties; this is what gives them a look. Every colour is a custom property on .chizu (--cz-sea, --cz-land, --cz-line, ...), so a page's own style needs to set only the ones it wants different. The paper follows the page's light or dark. Nothing moves, so there is nothing for reduced motion to still.
A region may be given a tone (drawChizu's tones option): selected, correct, wrong, hint, muted or faint are looked after here, and any other name x is the class cz-tone-x for the page to colour.
type ChizuDrawOptions = { /** The window to show: a part of the map, as `focusBox`, `zoomBox` or `regionBox` frame it. Default: the whole map. */ box?: MapBox; /** The language of the names a screen reader hears, and of the labels. Default `en`. */ language?: ChizuLanguage; /** A tone for each region, by code: `selected`, `correct`, `wrong`, `hint`, `muted`, `faint`, or any name `x`, which is the class `cz-tone-x` f…
What a drawing shows beyond the map itself. Every part is optional: a map alone is its land on its sea.
drawChizu(map: ChizuMap, options?: ChizuDrawOptions): string
A map as SVG text: the sea, every region's land with its tone, the frames of its insets, and optionally the names and the numbered callouts. A string, so it works on a server, in a build step, in an email or in innerHTML; the look comes from CHIZU_STYLE (set style: true to carry it inside).
Each region is a <g class="cz-region" data-code="…"> holding a <path class="cz-land"> for each piece (an inset's region is one piece moved into its box). On a map that wraps, a window that overhangs the cut draws the land again on the other side.
@johnmorrisdotca/chizu/mountCHIZU_MAP_STYLE ChizuMount ChizuMountOptions ChizuView ensureChizuMapStyle mountChizu
CHIZU_MAP_STYLE: "\n.chizu {\n --cz-sea: #cfe3ee; --cz-land: #e9e1c8; --cz-line: #5d5a50; --cz-inset: #7a766a; --cz-ink: #1f2320;\n --cz-selected: #ffd23f; --cz-correct: #9bd6a8; --cz-wrong: #f0a99b; --cz-hint: #b7dcf4; --cz-muted: #d9d5c6; --cz-faint: #f3efe4;\n --cz-callout: #ffffff; --cz-callout-ink: #1f2320; --cz-leader: #3a3d38; --cz-halo: #f7f3e8;\n --cz-font: system-ui, -apple-system, \"Segoe UI\", \"Hiragino…
THE STYLE a mounted map wears, beside the drawing's own (CHIZU_STYLE): the frame the map sits in, the zoom buttons over its corner and the line a screen reader is told what was chosen on. Custom properties on .chizu-map (--czm-ink, --czm-surface, --czm-rule, --czm-accent) with the page's light and dark.
type ChizuMount = { /** Change any of the options but `map`, and redraw. */ set(patch: Partial<Omit<ChizuMountOptions, "map">>): void; /** Show another map, from its whole. */ setMap(map: ChizuMap): void; /** Choose a region (or none), zooming to it unless `fit` is off. */ select(code: string | null): void; /** Look at these regions, as close as will hold them all. */ show(codes: readonly string[]): void; /** One st…
type ChizuMountOptions = { /** The map to show: `WORLD`, a country's own, or a country's regions. */ map: ChizuMap; /** The language of the names and of the buttons. Default: the page's `lang`, or English. */ language?: ChizuLanguage; /** The zoom step to start at (1 to 5). Default 1. */ zoom?: MapZoom; /** Tones by code (`drawChizu`'s `tones`). The chosen region is `selected` unless it is given a tone here. */ tone…
What a mounted map is told. Everything but map may be changed later with set.
type ChizuView = { zoom: MapZoom; centre: { x: number; y: number }; box: MapBox };
Where a mounted map is looking.
ensureChizuMapStyle(host: Element): void
Put the style in the page once: in the document's head, or in the shadow root the host is in.
mountChizu(host: HTMLElement, initial: ChizuMountOptions): ChizuMount
Puts a map in an element, to look at by touch, mouse and keyboard: drag to move it, the buttons, the wheel or a pinch to zoom in five steps, a press to choose a region, the arrow keys and plus and minus. The world wraps: it pans east and west without stopping. Every part of it is the package's own drawing (drawChizu) in the page's own DOM, with no shadow DOM and nothing the page's style cannot reach.
@johnmorrisdotca/chizu/namesCHIZU_COUNTRIES CHIZU_SOURCE countriesFromText countryByCode
CHIZU_COUNTRIES: readonly ChizuCountry[]
CHIZU_SOURCE: { name: string; version: string; repository: string; tag: string; retrieved: string; licence: string; files: string[]; }
Where the data came from.
countriesFromText(text: string): { countries: ChizuCountry[]; missing: string[]; }
The countries a pasted list names, by English name, Japanese name, short Japanese name, reading or code, in the order written (placesFromText): countriesFromText("Japan, フランス\nBrazil").
countryByCode(code: string): ChizuCountry | undefined
The country with this code (upper or lower case), or undefined.
@johnmorrisdotca/chizu/loadCOUNTRY_CODES DIVISIONS_CODES loadCountry loadDivisions
COUNTRY_CODES: readonly string[]
Every country that has a map of its own, by code, upper or lower case.
DIVISIONS_CODES: readonly string[]
Every country that has a map of its own regions (provinces, states, départements), by code.
loadCountry(code: string): Promise<ChizuMap | null>
One country alone, drawn from Natural Earth's 1:50m countries (the small ones from 1:10m); null for a code with no map.
loadDivisions(code: string): Promise<ChizuMap | null>
The regions of one country, from Natural Earth's 1:10m states and provinces; null for a country that has none.
@johnmorrisdotca/chizu/worlddefault: ChizuMap
WORLD: ChizuMap
この日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。