diff --git a/examples/world.ts b/examples/world.ts
new file mode 100644
index 0000000..9c23511
--- /dev/null
+++ b/examples/world.ts
@@ -0,0 +1,77 @@
+/**
+ * A clickable world map: `bun examples/world.ts`
+ *
+ * Click a country to select it, hover to see what is under the cursor, and
+ * press z to zoom to the selection or r to go back to the whole globe.
+ *
+ * The map is the canvas drawing polylines in degrees, which is all a map is
+ * once the canvas works in the caller's own coordinates. The clicking is the
+ * other direction: the cell under the cursor is turned back into a longitude
+ * and latitude and tested against the outlines, so the answer is the country
+ * actually there rather than whichever bounding box happened to be first.
+ */
+import { createApp, countryBounds, findCountry, WORLD_X, WORLD_Y } from "@profullstack/hqtui";
+import type { CountryOutline, Bounds } from "@profullstack/hqtui";
+
+let selected: CountryOutline | undefined;
+let hovered: CountryOutline | undefined;
+let window: { x: Bounds; y: Bounds } = { x: WORLD_X, y: WORLD_Y };
+
+const app = await createApp({ title: "hqtui · world" });
+
+app.render(({ ui, theme }) => {
+ const shown = hovered ?? selected;
+ const zoomed = window.x !== WORLD_X;
+
+ ui.panel(
+ {
+ title: "World",
+ subtitle: shown ? shown.name : "click a country",
+ footer: zoomed ? "z zoom · r reset · q quit" : "z zoom to selection · q quit",
+ },
+ (p) => {
+ p.worldMap({
+ ...window,
+ color: theme.border,
+ highlight: [selected?.iso || selected?.name || "", hovered?.iso || hovered?.name || ""],
+ highlightColor: theme.accent,
+ onSelect: (country) => {
+ selected = country;
+ app.invalidate();
+ },
+ onHover: (country) => {
+ if (country?.name !== hovered?.name) {
+ hovered = country;
+ app.invalidate();
+ }
+ },
+ });
+
+ p.row({ size: 1, gap: 2 }, (row) => {
+ row.keyValues(
+ [
+ { label: "Selected", value: selected?.name ?? "—" },
+ { label: "ISO", value: selected?.iso || "—" },
+ ],
+ { spread: false },
+ );
+ });
+ },
+ );
+});
+
+app.on("key", (key) => {
+ if (key.name === "z" && selected) {
+ window = countryBounds(selected);
+ app.invalidate();
+ }
+ if (key.name === "r") {
+ window = { x: WORLD_X, y: WORLD_Y };
+ app.invalidate();
+ }
+});
+
+// Somewhere to start from, so the first frame is not an empty selection.
+selected = findCountry("Japan");
+
+await app.start();
diff --git a/packages/hqtui/scripts/generate-world.ts b/packages/hqtui/scripts/generate-world.ts
new file mode 100644
index 0000000..cbceef2
--- /dev/null
+++ b/packages/hqtui/scripts/generate-world.ts
@@ -0,0 +1,280 @@
+/**
+ * Generates `src/graphics/world-data.ts` from Natural Earth's country polygons.
+ *
+ * bun packages/hqtui/scripts/generate-world.ts
+ *
+ * The source is Natural Earth 1:110m Admin 0 countries, which is public domain
+ * . It is fetched rather
+ * than committed: the raw file is 800KB of GeoJSON and this only needs running
+ * when the borders change, which is roughly never.
+ *
+ * The output is committed. Regenerating it should be a no-op unless the
+ * tolerance below changes.
+ */
+import { writeFileSync } from "node:fs";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+
+const SOURCE =
+ "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson";
+
+/**
+ * Douglas-Peucker tolerance, in degrees.
+ *
+ * A world map across an eighty-column terminal is about 160 Braille pixels
+ * wide, so one pixel is a bit over two degrees. At one degree the simplified
+ * outline is already finer than anything a terminal can show, and it keeps 171
+ * of the 177 countries; the six it drops are island states smaller than a pixel,
+ * which could be neither seen nor clicked.
+ */
+const TOLERANCE = 1.0;
+
+/** A country whose bounding box is smaller than this cannot be drawn or hit. */
+const MIN_SPAN = 1.5;
+
+type Point = [number, number];
+
+function perpendicular(p: Point, a: Point, b: Point): number {
+ const [px, py] = p;
+ const [ax, ay] = a;
+ const [bx, by] = b;
+ const dx = bx - ax;
+ const dy = by - ay;
+ if (dx === 0 && dy === 0) return Math.hypot(px - ax, py - ay);
+ const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy)));
+ return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
+}
+
+/** Douglas-Peucker, iterative so a long coastline cannot blow the stack. */
+function simplify(points: Point[], tolerance: number): Point[] {
+ if (points.length < 3) return points;
+ const keep = new Array(points.length).fill(false);
+ keep[0] = true;
+ keep[points.length - 1] = true;
+ const stack: [number, number][] = [[0, points.length - 1]];
+ while (stack.length > 0) {
+ const [lo, hi] = stack.pop() as [number, number];
+ let worst = 0;
+ let at = -1;
+ for (let i = lo + 1; i < hi; i++) {
+ const d = perpendicular(points[i], points[lo], points[hi]);
+ if (d > worst) {
+ worst = d;
+ at = i;
+ }
+ }
+ if (worst > tolerance && at !== -1) {
+ keep[at] = true;
+ stack.push([lo, at], [at, hi]);
+ }
+ }
+ return points.filter((_, i) => keep[i]);
+}
+
+interface Feature {
+ properties: Record;
+ geometry: { type: string; coordinates: number[][][] | number[][][][] };
+}
+
+const response = await fetch(SOURCE);
+if (!response.ok) throw new Error(`${SOURCE} responded ${response.status}`);
+const collection = (await response.json()) as { features: Feature[] };
+
+const countries: { name: string; iso: string; rings: Point[][] }[] = [];
+for (const feature of collection.features) {
+ const props = feature.properties;
+ const name = String(props.NAME ?? props.NAME_LONG ?? "");
+ const iso = String(props.ISO_A2_EH ?? props.ISO_A2 ?? "");
+ if (!name) continue;
+
+ const geometry = feature.geometry;
+ const polygons = (geometry.type === "MultiPolygon"
+ ? geometry.coordinates
+ : [geometry.coordinates]) as number[][][][];
+
+ const rings: Point[][] = [];
+ for (const polygon of polygons) {
+ // Only the outer ring. A lake inside a country is not a border at this
+ // resolution, and carrying the holes doubles the data for nothing.
+ const ring = polygon[0].map(([x, y]) => [x, y] as Point);
+ const xs = ring.map((p) => p[0]);
+ const ys = ring.map((p) => p[1]);
+ if (Math.max(...xs) - Math.min(...xs) < MIN_SPAN &&
+ Math.max(...ys) - Math.min(...ys) < MIN_SPAN) continue;
+ const cut = simplify(ring, TOLERANCE);
+ if (cut.length >= 3) {
+ rings.push(cut.map(([x, y]) => [Math.round(x * 10) / 10, Math.round(y * 10) / 10] as Point));
+ }
+ }
+ if (rings.length > 0) countries.push({ name, iso, rings });
+}
+
+countries.sort((a, b) => a.name.localeCompare(b.name));
+
+const points = countries.reduce((n, c) => n + c.rings.reduce((m, r) => m + r.length, 0), 0);
+const here = dirname(fileURLToPath(import.meta.url));
+const repo = join(here, "..", "..", "..");
+
+/** The same provenance note at the top of every generated file. */
+function banner(comment: string): string {
+ const line = (text: string) => (text ? `${comment} ${text}` : comment.trimEnd());
+ return [
+ line("Country outlines, flattened for a terminal."),
+ line(""),
+ line("Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's"),
+ line("1:110m Admin 0 countries, which is public domain. Do not edit by hand."),
+ line(""),
+ line("Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --"),
+ line(`rather than a list of pairs, because at ${points} points the nested form`),
+ line("costs a container per coordinate for no gain. A country has more than one"),
+ line("ring when it is more than one landmass."),
+ line(""),
+ line(`${countries.length} countries, ${points} points, simplified at ${TOLERANCE} degrees.`),
+ ].join("\n");
+}
+
+const flat = (ring: Point[]) => ring.flatMap(([x, y]) => [x, y]);
+/** A float literal every language reads the same way, including whole numbers. */
+const real = (v: number) => (Number.isInteger(v) ? `${v}.0` : `${v}`);
+const quoted = (v: string) => JSON.stringify(v);
+
+const write = (relative: string, text: string) => {
+ const target = join(repo, relative);
+ writeFileSync(target, text);
+ console.log(` ${relative}`);
+};
+
+// ------------------------------------------------------------------ TypeScript
+write(
+ "packages/hqtui/src/graphics/world-data.ts",
+ `/**\n${banner(" *")}\n */\n\n` +
+ `export interface CountryOutline {\n name: string;\n` +
+ ` /** ISO 3166-1 alpha-2, where Natural Earth has one. */\n iso: string;\n` +
+ ` /** Longitude and latitude, interleaved. */\n rings: number[][];\n}\n\n` +
+ `export const WORLD_COUNTRIES: readonly CountryOutline[] = [\n` +
+ countries
+ .map((c) =>
+ ` {\n name: ${quoted(c.name)},\n iso: ${quoted(c.iso)},\n rings: [\n` +
+ c.rings.map((r) => ` [${flat(r).join(",")}],`).join("\n") +
+ `\n ],\n },`)
+ .join("\n") +
+ `\n];\n`,
+);
+
+// ------------------------------------------------------------------------ Rust
+write(
+ "ports/rust/src/graphics/world_data.rs",
+ `${banner("//!")}\n\npub struct CountryOutline {\n pub name: &'static str,\n` +
+ ` /// ISO 3166-1 alpha-2, where Natural Earth has one.\n pub iso: &'static str,\n` +
+ ` /// Longitude and latitude, interleaved.\n pub rings: &'static [&'static [f64]],\n}\n\n` +
+ `pub static WORLD_COUNTRIES: &[CountryOutline] = &[\n` +
+ countries
+ .map((c) =>
+ ` CountryOutline {\n name: ${quoted(c.name)},\n iso: ${quoted(c.iso)},\n` +
+ ` rings: &[\n` +
+ c.rings.map((r) => ` &[${flat(r).map(real).join(",")}],`).join("\n") +
+ `\n ],\n },`)
+ .join("\n") +
+ `\n];\n`,
+);
+
+// -------------------------------------------------------------------------- Go
+write(
+ "ports/go/world_data.go",
+ `package hqtui\n\n${banner("//")}\n\ntype CountryOutline struct {\n\tName string\n` +
+ `\t// ISO 3166-1 alpha-2, where Natural Earth has one.\n\tISO string\n` +
+ `\t// Rings are longitude and latitude, interleaved.\n\tRings [][]float64\n}\n\n` +
+ `var WorldCountries = []CountryOutline{\n` +
+ countries
+ .map((c) =>
+ `\t{\n\t\tName: ${quoted(c.name)},\n\t\tISO: ${quoted(c.iso)},\n\t\tRings: [][]float64{\n` +
+ c.rings.map((r) => `\t\t\t{${flat(r).join(",")}},`).join("\n") +
+ `\n\t\t},\n\t},`)
+ .join("\n") +
+ `\n}\n`,
+);
+
+// ---------------------------------------------------------------------- Python
+write(
+ "ports/python/hqtui/graphics/world_data.py",
+ `"""\n${banner("")}\n"""\n\nfrom __future__ import annotations\n\n` +
+ `from dataclasses import dataclass\n\n\n` +
+ `@dataclass(frozen=True, slots=True)\nclass CountryOutline:\n name: str\n` +
+ ` #: ISO 3166-1 alpha-2, where Natural Earth has one.\n iso: str\n` +
+ ` #: Longitude and latitude, interleaved.\n rings: tuple[tuple[float, ...], ...]\n\n\n` +
+ `WORLD_COUNTRIES: tuple[CountryOutline, ...] = (\n` +
+ countries
+ .map((c) =>
+ ` CountryOutline(\n ${quoted(c.name)},\n ${quoted(c.iso)},\n (\n` +
+ c.rings.map((r) => ` (${flat(r).join(",")}),`).join("\n") +
+ `\n ),\n ),`)
+ .join("\n") +
+ `\n)\n`,
+);
+
+// ------------------------------------------------------------------------- Zig
+write(
+ "ports/zig/src/graphics/world_data.zig",
+ `${banner("//!")}\n\npub const CountryOutline = struct {\n name: []const u8,\n` +
+ ` /// ISO 3166-1 alpha-2, where Natural Earth has one.\n iso: []const u8,\n` +
+ ` /// Longitude and latitude, interleaved.\n rings: []const []const f64,\n};\n\n` +
+ `pub const WORLD_COUNTRIES = [_]CountryOutline{\n` +
+ countries
+ .map((c) =>
+ ` .{\n .name = ${quoted(c.name)},\n .iso = ${quoted(c.iso)},\n` +
+ ` .rings = &.{\n` +
+ c.rings.map((r) => ` &.{${flat(r).map(real).join(",")}},`).join("\n") +
+ `\n },\n },`)
+ .join("\n") +
+ `\n};\n`,
+);
+
+// ------------------------------------------------------------------------- C++
+// A flat coordinate pool with index tables, rather than nested initialiser
+// lists: the nested form is the same data but minutes of compile time, and this
+// is the one language where that bites.
+const pool: number[] = [];
+const ringSpans: [number, number][] = [];
+const countrySpans: [number, number][] = [];
+for (const c of countries) {
+ const first = ringSpans.length;
+ for (const ring of c.rings) {
+ const at = pool.length;
+ pool.push(...flat(ring));
+ ringSpans.push([at, pool.length - at]);
+ }
+ countrySpans.push([first, ringSpans.length - first]);
+}
+const chunk = (values: string[], per: number) => {
+ const lines: string[] = [];
+ for (let i = 0; i < values.length; i += per) lines.push(` ${values.slice(i, i + per).join(",")},`);
+ return lines.join("\n");
+};
+write(
+ "ports/cpp/src/world_data.cpp",
+ `${banner("///")}\n#include \n\nnamespace hqtui {\nnamespace {\n\n` +
+ `const double POOL[] = {\n${chunk(pool.map(real), 12)}\n};\n\n` +
+ `struct Span {\n int at, length;\n};\n\n` +
+ `const Span RINGS[] = {\n${chunk(ringSpans.map(([a, n]) => `{${a},${n}}`), 8)}\n};\n\n` +
+ `const Span COUNTRIES[] = {\n${chunk(countrySpans.map(([a, n]) => `{${a},${n}}`), 8)}\n};\n\n` +
+ `const char *const NAMES[] = {\n${chunk(countries.map((c) => quoted(c.name)), 4)}\n};\n\n` +
+ `const char *const ISO[] = {\n${chunk(countries.map((c) => quoted(c.iso)), 12)}\n};\n\n` +
+ `} // namespace\n\n` +
+ `/// Built once, on first use: the pool above is plain static data, and this\n` +
+ `/// turns it into the shape the rest of the code wants without a static\n` +
+ `/// initialiser that has to run before main.\n` +
+ `const std::vector &world_countries() {\n` +
+ ` static const std::vector countries = [] {\n` +
+ ` std::vector out;\n` +
+ ` out.reserve(${countries.length});\n` +
+ ` for (std::size_t i = 0; i < ${countries.length}; i++) {\n` +
+ ` CountryOutline c;\n c.name = NAMES[i];\n c.iso = ISO[i];\n` +
+ ` const Span &cs = COUNTRIES[i];\n` +
+ ` for (int r = 0; r < cs.length; r++) {\n` +
+ ` const Span &rs = RINGS[cs.at + r];\n` +
+ ` c.rings.emplace_back(POOL + rs.at, POOL + rs.at + rs.length);\n` +
+ ` }\n out.push_back(std::move(c));\n }\n return out;\n }();\n` +
+ ` return countries;\n}\n\n} // namespace hqtui\n`,
+);
+
+console.log(`${countries.length} countries, ${points} points`);
diff --git a/packages/hqtui/scripts/world-probe.ts b/packages/hqtui/scripts/world-probe.ts
new file mode 100644
index 0000000..3cb0d4d
--- /dev/null
+++ b/packages/hqtui/scripts/world-probe.ts
@@ -0,0 +1,42 @@
+/**
+ * Prints what the country lookup answers for a fixed set of points.
+ *
+ * The same probe exists for every port, so "the ports agree about the world" is
+ * a diff rather than a hope. The reference output is this one, because the
+ * TypeScript implementation is the reference for everything else too.
+ *
+ * bun packages/hqtui/scripts/world-probe.ts
+ */
+import { WORLD_X, WORLD_Y, countryAt, degreesAt } from "../src/graphics/world.ts";
+import { countryAtCell } from "../src/widgets/world.ts";
+
+const places: [string, number, number][] = [
+ ["Paris", 2.35, 48.86],
+ ["Tokyo", 139.7, 35.7],
+ ["Cairo", 31.2, 30.0],
+ ["Brasilia", -47.9, -15.8],
+ ["Canberra", 149.1, -35.3],
+ ["Denver", -105.0, 39.7],
+ ["Moscow", 37.6, 55.75],
+ ["Delhi", 77.2, 28.6],
+ ["Nairobi", 36.8, -1.3],
+ ["Pacific", -140.0, 0.0],
+ ["Atlantic", -30.0, 0.0],
+ ["SouthernOcean", 80.0, -40.0],
+ ["NorthPacific", -150.0, 40.0],
+];
+for (const [name, lon, lat] of places) {
+ console.log(`${name} ${countryAt(lon, lat)?.name ?? "-"}`);
+}
+
+// The cell path, which has to agree with what the canvas drew.
+for (const [column, row] of [[173, 28], [74, 2], [20, 25], [88, 7]]) {
+ console.log(`cell:${column},${row} ${countryAtCell(column, row, 200, 50)?.name ?? "-"}`);
+}
+
+// And the projection itself, so a drift shows up as a number rather than as a
+// country that happens to still be right.
+for (const [column, row] of [[0, 0], [99, 25], [50, 13]]) {
+ const at = degreesAt(column, row, 100, 26, WORLD_X, WORLD_Y);
+ console.log(`degrees:${column},${row} ${at!.lon.toFixed(4)} ${at!.lat.toFixed(4)}`);
+}
diff --git a/packages/hqtui/src/graphics/index.ts b/packages/hqtui/src/graphics/index.ts
index 3feeadb..c0a8c29 100644
--- a/packages/hqtui/src/graphics/index.ts
+++ b/packages/hqtui/src/graphics/index.ts
@@ -2,4 +2,5 @@ export { BrailleCanvas } from "./braille.ts";
export * from "./blocks.ts";
export * from "./plot.ts";
export * from "./canvas.ts";
+export * from "./world.ts";
export * from "./chart.ts";
diff --git a/packages/hqtui/src/graphics/world-data.ts b/packages/hqtui/src/graphics/world-data.ts
new file mode 100644
index 0000000..d794667
--- /dev/null
+++ b/packages/hqtui/src/graphics/world-data.ts
@@ -0,0 +1,1308 @@
+/**
+ * Country outlines, flattened for a terminal.
+ *
+ * Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's
+ * 1:110m Admin 0 countries, which is public domain. Do not edit by hand.
+ *
+ * Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --
+ * rather than a list of pairs, because at 1982 points the nested form
+ * costs a container per coordinate for no gain. A country has more than one
+ * ring when it is more than one landmass.
+ *
+ * 171 countries, 1982 points, simplified at 1 degrees.
+ */
+
+export interface CountryOutline {
+ name: string;
+ /** ISO 3166-1 alpha-2, where Natural Earth has one. */
+ iso: string;
+ /** Longitude and latitude, interleaved. */
+ rings: number[][];
+}
+
+export const WORLD_COUNTRIES: readonly CountryOutline[] = [
+ {
+ name: "Afghanistan",
+ iso: "AF",
+ rings: [
+ [66.5,37.4,70.8,38.5,71.8,36.7,75.2,37.1,71.3,36.1,69.3,31.9,66.3,29.9,60.9,29.8,61.2,35.7,66.5,37.4],
+ ],
+ },
+ {
+ name: "Albania",
+ iso: "AL",
+ rings: [
+ [21,40.8,19.4,40.3,19.7,42.7,21,40.8],
+ ],
+ },
+ {
+ name: "Algeria",
+ iso: "DZ",
+ rings: [
+ [-8.7,27.4,-8.7,28.8,-1.3,32.3,-1.2,35.7,8.4,36.9,7.5,34.1,9.8,29.4,9.3,26.1,12,23.5,3.2,19.1,-8.7,27.4],
+ ],
+ },
+ {
+ name: "Angola",
+ iso: "AO",
+ rings: [
+ [12.3,-6.1,16.3,-5.9,17.5,-8.1,21.7,-7.3,22.2,-11.1,24,-11.2,24,-12.9,21.9,-12.9,23.2,-17.5,11.7,-17.3,13.7,-11.3,12.3,-6.1],
+ ],
+ },
+ {
+ name: "Antarctica",
+ iso: "AQ",
+ rings: [
+ [-48.7,-78,-43.9,-78.5,-43.3,-80,-54.2,-80.6,-48.7,-78],
+ [-66.3,-80.3,-59.6,-80,-66.3,-80.3],
+ [-73.9,-71.3,-70.3,-68.9,-68.3,-71.4,-75,-72.1,-73.9,-71.3],
+ [-102.3,-71.9,-96.2,-72.5,-102.3,-71.9],
+ [-122.6,-73.7,-118.7,-73.5,-122.6,-73.7],
+ [-127.3,-73.5,-124,-73.9,-127.3,-73.5],
+ [-163.7,-78.6,-159.2,-79.5,-163.7,-78.6],
+ [180,-84.7,180,-90,-180,-90,-179.1,-84.1,-143.1,-85,-153.6,-83.7,-152.9,-82,-156.8,-81.1,-146.4,-80.3,-155.3,-79.1,-158.4,-76.9,-151.3,-77.4,-146.1,-76.5,-146.2,-75.4,-135.2,-74.3,-100.1,-74.9,-103.7,-72.6,-74.9,-73.9,-67.4,-72.5,-67.7,-67.3,-57.8,-63.3,-65.7,-68,-61.8,-70.7,-60.8,-73.7,-70.6,-76.6,-77.2,-76.7,-73.7,-77.9,-78,-79.2,-58.2,-83.2,-28.5,-80.3,-35.6,-79.5,-35.8,-78.3,-17.5,-75.1,-15.4,-73.1,-6.9,-70.9,27.1,-70.5,33.9,-68.5,38.6,-69.8,54.5,-65.8,61.4,-68,68.9,-67.9,69.7,-69.2,67.9,-71.9,69.9,-72.3,73.9,-69.9,88,-66.2,95.8,-67.4,102.8,-65.6,106.2,-66.9,113.6,-65.9,119.8,-67.3,135.1,-65.3,137.5,-67,145.5,-66.9,171.2,-71.7,163.6,-76.2,167,-78.8,161.8,-79.2,159.8,-80.9,169.4,-83.8,180,-84.7],
+ ],
+ },
+ {
+ name: "Argentina",
+ iso: "AR",
+ rings: [
+ [-68.6,-52.6,-65,-54.7,-68.6,-54.9,-68.6,-52.6],
+ [-57.6,-30.2,-58.5,-34.4,-56.8,-36.9,-62.3,-38.8,-62.7,-41,-65.1,-41.1,-63.5,-42.6,-67.3,-45.6,-65.6,-47.2,-69.1,-50.7,-68.1,-52.3,-71.9,-52,-73.4,-49.3,-71.2,-44.8,-72.1,-42.3,-68.4,-24.5,-66.3,-21.8,-62.8,-22,-57.8,-25.2,-58.6,-27.1,-55.7,-27.4,-54.1,-25.5,-53.6,-26.9,-57.6,-30.2],
+ ],
+ },
+ {
+ name: "Armenia",
+ iso: "AM",
+ rings: [
+ [46.5,38.8,43.6,41.1,45.6,40.8,46.5,38.8],
+ ],
+ },
+ {
+ name: "Australia",
+ iso: "AU",
+ rings: [
+ [147.7,-40.8,147.9,-43.2,146,-43.5,144.7,-40.7,147.7,-40.8],
+ [126.1,-32.2,118,-35.1,115,-34.2,113.7,-22.5,120.9,-19.7,125.7,-14.2,129.6,-15,132.4,-11.1,136.5,-11.9,135.5,-15,140.2,-17.7,142.5,-10.7,146.4,-19,150.7,-22.4,153.6,-28.1,150,-37.4,146.3,-39,140.6,-38,138.2,-34.4,136.8,-35.3,137.8,-32.9,136,-34.9,131.3,-31.5,126.1,-32.2],
+ ],
+ },
+ {
+ name: "Austria",
+ iso: "AT",
+ rings: [
+ [17,48.1,14.6,46.4,9.5,47.1,12.9,47.5,13.6,48.9,17,48.1],
+ ],
+ },
+ {
+ name: "Azerbaijan",
+ iso: "AZ",
+ rings: [
+ [46.4,41.9,50.4,40.3,48.9,38.3,45.6,39.9,45,41.2,46.4,41.9],
+ ],
+ },
+ {
+ name: "Bahamas",
+ iso: "BS",
+ rings: [
+ [-78.2,25.2,-77.5,23.8,-78.2,25.2],
+ ],
+ },
+ {
+ name: "Bangladesh",
+ iso: "BD",
+ rings: [
+ [92.7,22,92.4,20.7,91.4,22.8,89,22.1,88.6,26.4,92.4,25,91.2,23.5,92.7,22],
+ ],
+ },
+ {
+ name: "Belarus",
+ iso: "BY",
+ rings: [
+ [28.2,56.2,30.9,55.6,32.7,53.4,31.8,52.1,23.5,51.6,23.5,53.9,28.2,56.2],
+ ],
+ },
+ {
+ name: "Belgium",
+ iso: "BE",
+ rings: [
+ [6.2,50.8,5.7,49.5,2.5,51.1,6.2,50.8],
+ ],
+ },
+ {
+ name: "Belize",
+ iso: "BZ",
+ rings: [
+ [-89.1,17.8,-88.1,18.3,-88.9,15.9,-89.1,17.8],
+ ],
+ },
+ {
+ name: "Benin",
+ iso: "BJ",
+ rings: [
+ [2.7,6.3,0.8,10.5,2.8,12.2,2.7,6.3],
+ ],
+ },
+ {
+ name: "Bhutan",
+ iso: "BT",
+ rings: [
+ [91.7,27.8,88.8,27.1,91.7,27.8],
+ ],
+ },
+ {
+ name: "Bolivia",
+ iso: "BO",
+ rings: [
+ [-69.5,-11,-65.3,-9.8,-65.4,-11.6,-60.5,-13.8,-60.2,-16.3,-58.2,-16.3,-57.9,-20,-61.8,-19.6,-62.7,-22.2,-67.8,-22.9,-69.5,-11],
+ ],
+ },
+ {
+ name: "Bosnia and Herz.",
+ iso: "BA",
+ rings: [
+ [18.6,42.7,16,45.2,19.4,44.9,18.6,42.7],
+ ],
+ },
+ {
+ name: "Botswana",
+ iso: "BW",
+ rings: [
+ [29.4,-22.1,25.7,-25.5,21.6,-26.7,19.9,-24.8,20.9,-18.3,25.3,-17.7,29.4,-22.1],
+ ],
+ },
+ {
+ name: "Brazil",
+ iso: "BR",
+ rings: [
+ [-53.4,-33.8,-53.8,-32,-57.6,-30.2,-53.6,-26.1,-55.8,-22.4,-57.9,-22.1,-58.2,-16.3,-60.2,-16.3,-60.5,-13.8,-65.4,-11.6,-65.3,-9.8,-70.5,-11,-70.5,-9.5,-72.2,-10.1,-74,-7.5,-72.9,-5.3,-69.9,-4.3,-69.8,1.7,-65.5,0.8,-63.4,2.2,-64.8,4.1,-60.7,5.2,-59,1.3,-52.9,2.1,-51.3,4.2,-50.4,-0.1,-44.6,-2.7,-40,-2.9,-35.6,-5.1,-34.7,-7.3,-38.7,-13.1,-40.9,-21.9,-47.6,-24.9,-48.9,-28.7,-53.4,-33.8],
+ ],
+ },
+ {
+ name: "Bulgaria",
+ iso: "BG",
+ rings: [
+ [22.7,44.2,28.6,43.7,28,42,23,41.3,22.7,44.2],
+ ],
+ },
+ {
+ name: "Burkina Faso",
+ iso: "BF",
+ rings: [
+ [-5.4,10.4,-4.3,13.2,-1.1,15,2.2,12.6,0.9,11,-2.9,11,-2.8,9.6,-5.4,10.4],
+ ],
+ },
+ {
+ name: "Burundi",
+ iso: "BI",
+ rings: [
+ [30.5,-2.4,29.3,-4.5,29,-2.8,30.5,-2.4],
+ ],
+ },
+ {
+ name: "Cambodia",
+ iso: "KH",
+ rings: [
+ [102.6,12.2,103,14.2,107.6,13.5,106.2,11,103.5,10.6,102.6,12.2],
+ ],
+ },
+ {
+ name: "Cameroon",
+ iso: "CM",
+ rings: [
+ [14.5,12.9,14.5,4.7,15.9,1.7,9.6,2.3,8.8,5.5,11.7,7,14.5,12.9],
+ ],
+ },
+ {
+ name: "Canada",
+ iso: "CA",
+ rings: [
+ [-122.8,49,-127.4,50.8,-130.5,54.3,-130,55.9,-135.5,59.8,-137.5,58.9,-141,60.3,-141,69.7,-136.5,68.9,-128.1,70.5,-113.5,67.7,-106.1,68.8,-101.5,67.6,-97.7,68.6,-96.1,67.3,-94.2,69.1,-96.5,70.1,-95.2,71.9,-87.4,67.2,-85.5,69.9,-82.6,69.7,-81.4,67.1,-85.8,66.6,-90.7,63.6,-94.7,58.9,-92.3,57.1,-82.3,55.1,-79.9,51.2,-78.6,52.6,-79.8,54.7,-76.5,56.5,-78.5,58.8,-77.3,59.9,-78.1,62.3,-73.8,62.4,-69.6,61.1,-67.6,58.2,-64.6,60.3,-61.8,56.3,-57.3,54.6,-55.7,52.1,-60,50.2,-66.4,50.2,-71.1,46.8,-65.1,49.2,-64.5,46.2,-60.5,47,-59.8,45.9,-65.4,43.5,-66.2,44.5,-64.4,45.3,-67.1,45.1,-69.2,47.4,-71.5,45,-82.4,41.7,-82.6,45.3,-88.4,48.3,-122.8,49],
+ [-84,62.5,-81.9,62.9,-84,62.5],
+ [-79.8,72.8,-80.8,73.7,-76.3,72.8,-79.8,72.8],
+ [-93.6,75,-96.8,74.9,-93.6,75],
+ [-93.8,77.5,-96.4,77.8,-93.8,77.5],
+ [-96.8,78.8,-95.6,78.4,-98.6,78.9,-96.8,78.8],
+ [-88.2,74.4,-97.1,76.8,-79.8,74.9,-88.2,74.4],
+ [-111.3,78.2,-109.9,78,-113.5,77.7,-111.3,78.2],
+ [-111,78.8,-109.7,78.6,-112.5,78.4,-111,78.8],
+ [-55.6,51.3,-56.8,49.8,-53.5,49.2,-53.1,46.7,-59.3,47.6,-55.6,51.3],
+ [-83.9,65.1,-80.1,63.7,-87.2,63.5,-85.9,65.7,-83.9,65.1],
+ [-78.8,72.4,-68.8,70.5,-67,69.2,-68.8,68.7,-61.9,66.9,-63.9,65,-68,66.3,-64.7,63.4,-68.8,63.7,-66.2,61.9,-68.9,62.3,-78.6,64.6,-74,65.5,-73.3,68.1,-79,70.2,-88.7,70.4,-90.2,72.2,-85.8,73.8,-85.8,72.5,-82.3,73.8,-78.8,72.4],
+ [-94.5,74.1,-90.5,73.9,-95.4,72.1,-96,73.4,-94.5,74.1],
+ [-122.9,76.1,-116.2,77.6,-122.9,76.1],
+ [-132.7,54,-131.2,52.2,-132.7,54],
+ [-105.5,79.3,-99.7,77.9,-105.5,79.3],
+ [-123.5,48.5,-128.4,50.8,-123.5,48.5],
+ [-121.5,74.4,-115.5,73.5,-123.1,70.9,-125.9,71.9,-123.9,73.7,-124.9,74.3,-121.5,74.4],
+ [-107.8,75.8,-105.7,75.5,-117.7,75.2,-115.4,76.5,-107.8,75.8],
+ [-106.5,73.1,-101.1,69.6,-113.3,68.5,-117.3,70,-112.4,70.4,-119.4,71.6,-115.2,73.3,-108.2,71.7,-108.4,73.1,-106.5,73.1],
+ [-100.4,72.7,-101.5,73.4,-97.4,73.8,-96.5,72.6,-98.4,71.3,-102.5,72.5,-100.4,72.7],
+ [-106.6,73.6,-104.5,73.4,-106.6,73.6],
+ [-98.5,76.7,-98.2,75,-102.5,75.6,-98.5,76.7],
+ [-96,80.6,-92.4,81.3,-85.8,79.3,-92.9,78.3,-96,80.6],
+ [-91.6,81.9,-61.8,82.6,-76.9,79.3,-75.4,78.5,-80.6,76.2,-89.5,76.5,-88.3,77.9,-85,77.5,-88,78.4,-85.1,79.3,-86.9,80.3,-81.8,80.5,-91.6,81.9],
+ [-75.2,67.4,-77.2,67.6,-75.2,67.4],
+ [-96.3,69.5,-99.8,69.4,-96.3,69.5],
+ [-64.5,49.9,-61.8,49.1,-64.5,49.9],
+ [-64,47,-62,46.4,-64,47],
+ ],
+ },
+ {
+ name: "Central African Rep.",
+ iso: "CF",
+ rings: [
+ [27.4,5.2,22.4,4,19.5,5,16,2.3,14.5,5.5,15.3,7.4,22.9,11.1,27.4,5.2],
+ ],
+ },
+ {
+ name: "Chad",
+ iso: "TD",
+ rings: [
+ [23.8,19.6,23.9,15.6,21.9,12.6,22.9,11.1,15.3,7.4,13.5,14.4,15.9,20.4,14.9,22.9,23.8,19.6],
+ ],
+ },
+ {
+ name: "Chile",
+ iso: "CL",
+ rings: [
+ [-68.6,-52.6,-68.6,-54.9,-67,-54.9,-68.1,-55.6,-74.7,-52.8,-71.1,-54.1,-68.6,-52.6],
+ [-69.6,-17.6,-67,-23,-70.5,-31.4,-69.8,-34.2,-72.1,-42.3,-71.2,-44.8,-73.4,-49.3,-71.9,-52,-68.6,-52.3,-71.4,-53.9,-74.9,-52.3,-75.6,-48.7,-74.1,-46.9,-75.6,-46.6,-72.7,-42.4,-74.3,-43.2,-69.6,-17.6],
+ ],
+ },
+ {
+ name: "China",
+ iso: "CN",
+ rings: [
+ [109.5,18.2,108.6,19.4,110.8,20.1,109.5,18.2],
+ [80.3,42.3,80,44.9,87.8,49.3,91,46.9,90.9,45.3,96.3,42.7,109.2,42.5,111.9,45.1,119.7,46.7,115.5,48.1,122.2,53.4,125.9,52.8,131,47.8,135,48.5,133.1,45.1,131,45,130.6,42.4,121.1,38.9,121.6,40.9,117.5,38.7,122.4,37.5,119.2,34.9,121.9,31.7,121.7,28.2,118.7,24.5,110.4,20.3,105.3,23.4,101.7,22.3,101.8,21.2,99.2,22.1,97.6,23.9,98.7,27.5,96.1,29.5,88.8,27.3,78.7,31.5,78.9,34.3,73.7,39.4,80.3,42.3],
+ ],
+ },
+ {
+ name: "Colombia",
+ iso: "CO",
+ rings: [
+ [-66.9,1.3,-69.8,1.7,-69.9,-4.3,-70,-2.7,-77.4,0.4,-79,1.7,-77.1,3.8,-77.5,8.5,-71.4,12.4,-73.3,9.2,-72,7,-67.3,6.1,-66.9,1.3],
+ ],
+ },
+ {
+ name: "Congo",
+ iso: "CG",
+ rings: [
+ [18.5,3.5,16,-3.5,11.9,-5,11.5,-2.8,14.4,-1.3,13.1,2.3,15.9,1.7,18.5,3.5],
+ ],
+ },
+ {
+ name: "Costa Rica",
+ iso: "CR",
+ rings: [
+ [-82.5,9.6,-83,8.2,-85.9,10.9,-82.5,9.6],
+ ],
+ },
+ {
+ name: "Côte d'Ivoire",
+ iso: "CI",
+ rings: [
+ [-8,10.2,-2.8,9.6,-2.9,5,-7.7,4.4,-8,10.2],
+ ],
+ },
+ {
+ name: "Croatia",
+ iso: "HR",
+ rings: [
+ [16.6,46.5,19.4,45.2,15.8,44.8,18.5,42.5,13.7,45.1,16.6,46.5],
+ ],
+ },
+ {
+ name: "Cuba",
+ iso: "CU",
+ rings: [
+ [-82.3,23.2,-74.2,20.3,-77.8,19.9,-81.8,22.6,-85,21.9,-82.3,23.2],
+ ],
+ },
+ {
+ name: "Cyprus",
+ iso: "CY",
+ rings: [
+ [32.7,35.1,34,35,32.7,35.1],
+ ],
+ },
+ {
+ name: "Czechia",
+ iso: "CZ",
+ rings: [
+ [15,51.1,18.9,49.5,12.5,49.5,15,51.1],
+ ],
+ },
+ {
+ name: "Dem. Rep. Congo",
+ iso: "CD",
+ rings: [
+ [29.3,-4.5,30.7,-8.3,28.7,-8.5,28.4,-11.8,29.7,-13.3,22.2,-11.1,21.7,-7.3,17.5,-8.1,16.3,-5.9,12.2,-5.8,16,-3.5,19.5,5,29.7,4.6,31.2,2.2,29.3,-4.5],
+ ],
+ },
+ {
+ name: "Denmark",
+ iso: "DK",
+ rings: [
+ [9.9,55,8.1,56.5,10.6,57.7,9.9,55],
+ [12.4,56.1,12.1,54.8,11,55.4,12.4,56.1],
+ ],
+ },
+ {
+ name: "Djibouti",
+ iso: "DJ",
+ rings: [
+ [42.4,12.5,42.8,10.9,42.4,12.5],
+ ],
+ },
+ {
+ name: "Dominican Rep.",
+ iso: "DO",
+ rings: [
+ [-71.7,18,-71.6,19.9,-68.3,18.6,-71.7,18],
+ ],
+ },
+ {
+ name: "Ecuador",
+ iso: "EC",
+ rings: [
+ [-75.4,-0.2,-78.6,-4.5,-80.4,-4.4,-80.1,0.8,-75.4,-0.2],
+ ],
+ },
+ {
+ name: "Egypt",
+ iso: "EG",
+ rings: [
+ [36.9,22,25,22,25.2,31.6,34.3,31.2,34.2,27.8,32.3,29.8,36.9,22],
+ ],
+ },
+ {
+ name: "El Salvador",
+ iso: "SV",
+ rings: [
+ [-89.4,14.4,-87.9,13.1,-90.1,13.7,-89.4,14.4],
+ ],
+ },
+ {
+ name: "Eq. Guinea",
+ iso: "GQ",
+ rings: [
+ [9.6,2.3,11.3,1.1,9.5,1,9.6,2.3],
+ ],
+ },
+ {
+ name: "Eritrea",
+ iso: "ER",
+ rings: [
+ [36.4,14.4,38.4,18,43.1,12.7,36.4,14.4],
+ ],
+ },
+ {
+ name: "Estonia",
+ iso: "EE",
+ rings: [
+ [28,59.5,27.3,57.5,23.3,59.2,28,59.5],
+ ],
+ },
+ {
+ name: "eSwatini",
+ iso: "SZ",
+ rings: [
+ [32.1,-26.7,31,-25.7,32.1,-26.7],
+ ],
+ },
+ {
+ name: "Ethiopia",
+ iso: "ET",
+ rings: [
+ [47.8,8,45,5,39.6,3.4,36.2,4.4,33,7.8,37.9,15,41.6,13.5,43.7,9.2,47.8,8],
+ ],
+ },
+ {
+ name: "Falkland Is.",
+ iso: "FK",
+ rings: [
+ [-61.2,-51.8,-57.7,-51.5,-61.2,-51.8],
+ ],
+ },
+ {
+ name: "Finland",
+ iso: "FI",
+ rings: [
+ [28.6,69.1,31.1,62.4,28.1,60.5,21.3,60.7,21.5,63.2,25.4,65.1,20.6,69.1,24.7,68.6,27.7,70.2,28.6,69.1],
+ ],
+ },
+ {
+ name: "Fr. S. Antarctic Lands",
+ iso: "TF",
+ rings: [
+ [68.9,-48.6,70.6,-49.3,68.7,-49.8,68.9,-48.6],
+ ],
+ },
+ {
+ name: "France",
+ iso: "FR",
+ rings: [
+ [-51.7,4.2,-52.9,2.1,-54.5,2.3,-54,5.8,-51.7,4.2],
+ [6.2,49.5,8.1,49,6,46.7,7.4,43.7,1.8,42.3,-1.9,43.4,-1.2,46,-4.6,48.7,-1.6,48.6,-1.9,49.8,2.5,51.1,6.2,49.5],
+ [8.7,42.6,9.2,41.4,8.7,42.6],
+ ],
+ },
+ {
+ name: "Gabon",
+ iso: "GA",
+ rings: [
+ [11.3,2.3,14.3,1.2,14.4,-1.3,11.1,-4,8.8,-1.1,11.3,2.3],
+ ],
+ },
+ {
+ name: "Gambia",
+ iso: "GM",
+ rings: [
+ [-16.7,13.6,-13.8,13.5,-16.7,13.6],
+ ],
+ },
+ {
+ name: "Georgia",
+ iso: "GE",
+ rings: [
+ [40,43.4,46.6,41.2,41.6,41.5,40,43.4],
+ ],
+ },
+ {
+ name: "Germany",
+ iso: "DE",
+ rings: [
+ [14.1,53.8,15,51.1,12.2,50.3,12.9,47.5,7.5,47.6,8.1,49,6,50.1,7.1,53.7,9.9,55,14.1,53.8],
+ ],
+ },
+ {
+ name: "Ghana",
+ iso: "GH",
+ rings: [
+ [0,11,1.1,5.9,-2.9,5,-2.9,11,0,11],
+ ],
+ },
+ {
+ name: "Greece",
+ iso: "GR",
+ rings: [
+ [26.3,35.3,23.5,35.3,26.3,35.3],
+ [23,41.3,26.6,41.6,22.6,40.3,24,37.7,22.5,36.4,20.2,39.6,23,41.3],
+ ],
+ },
+ {
+ name: "Greenland",
+ iso: "GL",
+ rings: [
+ [-46.8,82.6,-27.1,83.5,-20.8,82.7,-31.9,82.2,-12.2,81.3,-20,80.2,-17.7,80.1,-19.7,78.8,-18.5,77,-21.7,76.6,-19.4,74.3,-24.8,72.3,-21.8,70.7,-25.5,71.4,-26.4,70.2,-22.3,70.1,-39.8,65.5,-43.4,60.1,-48.3,60.9,-51.6,63.6,-54,67.2,-50.9,69.9,-54.7,69.6,-54.4,70.8,-51.4,70.6,-55.8,71.7,-54.7,72.6,-58.6,75.5,-68.5,76.1,-71.4,77,-66.8,77.4,-73.3,78,-65.7,79.4,-68,80.1,-62.7,81.8,-44.5,81.7,-46.8,82.6],
+ ],
+ },
+ {
+ name: "Guatemala",
+ iso: "GT",
+ rings: [
+ [-92.2,14.5,-90.5,16.1,-91,17.8,-89.1,17.8,-88.2,15.7,-89.4,14.4,-92.2,14.5],
+ ],
+ },
+ {
+ name: "Guinea",
+ iso: "GN",
+ rings: [
+ [-13.7,12.6,-9.1,12.3,-8.3,7.7,-11.1,10,-13.2,8.9,-15.1,11,-13.7,12.6],
+ ],
+ },
+ {
+ name: "Guinea-Bissau",
+ iso: "GW",
+ rings: [
+ [-16.7,12.4,-13.7,11.8,-15.1,11,-16.7,12.4],
+ ],
+ },
+ {
+ name: "Guyana",
+ iso: "GY",
+ rings: [
+ [-56.5,1.9,-59.6,1.8,-61.4,6,-59.8,8.4,-57.1,6,-58,4.1,-56.5,1.9],
+ ],
+ },
+ {
+ name: "Haiti",
+ iso: "HT",
+ rings: [
+ [-71.7,19.7,-71.7,18,-74.5,18.3,-71.7,19.7],
+ ],
+ },
+ {
+ name: "Honduras",
+ iso: "HN",
+ rings: [
+ [-83.1,15,-87.3,13,-89.4,14.4,-87.9,15.9,-83.1,15],
+ ],
+ },
+ {
+ name: "Hungary",
+ iso: "HU",
+ rings: [
+ [22.1,48.4,21,46.3,16.2,46.9,17,48.1,22.1,48.4],
+ ],
+ },
+ {
+ name: "Iceland",
+ iso: "IS",
+ rings: [
+ [-14.5,66.5,-13.6,65.1,-18.7,63.5,-22.8,64,-21.8,64.4,-24,64.9,-22.2,65.4,-24.3,65.6,-14.5,66.5],
+ ],
+ },
+ {
+ name: "India",
+ iso: "IN",
+ rings: [
+ [97.3,28.3,92.7,22,91.2,23.5,92.4,25,88.6,26.4,88.9,21.7,80.3,15.9,79.9,10.4,77.5,8,72.6,21.4,70.5,20.9,68.2,23.7,71,24.4,69.5,26.9,75.3,32.3,73.7,34.3,77.8,35.5,78.7,31.5,81.1,30.2,80.1,28.8,83.3,27.4,88.1,26.4,88.7,28.1,92,26.8,96.1,29.5,97.3,28.3],
+ ],
+ },
+ {
+ name: "Indonesia",
+ iso: "ID",
+ rings: [
+ [141,-2.6,141,-9.1,137.6,-8.4,137.9,-5.4,133,-4.1,132,-2.8,133.7,-2.2,130.5,-0.9,134,-0.8,135.5,-3.4,137.4,-1.7,141,-2.6],
+ [125,-8.9,123.5,-10.2,125,-8.9],
+ [117.9,4.1,119,0.9,116.1,-4,110.2,-2.9,109.7,2,110.5,0.8,113.8,1.2,115.9,4.3,117.9,4.1],
+ [129.4,-2.8,130.8,-3.9,127.9,-3.4,129.4,-2.8],
+ [127.9,2.2,128.1,-0.9,127.9,2.2],
+ [122.9,0.9,125.2,1.4,120,-0.5,123.3,-0.6,121.5,-1.9,123.2,-5.3,121,-2.6,119.8,-5.7,118.8,-2.8,119.8,0.2,122.9,0.9],
+ [120.3,-10.3,119,-9.6,120.3,-10.3],
+ [121.3,-8.5,122.9,-8.1,119.9,-8.8,121.3,-8.5],
+ [118.3,-8.4,116.7,-9,118.3,-8.4],
+ [108.5,-6.4,115.7,-8.4,105.4,-6.9,108.5,-6.4],
+ [104.4,-1.1,106.1,-3.1,105.8,-5.9,102.6,-4.2,95.3,5.5,97.5,5.2,104.4,-1.1],
+ ],
+ },
+ {
+ name: "Iran",
+ iso: "IR",
+ rings: [
+ [48.6,29.9,45.4,34,46.1,35.7,44.1,39.4,48.1,39.6,50.8,36.9,56.6,38.1,61.1,36.5,60.9,29.8,63.3,26.8,61.5,25.1,57.4,25.7,48.6,29.9],
+ ],
+ },
+ {
+ name: "Iraq",
+ iso: "IQ",
+ rings: [
+ [39.2,32.2,41.3,36.4,44.8,37.2,48.6,29.9,44.7,29.2,39.2,32.2],
+ ],
+ },
+ {
+ name: "Ireland",
+ iso: "IE",
+ rings: [
+ [-6.2,53.9,-6.8,52.3,-10,51.8,-7.6,55.1,-6.2,53.9],
+ ],
+ },
+ {
+ name: "Israel",
+ iso: "IL",
+ rings: [
+ [35.7,32.7,34.9,29.5,34.3,31.2,35.7,32.7],
+ ],
+ },
+ {
+ name: "Italy",
+ iso: "IT",
+ rings: [
+ [10.4,46.9,13.8,46.5,12.6,44.1,18.3,39.8,16.9,40.4,15.7,37.9,15.4,40,10.2,43.9,7.4,43.7,6.8,46,10.4,46.9],
+ [14.8,38.1,15.1,36.6,12.4,37.6,14.8,38.1],
+ [8.7,40.9,9.8,40.5,8.8,38.9,8.7,40.9],
+ ],
+ },
+ {
+ name: "Jamaica",
+ iso: "JM",
+ rings: [
+ [-77.6,18.5,-76.2,17.9,-77.6,18.5],
+ ],
+ },
+ {
+ name: "Japan",
+ iso: "JP",
+ rings: [
+ [141.9,39.2,140.3,35.1,135.8,33.5,135.1,34.6,131,33.9,132,33.1,130.2,31.4,129.4,33.3,139.4,38.2,140.3,41.2,141.9,39.2],
+ [144.6,44,145.5,43.3,140,41.6,142,45.6,144.6,44],
+ [132.4,33.5,134.8,33.8,132.4,33.5],
+ ],
+ },
+ {
+ name: "Jordan",
+ iso: "JO",
+ rings: [
+ [35.5,32.4,38.8,33.4,39.2,32.2,37,31.5,38,30.5,36.1,29.2,34.9,29.5,35.5,32.4],
+ ],
+ },
+ {
+ name: "Kazakhstan",
+ iso: "KZ",
+ rings: [
+ [87.4,49.2,80,44.9,80.3,42.3,74.2,43.3,68.6,40.7,64.9,43.7,62,43.5,58.5,45.6,55.9,45,56,41.3,52.5,41.8,50.3,44.6,53,45.3,53,46.9,49.1,46.4,46.5,48.4,50.8,51.7,61.3,50.8,60,52,61.4,54,69.1,55.4,73.4,53.5,76.9,54.5,80,50.9,87.4,49.2],
+ ],
+ },
+ {
+ name: "Kenya",
+ iso: "KE",
+ rings: [
+ [39.2,-4.7,33.9,-0.9,35.3,5.5,38.1,3.6,41.9,3.9,41.6,-1.7,39.2,-4.7],
+ ],
+ },
+ {
+ name: "Kosovo",
+ iso: "XK",
+ rings: [
+ [20.6,41.9,20.6,43.2,21.8,42.7,20.6,41.9],
+ ],
+ },
+ {
+ name: "Kuwait",
+ iso: "KW",
+ rings: [
+ [48,30,48.4,28.6,46.6,29.1,48,30],
+ ],
+ },
+ {
+ name: "Kyrgyzstan",
+ iso: "KG",
+ rings: [
+ [71,42.3,74.2,43.3,80.3,42.3,73.7,39.4,69.5,39.5,73.1,40.9,71,42.3],
+ ],
+ },
+ {
+ name: "Laos",
+ iso: "LA",
+ rings: [
+ [107.4,14.2,105.2,14.3,104,18.2,101.1,17.5,100.1,20.4,101.7,22.3,104.4,20.8,103.9,19.3,107.4,14.2],
+ ],
+ },
+ {
+ name: "Latvia",
+ iso: "LV",
+ rings: [
+ [27.3,57.5,28.2,56.2,26.5,55.6,21.1,56,22.5,57.8,27.3,57.5],
+ ],
+ },
+ {
+ name: "Lebanon",
+ iso: "LB",
+ rings: [
+ [35.8,33.3,36.4,34.6,35.8,33.3],
+ ],
+ },
+ {
+ name: "Lesotho",
+ iso: "LS",
+ rings: [
+ [29,-29,28.1,-30.5,27,-29.9,29,-29],
+ ],
+ },
+ {
+ name: "Liberia",
+ iso: "LR",
+ rings: [
+ [-8.4,7.7,-7.7,4.4,-11.4,6.8,-10.2,8.4,-8.4,7.7],
+ ],
+ },
+ {
+ name: "Libya",
+ iso: "LY",
+ rings: [
+ [25,22,23.8,19.6,10.3,24.4,10,31.4,11.5,33.1,19.1,30.3,20.9,32.7,24.9,31.9,25,22],
+ ],
+ },
+ {
+ name: "Lithuania",
+ iso: "LT",
+ rings: [
+ [26.5,55.6,23.5,53.9,21.1,56,26.5,55.6],
+ ],
+ },
+ {
+ name: "Madagascar",
+ iso: "MG",
+ rings: [
+ [49.5,-12.5,50.4,-15.7,47.1,-24.9,45.4,-25.6,43.3,-22.8,44,-17.4,49.5,-12.5],
+ ],
+ },
+ {
+ name: "Malawi",
+ iso: "MW",
+ rings: [
+ [32.8,-9.2,35.7,-14.6,35,-16.8,32.7,-13.7,32.8,-9.2],
+ ],
+ },
+ {
+ name: "Malaysia",
+ iso: "MY",
+ rings: [
+ [100.1,6.5,103,5.5,104.2,1.3,101.4,2.8,100.1,6.5],
+ [117.9,4.1,115.9,4.3,114.6,1.4,109.8,1.3,115.3,4.3,116.7,6.9,119.2,5.4,117.9,4.1],
+ ],
+ },
+ {
+ name: "Mali",
+ iso: "ML",
+ rings: [
+ [-11.5,12.4,-11.7,15.4,-5.5,15.5,-6.5,25,4.3,19.2,3.6,15.6,-4,13.5,-5.4,10.4,-11.5,12.4],
+ ],
+ },
+ {
+ name: "Mauritania",
+ iso: "MR",
+ rings: [
+ [-17.1,21,-12.9,21.3,-12,25.9,-8.7,25.9,-8.7,27.4,-4.9,25,-6.5,25,-5.5,15.5,-12.2,14.6,-14.6,16.6,-16.5,16.1,-17.1,21],
+ ],
+ },
+ {
+ name: "Mexico",
+ iso: "MX",
+ rings: [
+ [-117.1,32.5,-106.5,31.8,-103.9,29.3,-101.7,29.8,-97.1,25.9,-97.9,22.4,-95.9,18.8,-91.4,18.9,-90.3,21,-87.1,21.5,-87.8,18.3,-91,17.8,-90.5,16.1,-92.2,14.5,-103.5,18.3,-113.1,31.2,-114.9,31.4,-109.9,22.8,-115.1,27.7,-114.2,28.6,-117.1,32.5],
+ ],
+ },
+ {
+ name: "Moldova",
+ iso: "MD",
+ rings: [
+ [26.6,48.2,30,46.4,28.2,45.5,26.6,48.2],
+ ],
+ },
+ {
+ name: "Mongolia",
+ iso: "MN",
+ rings: [
+ [87.8,49.3,92.2,50.8,97.3,49.7,98.9,52,108.5,49.3,116.7,49.9,115.7,47.7,119.8,47,105,41.6,96.3,42.7,90.9,45.3,91,46.9,87.8,49.3],
+ ],
+ },
+ {
+ name: "Montenegro",
+ iso: "ME",
+ rings: [
+ [20.1,42.6,18.5,42.5,20.1,42.6],
+ ],
+ },
+ {
+ name: "Morocco",
+ iso: "MA",
+ rings: [
+ [-2.2,35.2,-1.3,32.3,-8.7,28.8,-8.8,27.1,-11.4,26.9,-14.8,21.5,-17,21.4,-14.4,26.3,-9.6,29.9,-8.7,33.2,-5.9,35.8,-2.2,35.2],
+ ],
+ },
+ {
+ name: "Mozambique",
+ iso: "MZ",
+ rings: [
+ [34.6,-11.5,40.3,-10.3,40.8,-14.7,34.8,-19.8,35.5,-24.1,32.1,-26.7,31.2,-22.3,32.8,-16.7,30.2,-14.8,33.2,-14,35,-16.8,34.6,-11.5],
+ ],
+ },
+ {
+ name: "Myanmar",
+ iso: "MM",
+ rings: [
+ [100.1,20.4,97.4,18.4,99.6,11.9,98.6,9.9,97.2,16.9,94.2,16,92.3,21.5,97.3,28.3,98.7,27.5,97.6,23.9,101.2,21.8,100.1,20.4],
+ ],
+ },
+ {
+ name: "N. Cyprus",
+ iso: "-99",
+ rings: [
+ [32.7,35.1,34.6,35.7,32.7,35.1],
+ ],
+ },
+ {
+ name: "Namibia",
+ iso: "NA",
+ rings: [
+ [19.9,-24.8,19.9,-28.5,16.3,-28.6,11.7,-17.3,25.1,-17.6,20.9,-18.3,19.9,-24.8],
+ ],
+ },
+ {
+ name: "Nepal",
+ iso: "NP",
+ rings: [
+ [88.1,27.9,87.2,26.4,80.1,28.8,81.5,30.4,88.1,27.9],
+ ],
+ },
+ {
+ name: "Netherlands",
+ iso: "NL",
+ rings: [
+ [6.9,53.5,6.2,50.8,3.3,51.3,6.9,53.5],
+ ],
+ },
+ {
+ name: "New Caledonia",
+ iso: "NC",
+ rings: [
+ [165.8,-21.1,167.1,-22.2,164,-20.1,165.8,-21.1],
+ ],
+ },
+ {
+ name: "New Zealand",
+ iso: "NZ",
+ rings: [
+ [176.9,-40.1,174.7,-41.3,174.7,-37.4,172.6,-34.5,176,-37.6,178.5,-37.7,176.9,-40.1],
+ [169.7,-43.6,172.8,-40.5,174.2,-41.3,170.6,-45.9,166.7,-46.2,169.7,-43.6],
+ ],
+ },
+ {
+ name: "Nicaragua",
+ iso: "NI",
+ rings: [
+ [-83.7,10.9,-87.7,12.9,-83.1,15,-83.7,10.9],
+ ],
+ },
+ {
+ name: "Niger",
+ iso: "NE",
+ rings: [
+ [14.9,22.9,15.9,20.4,13.5,14.4,14.2,12.5,5.4,13.9,3.6,11.7,1,12.9,0.4,14.9,3.6,15.6,4.3,19.2,12,23.5,14.9,22.9],
+ ],
+ },
+ {
+ name: "Nigeria",
+ iso: "NG",
+ rings: [
+ [2.7,6.3,4.4,13.7,13.1,13.6,14.6,12.1,11.7,7,8.5,4.8,5.9,4.3,2.7,6.3],
+ ],
+ },
+ {
+ name: "North Korea",
+ iso: "KP",
+ rings: [
+ [130.6,42.4,127.5,39.8,128.2,38.4,124.7,38.1,125.1,40.6,130.6,42.4],
+ ],
+ },
+ {
+ name: "North Macedonia",
+ iso: "MK",
+ rings: [
+ [22.4,42.3,23,41.3,20.6,41.1,22.4,42.3],
+ ],
+ },
+ {
+ name: "Norway",
+ iso: "NO",
+ rings: [
+ [15.1,79.7,21.5,79,15.9,76.8,10.4,79.7,15.1,79.7],
+ [31.1,69.6,18,68.6,12.6,64.1,11,58.9,5.7,58.6,5,62,19.2,69.8,28.2,71.2,31.1,69.6],
+ [27.4,80.1,17.4,80.3,27.4,80.1],
+ [24.7,77.9,20.7,77.7,24.7,77.9],
+ ],
+ },
+ {
+ name: "Oman",
+ iso: "OM",
+ rings: [
+ [55.2,22.7,56.4,24.9,59.8,22.3,57.7,18.9,53.1,16.7,52,19,55,20,55.2,22.7],
+ ],
+ },
+ {
+ name: "Pakistan",
+ iso: "PK",
+ rings: [
+ [77.8,35.5,73.7,34.3,75.3,32.3,69.5,26.9,71,24.4,61.5,25.1,63.3,26.8,60.9,29.8,66.3,29.9,71.8,36.5,75.2,37.1,77.8,35.5],
+ ],
+ },
+ {
+ name: "Panama",
+ iso: "PA",
+ rings: [
+ [-77.4,8.7,-77.9,7.2,-79.1,9,-80.9,7.2,-82.9,9.5,-77.4,8.7],
+ ],
+ },
+ {
+ name: "Papua New Guinea",
+ iso: "PG",
+ rings: [
+ [141,-2.6,147.6,-6.1,147.2,-7.4,150.7,-10.6,144.7,-7.6,141,-9.1,141,-2.6],
+ [152.6,-3.7,152.8,-4.8,150.7,-2.7,152.6,-3.7],
+ [151.3,-5.8,148.3,-5.7,152.1,-4.1,151.3,-5.8],
+ [154.8,-5.3,155.9,-6.8,154.8,-5.3],
+ ],
+ },
+ {
+ name: "Paraguay",
+ iso: "PY",
+ rings: [
+ [-58.2,-20.2,-57.9,-22.1,-54.3,-24,-55.7,-27.4,-58.6,-27.1,-57.8,-25.2,-62.7,-22.2,-61.8,-19.6,-58.2,-20.2],
+ ],
+ },
+ {
+ name: "Peru",
+ iso: "PE",
+ rings: [
+ [-69.9,-4.3,-72.9,-5.3,-74,-7.5,-68.7,-12.6,-70.4,-18.3,-76,-14.6,-81.4,-4.7,-80.3,-3.4,-78.6,-4.5,-75.1,-0.1,-73.1,-2.3,-70,-2.7,-69.9,-4.3],
+ ],
+ },
+ {
+ name: "Philippines",
+ iso: "PH",
+ rings: [
+ [122.6,10,124.1,11.2,123,9,122.6,10],
+ [126.4,8.4,125.4,5.6,123.6,7.8,121.9,7.2,125.4,9.8,126.4,8.4],
+ [118.5,9.3,117.2,8.4,119.5,11.4,118.5,9.3],
+ [122.3,18.2,121.7,14.3,124.1,12.5,119.9,15.4,120.7,18.5,122.3,18.2],
+ [125.5,12.2,124.8,10.1,124.3,12.6,125.5,12.2],
+ ],
+ },
+ {
+ name: "Poland",
+ iso: "PL",
+ rings: [
+ [23.5,53.9,24,50.7,22.8,49,16.2,50.4,14.1,53,17.6,54.9,23.5,53.9],
+ ],
+ },
+ {
+ name: "Portugal",
+ iso: "PT",
+ rings: [
+ [-9,41.9,-6.4,41.4,-7.9,36.8,-9.5,38.7,-9,41.9],
+ ],
+ },
+ {
+ name: "Puerto Rico",
+ iso: "PR",
+ rings: [
+ [-66.3,18.5,-67.2,17.9,-66.3,18.5],
+ ],
+ },
+ {
+ name: "Qatar",
+ iso: "QA",
+ rings: [
+ [50.8,24.8,51.3,26.1,50.8,24.8],
+ ],
+ },
+ {
+ name: "Romania",
+ iso: "RO",
+ rings: [
+ [28.2,45.5,29.6,45.3,28.6,43.7,22.9,43.8,20.2,46.1,26.6,48.2,28.2,45.5],
+ ],
+ },
+ {
+ name: "Russia",
+ iso: "RU",
+ rings: [
+ [49.1,46.4,46.7,44.6,47.8,41.2,36.7,45.2,40.1,49.6,31.8,52.1,32.7,53.4,30.9,55.6,27.3,57.5,29.1,60,28.1,60.5,31.5,62.9,30,63.6,28.6,69.1,32.1,69.9,41.1,67.5,38.4,66,33.2,66.6,37,63.8,37.2,65.1,43.9,66.1,43.5,68.6,46.3,68.3,46.3,66.7,53.7,68.9,59.9,68.3,60.6,69.9,68.5,68.1,66.7,71,69.9,73,72.8,72.2,71.8,71.4,73.7,68.4,71.3,66.3,72.4,66.2,75.1,67.8,73.1,71.4,74.7,72.8,76.4,71.2,81.5,71.8,80.5,73.6,104.4,77.7,114.1,75.8,109.4,74.2,127,73.6,131.3,70.8,139.9,71.5,139.1,72.4,140.5,72.8,159,70.9,160.9,69.4,180,69,180,65,177.4,64.6,179.2,62.3,170.3,59.9,163.5,59.9,162,58.2,163.2,57.6,162.1,54.9,156.8,51,155.9,56.8,164.5,62.6,160.1,60.5,156.7,61.4,154.2,59.8,155,59.1,142.2,59,135.1,54.7,141.3,53.1,140.1,48.4,134.9,43.4,130.8,42.2,131,45,133.1,45.1,135,48.5,131,47.8,123.6,53.5,120.2,52.8,117.9,49.5,108.5,49.3,98.9,52,97.3,49.7,92.2,50.8,87.4,49.2,80,50.9,76.9,54.5,73.4,53.5,69.1,55.4,61.4,54,60,52,61.3,50.8,50.8,51.7,47.5,50.5,46.5,48.4,49.1,46.4],
+ [93.8,81,100.2,79.8,97.8,78.8,91.2,80.3,93.8,81],
+ [102.8,79.3,105.4,78.7,99.4,77.9,102.8,79.3],
+ [138.8,76.1,145.1,75.6,137,75.3,138.8,76.1],
+ [148.2,75.3,150.7,75.1,146.1,75.2,148.2,75.3],
+ [139.9,73.4,143.6,73.2,139.9,73.4],
+ [44.8,80.6,51.5,80.7,44.8,80.6],
+ [22.7,54.3,19.7,54.4,22.7,54.3],
+ [53.5,73.7,61.2,76.3,68.9,76.5,58.5,74.3,55.4,72.4,57.5,70.7,51.6,71.5,53.5,73.7],
+ [142.9,53.7,144.7,49,143.2,49.3,143.5,46.1,142.1,46,141.7,53.3,142.9,53.7],
+ [-174.9,67.2,-169.9,66,-173,64.3,-178.7,66.1,-180,65,-180,69,-174.9,67.2],
+ [-178.7,70.9,-180,71.5,-177.6,71.3,-178.7,70.9],
+ [33.4,46,36.5,45.5,33.9,44.4,32.5,45.3,33.4,46],
+ ],
+ },
+ {
+ name: "Rwanda",
+ iso: "RW",
+ rings: [
+ [30.4,-1.1,29,-2.8,30.4,-1.1],
+ ],
+ },
+ {
+ name: "S. Sudan",
+ iso: "SS",
+ rings: [
+ [30.8,3.5,23.9,8.6,25.8,10.4,31.4,9.8,33.2,12.2,33,7.8,35.3,5.5,30.8,3.5],
+ ],
+ },
+ {
+ name: "Saudi Arabia",
+ iso: "SA",
+ rings: [
+ [35,29.4,39.2,32.2,47.5,29,52,23,55.2,22.7,55,20,47,16.9,43.4,17.6,42.8,16.3,35,29.4],
+ ],
+ },
+ {
+ name: "Senegal",
+ iso: "SN",
+ rings: [
+ [-16.7,13.6,-17.6,14.7,-14.6,16.6,-11.5,12.4,-16.7,12.4,-13.8,13.5,-16.7,13.6],
+ ],
+ },
+ {
+ name: "Serbia",
+ iso: "RS",
+ rings: [
+ [18.8,45.9,22.7,44.6,22.5,42.5,19.2,43.5,18.8,45.9],
+ ],
+ },
+ {
+ name: "Sierra Leone",
+ iso: "SL",
+ rings: [
+ [-13.2,8.9,-11.1,10,-10.2,8.4,-11.4,6.8,-13.2,8.9],
+ ],
+ },
+ {
+ name: "Slovakia",
+ iso: "SK",
+ rings: [
+ [22.6,49.1,16.9,48.5,22.6,49.1],
+ ],
+ },
+ {
+ name: "Slovenia",
+ iso: "SI",
+ rings: [
+ [13.8,46.5,16.6,46.5,15.3,45.5,13.8,46.5],
+ ],
+ },
+ {
+ name: "Solomon Is.",
+ iso: "SB",
+ rings: [
+ [159.6,-8,158.2,-7.4,159.6,-8],
+ ],
+ },
+ {
+ name: "Somalia",
+ iso: "SO",
+ rings: [
+ [41.6,-1.7,41,2.8,45,5,48.9,9.5,48.9,11.4,51.1,12,48.6,5.3,41.6,-1.7],
+ ],
+ },
+ {
+ name: "Somaliland",
+ iso: "-99",
+ rings: [
+ [48.9,11.4,47.8,8,42.6,10.6,48.9,11.4],
+ ],
+ },
+ {
+ name: "South Africa",
+ iso: "ZA",
+ rings: [
+ [16.3,-28.6,19.9,-28.5,19.9,-24.8,21.6,-26.7,25.7,-25.5,29.4,-22.1,31.2,-22.3,31.9,-24.4,30.7,-26.7,32.8,-26.7,28.2,-32.8,20.1,-34.8,18.4,-34.1,16.3,-28.6],
+ ],
+ },
+ {
+ name: "South Korea",
+ iso: "KR",
+ rings: [
+ [126.2,37.7,128.3,38.6,129.1,35.1,126.5,34.4,126.2,37.7],
+ ],
+ },
+ {
+ name: "Spain",
+ iso: "ES",
+ rings: [
+ [-7.5,37.1,-6.4,41.4,-9.4,43,3,42.5,-2.1,36.7,-7.5,37.1],
+ ],
+ },
+ {
+ name: "Sri Lanka",
+ iso: "LK",
+ rings: [
+ [81.8,7.5,80.3,6,80.1,9.8,81.8,7.5],
+ ],
+ },
+ {
+ name: "Sudan",
+ iso: "SD",
+ rings: [
+ [24.6,8.2,21.9,12.6,25,22,36.9,22,38.4,18,34,8.7,32.7,12.2,31.4,9.8,25.1,10.3,24.6,8.2],
+ ],
+ },
+ {
+ name: "Suriname",
+ iso: "SR",
+ rings: [
+ [-54.5,2.3,-56.5,1.9,-57.6,3.3,-57.1,6,-54,5.8,-54.5,2.3],
+ ],
+ },
+ {
+ name: "Sweden",
+ iso: "SE",
+ rings: [
+ [11,58.9,12.6,61.3,11.9,63.1,16.8,68,20.6,69.1,23.5,67.9,23.9,66,17.8,62.7,17.1,61.3,18.8,60.1,15.9,56.1,12.9,55.4,11,58.9],
+ ],
+ },
+ {
+ name: "Switzerland",
+ iso: "CH",
+ rings: [
+ [9.6,47.5,10.4,46.5,6,46.3,9.6,47.5],
+ ],
+ },
+ {
+ name: "Syria",
+ iso: "SY",
+ rings: [
+ [35.7,32.7,36.7,36.8,42.3,37.2,41,34.4,35.7,32.7],
+ ],
+ },
+ {
+ name: "Taiwan",
+ iso: "TW",
+ rings: [
+ [121.8,24.4,120.7,22,120.1,23.6,121.8,24.4],
+ ],
+ },
+ {
+ name: "Tajikistan",
+ iso: "TJ",
+ rings: [
+ [67.8,37.1,67.7,39.6,70.7,41,69.5,39.5,73.7,39.4,75,37.4,71.8,36.7,70.8,38.5,67.8,37.1],
+ ],
+ },
+ {
+ name: "Tanzania",
+ iso: "TZ",
+ rings: [
+ [33.9,-0.9,39.2,-4.7,39.5,-10.9,34.6,-11.5,29.6,-6.5,30.4,-1.1,33.9,-0.9],
+ ],
+ },
+ {
+ name: "Thailand",
+ iso: "TH",
+ rings: [
+ [105.2,14.3,103,14.2,102.6,12.2,100.1,13.4,99.2,9.2,102.1,6.2,101.2,5.7,98.2,8.4,99.6,11.9,97.4,18.4,100.1,20.4,101.1,17.5,104.7,17.4,105.2,14.3],
+ ],
+ },
+ {
+ name: "Timor-Leste",
+ iso: "TL",
+ rings: [
+ [125,-8.9,127.3,-8.4,125,-8.9],
+ ],
+ },
+ {
+ name: "Togo",
+ iso: "TG",
+ rings: [
+ [0.9,11,1.1,5.9,0.9,11],
+ ],
+ },
+ {
+ name: "Tunisia",
+ iso: "TN",
+ rings: [
+ [9.5,30.3,7.5,34.1,9.5,37.3,11,37.1,10.1,34.3,11.5,33.1,9.5,30.3],
+ ],
+ },
+ {
+ name: "Turkey",
+ iso: "TR",
+ rings: [
+ [44.8,37.2,29.7,36.1,27.6,36.7,26.2,39.5,33.5,42,42.6,41.6,44.8,39.7,44.8,37.2],
+ [26.1,41.8,29,41.3,26.4,40.2,26.1,41.8],
+ ],
+ },
+ {
+ name: "Turkmenistan",
+ iso: "TM",
+ rings: [
+ [52.5,41.8,57.1,41.3,58.6,42.8,66.5,37.4,62.2,35.3,57.3,38,53.9,37.2,52.7,40,54.7,41,52.5,41.8],
+ ],
+ },
+ {
+ name: "Uganda",
+ iso: "UG",
+ rings: [
+ [33.9,-0.9,29.6,-1.3,31.2,3.8,34.5,3.6,33.9,-0.9],
+ ],
+ },
+ {
+ name: "Ukraine",
+ iso: "UA",
+ rings: [
+ [31.8,52.1,40.1,49.6,39.7,47.9,35,45.7,31.7,46.7,28.7,45.3,30,46.4,28.7,48.1,22.1,48.4,23.5,51.6,31.8,52.1],
+ ],
+ },
+ {
+ name: "United Arab Emirates",
+ iso: "AE",
+ rings: [
+ [51.6,24.2,56.3,25.7,55,22.5,51.6,24.2],
+ ],
+ },
+ {
+ name: "United Kingdom",
+ iso: "GB",
+ rings: [
+ [-6.2,53.9,-7.6,55.1,-6.2,53.9],
+ [-3.1,53.4,-6.1,56.8,-5,58.6,-2,57.7,-3.1,56,1.7,52.7,1.4,51.3,-5.8,50.2,-3.4,51.4,-5.3,52,-4.6,53.5,-3.1,53.4],
+ ],
+ },
+ {
+ name: "United States of America",
+ iso: "US",
+ rings: [
+ [-122.8,49,-88.4,48.3,-82.6,45.3,-82.7,41.7,-71.5,45,-69.2,47.4,-67,44.8,-70.1,43.7,-70,41.6,-75.5,39.5,-75.9,37.2,-76.3,39.2,-77,38.2,-75.7,35.6,-81.3,31.4,-80.4,25.2,-83.7,29.9,-86.4,30.4,-94.7,29.5,-97.5,25.8,-101,29.4,-103.9,29.3,-106.5,31.8,-117.1,32.5,-120.6,34.6,-124.4,40.3,-124.7,48.2,-122.6,47.1,-122.8,49],
+ [-166.5,60.4,-165.6,59.9,-167.5,60.2,-166.5,60.4],
+ [-153.2,58,-152.1,57.6,-154.5,57,-153.2,58],
+ [-141,69.7,-141,60.3,-137.5,58.9,-135.5,59.8,-130,55.9,-130.5,54.8,-134.1,58.1,-139.9,59.5,-147.1,60.9,-151.7,59.2,-150.6,61.3,-158.4,56,-164.9,54.6,-157,58.9,-162,58.7,-165.3,60.5,-165.7,62.1,-160.8,64.8,-168.1,65.7,-161.7,66.1,-166.2,68.9,-156.6,71.4,-141,69.7],
+ [-171.7,63.8,-168.7,63.3,-171.7,63.8],
+ ],
+ },
+ {
+ name: "Uruguay",
+ iso: "UY",
+ rings: [
+ [-57.6,-30.2,-53.8,-32,-53.8,-34.4,-58.4,-33.9,-57.6,-30.2],
+ ],
+ },
+ {
+ name: "Uzbekistan",
+ iso: "UZ",
+ rings: [
+ [56,41.3,55.9,45,58.5,45.6,62,43.5,64.9,43.7,68.3,40.7,71,42.3,73.1,40.9,67.7,39.6,67.8,37.1,58.6,42.8,56,41.3],
+ ],
+ },
+ {
+ name: "Venezuela",
+ iso: "VE",
+ rings: [
+ [-60.7,5.2,-64.8,4.1,-63.4,2.2,-66.3,0.7,-67.8,2.8,-67.3,6.1,-72,7,-72.9,10.5,-71.3,11.8,-71.3,9.1,-69.9,12.2,-68.2,10.6,-61.9,10.7,-59.8,8.4,-60.7,5.2],
+ ],
+ },
+ {
+ name: "Vietnam",
+ iso: "VN",
+ rings: [
+ [104.3,10.5,107.5,12.3,107.6,15.2,102.2,22.5,105.3,23.4,108.1,21.6,105.7,19.1,108.9,15.3,109.2,11.7,105.2,8.6,104.3,10.5],
+ ],
+ },
+ {
+ name: "W. Sahara",
+ iso: "EH",
+ rings: [
+ [-8.7,27.7,-8.7,25.9,-12,25.9,-12.9,21.3,-17.1,21,-14.8,21.5,-11.4,26.9,-8.7,27.7],
+ ],
+ },
+ {
+ name: "Yemen",
+ iso: "YE",
+ rings: [
+ [52,19,52.2,15.6,43.5,12.6,43.4,17.6,47,16.9,52,19],
+ ],
+ },
+ {
+ name: "Zambia",
+ iso: "ZM",
+ rings: [
+ [30.7,-8.3,33.2,-9.7,33.2,-14,27,-17.9,23.2,-17.5,21.9,-12.9,24,-12.9,23.9,-10.9,29.7,-13.3,28.4,-9.2,30.7,-8.3],
+ ],
+ },
+ {
+ name: "Zimbabwe",
+ iso: "ZW",
+ rings: [
+ [31.2,-22.3,28,-21.5,25.3,-17.7,30.3,-15.5,32.8,-16.7,31.2,-22.3],
+ ],
+ },
+];
diff --git a/packages/hqtui/src/graphics/world.ts b/packages/hqtui/src/graphics/world.ts
new file mode 100644
index 0000000..ec1faca
--- /dev/null
+++ b/packages/hqtui/src/graphics/world.ts
@@ -0,0 +1,171 @@
+/**
+ * The world, as shapes for the canvas, and the lookup that makes it clickable.
+ *
+ * The canvas already draws in the caller's own coordinates, and longitude and
+ * latitude are just another pair of axes -- so a map is a list of polylines in
+ * degrees, and nothing here needs a projection of its own beyond deciding which
+ * window on the globe to show.
+ *
+ * The interesting half 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 testing it against the outlines. Doing
+ * it that way rather than with bounding boxes is what makes the answer right:
+ * Russia's bounding box covers most of the northern hemisphere, and Chile's
+ * covers Argentina.
+ */
+import type { Color } from "../color.ts";
+import type { Bounds, Shape } from "./canvas.ts";
+import { type CountryOutline, WORLD_COUNTRIES } from "./world-data.ts";
+
+export { type CountryOutline, WORLD_COUNTRIES };
+
+/** The whole globe, which is what a map shows unless told otherwise. */
+export const WORLD_X: Bounds = { min: -180, max: 180 };
+export const WORLD_Y: Bounds = { min: -90, max: 90 };
+
+export interface WorldShapeOptions {
+ /** Colour for countries with nothing special about them. */
+ color?: Color;
+ /** Countries to pick out, by name or ISO code. */
+ highlight?: readonly string[];
+ highlightColor?: Color;
+}
+
+/** Match on either the name or the ISO code, case-insensitively. */
+function matches(country: CountryOutline, keys: readonly string[]): boolean {
+ for (const key of keys) {
+ if (key.length === 0) continue;
+ if (country.name.toLowerCase() === key.toLowerCase()) return true;
+ if (country.iso.length > 0 && country.iso.toLowerCase() === key.toLowerCase()) return true;
+ }
+ return false;
+}
+
+/**
+ * The world as canvas shapes, one polyline per landmass.
+ *
+ * Polylines rather than scattered points: the outlines are closed rings, so
+ * joining them draws a coastline instead of a dotted suggestion of one, and it
+ * reads at a fraction of the resolution dots would need.
+ */
+export function worldShapes(options: WorldShapeOptions = {}): Shape[] {
+ const shapes: Shape[] = [];
+ const highlight = options.highlight ?? [];
+ for (const country of WORLD_COUNTRIES) {
+ const picked = highlight.length > 0 && matches(country, highlight);
+ const color = picked ? options.highlightColor ?? options.color : options.color;
+ for (const ring of country.rings) {
+ const points: [number, number][] = [];
+ for (let i = 0; i + 1 < ring.length; i += 2) points.push([ring[i], ring[i + 1]]);
+ // Closed: the last point joins the first, or every country has a gap in
+ // its coastline where the ring started.
+ if (points.length > 0) points.push(points[0]);
+ shapes.push({ type: "polyline", points, color });
+ }
+ }
+ return shapes;
+}
+
+/**
+ * Whether a point is inside a ring, by ray casting.
+ *
+ * The ring is a flat list of interleaved coordinates, so this walks it two at a
+ * time rather than allocating a pair per vertex -- it runs once per country per
+ * click, and there are a couple of thousand vertices.
+ */
+function insideRing(ring: number[], lon: number, lat: number): boolean {
+ let inside = false;
+ const n = ring.length / 2;
+ for (let i = 0, j = n - 1; i < n; j = i++) {
+ const xi = ring[i * 2];
+ const yi = ring[i * 2 + 1];
+ const xj = ring[j * 2];
+ const yj = ring[j * 2 + 1];
+ if ((yi > lat) !== (yj > lat) && lon < ((xj - xi) * (lat - yi)) / (yj - yi) + xi) {
+ inside = !inside;
+ }
+ }
+ return inside;
+}
+
+/**
+ * The country containing a point, or undefined for open water.
+ *
+ * Where outlines overlap -- and at this resolution simplified borders do
+ * overlap -- the first match wins, which is stable because the data is sorted
+ * by name.
+ */
+export function countryAt(lon: number, lat: number): CountryOutline | undefined {
+ if (!Number.isFinite(lon) || !Number.isFinite(lat)) return undefined;
+ for (const country of WORLD_COUNTRIES) {
+ for (const ring of country.rings) {
+ if (insideRing(ring, lon, lat)) return country;
+ }
+ }
+ return undefined;
+}
+
+/** Look a country up by name or ISO code. */
+export function findCountry(key: string): CountryOutline | undefined {
+ return WORLD_COUNTRIES.find((c) => matches(c, [key]));
+}
+
+/**
+ * The window a country fills, with a little room around it.
+ *
+ * For zooming a map to a country: the bounding box alone puts the coastline
+ * flat against the edge of the panel, which reads as though the country has
+ * been cut off rather than framed.
+ */
+export function countryBounds(
+ country: CountryOutline,
+ margin = 0.08,
+): { x: Bounds; y: Bounds } {
+ let minLon = Infinity;
+ let maxLon = -Infinity;
+ let minLat = Infinity;
+ let maxLat = -Infinity;
+ for (const ring of country.rings) {
+ for (let i = 0; i + 1 < ring.length; i += 2) {
+ minLon = Math.min(minLon, ring[i]);
+ maxLon = Math.max(maxLon, ring[i]);
+ minLat = Math.min(minLat, ring[i + 1]);
+ maxLat = Math.max(maxLat, ring[i + 1]);
+ }
+ }
+ if (!Number.isFinite(minLon)) return { x: WORLD_X, y: WORLD_Y };
+ // A single-point country would give a zero-width window, which cannot be
+ // mapped onto anything.
+ const padX = Math.max((maxLon - minLon) * margin, 1);
+ const padY = Math.max((maxLat - minLat) * margin, 1);
+ return {
+ x: { min: minLon - padX, max: maxLon + padX },
+ y: { min: minLat - padY, max: maxLat + padY },
+ };
+}
+
+/**
+ * The degrees under a terminal cell, given the window the map was drawn with.
+ *
+ * The inverse of what the canvas does on the way in, taken at the centre of the
+ * cell: a click lands on a whole cell, and the centre is the only point in it
+ * that is not arbitrarily nearer one neighbour than the other.
+ */
+export function degreesAt(
+ column: number,
+ row: number,
+ width: number,
+ height: number,
+ x: Bounds = WORLD_X,
+ y: Bounds = WORLD_Y,
+): { lon: number; lat: number } | undefined {
+ if (width <= 0 || height <= 0) return undefined;
+ // The canvas is 2x4 Braille pixels per cell, and it spans its bounds across
+ // `pixels - 1`, so the inverse has to use the same denominators or a click
+ // drifts from what was drawn.
+ const px = Math.max(1, width * 2 - 1);
+ const py = Math.max(1, height * 4 - 1);
+ const lon = x.min + ((column * 2 + 1) / px) * (x.max - x.min);
+ const lat = y.min + (1 - (row * 4 + 2) / py) * (y.max - y.min);
+ return { lon, lat };
+}
diff --git a/packages/hqtui/src/index.ts b/packages/hqtui/src/index.ts
index 7de353c..bb8c556 100644
--- a/packages/hqtui/src/index.ts
+++ b/packages/hqtui/src/index.ts
@@ -71,9 +71,12 @@ export {
plot, blit, sparkline, bar, gauge, donut, histogram,
verticalGlyph, horizontalGlyph, shadeGlyph, bestMode,
plotPoints, domainOf, drawCanvas, projection,
+ worldShapes, countryAt, findCountry, countryBounds, degreesAt,
+ WORLD_COUNTRIES, WORLD_X, WORLD_Y,
type Series, type PlotOptions, type FillMode,
type Point, type MarkType, type ChartSeries, type AxisOptions, type ChartPlotOptions,
type Domain, type Shape, type Bounds, type CanvasOptions, type Projection,
+ type CountryOutline, type WorldShapeOptions,
} from "./graphics/index.ts";
// Widgets (for drawing straight onto a Surface)
diff --git a/packages/hqtui/src/ui.ts b/packages/hqtui/src/ui.ts
index f35d4eb..3565a18 100644
--- a/packages/hqtui/src/ui.ts
+++ b/packages/hqtui/src/ui.ts
@@ -10,6 +10,7 @@ import { stringWidth, wrap } from "./unicode.ts";
import { isRich, toSpanLines, wrapRich, type RichText } from "./richtext.ts";
import { BrailleCanvas } from "./graphics/braille.ts";
import { drawCanvas, type CanvasOptions } from "./graphics/canvas.ts";
+import type { CountryOutline } from "./graphics/world.ts";
import * as W from "./widgets/index.ts";
export interface HitRegion {
@@ -631,6 +632,33 @@ export class Container {
return this.add((s) => drawCanvas(s, options), this.sizeOf(options, "fill"));
}
+ /**
+ * A world map, and the country under whatever gets clicked.
+ *
+ * The click is answered by turning the cell back into degrees and testing it
+ * against the outlines, so the answer is the country actually under the
+ * cursor. Bounding boxes would be cheaper and wrong: Russia's covers most of
+ * the northern hemisphere and Chile's covers Argentina.
+ */
+ worldMap(
+ options: W.WorldMapOptions & ContainerOptions & {
+ onSelect?: (country: CountryOutline | undefined) => void;
+ onHover?: (country: CountryOutline | undefined) => void;
+ } = {},
+ ): this {
+ return this.add((s) => {
+ W.drawWorldMap(s, options);
+ if (!options.onSelect && !options.onHover) return;
+ const at = (x: number, y: number) =>
+ W.countryAtCell(x, y, s.width, s.height, options);
+ this.ctx.hit({
+ rect: s.hitRect(),
+ onClick: options.onSelect ? (x, y) => options.onSelect?.(at(x, y)) : undefined,
+ onHover: options.onHover ? (x, y) => options.onHover?.(at(x, y)) : undefined,
+ });
+ }, this.sizeOf(options, "fill"));
+ }
+
/** A Braille pixel canvas sized to the region, blitted when you are done. */
canvas(
fn: (canvas: BrailleCanvas, surface: Surface) => void,
diff --git a/packages/hqtui/src/widgets/index.ts b/packages/hqtui/src/widgets/index.ts
index b0f075f..49a88f2 100644
--- a/packages/hqtui/src/widgets/index.ts
+++ b/packages/hqtui/src/widgets/index.ts
@@ -3,6 +3,7 @@ export * from "./calendar.ts";
export * from "./chart.ts";
export * from "./shadow.ts";
export * from "./surface.ts";
+export * from "./world.ts";
export * from "./scrollbar.ts";
export * from "./table.ts";
export * from "./meters.ts";
diff --git a/packages/hqtui/src/widgets/world.ts b/packages/hqtui/src/widgets/world.ts
new file mode 100644
index 0000000..e951265
--- /dev/null
+++ b/packages/hqtui/src/widgets/world.ts
@@ -0,0 +1,61 @@
+/**
+ * A world map you can click.
+ *
+ * The drawing is the canvas doing what it already does -- polylines in the
+ * caller's own coordinates, which for a map are degrees. What this adds is the
+ * other direction: turning a click back into a country.
+ */
+import type { Surface } from "../surface.ts";
+import type { Color } from "../color.ts";
+import type { Bounds } from "../graphics/canvas.ts";
+import { drawCanvas } from "../graphics/canvas.ts";
+import {
+ type CountryOutline, WORLD_X, WORLD_Y, countryAt, degreesAt, worldShapes,
+} from "../graphics/world.ts";
+
+export interface WorldMapOptions {
+ /** The window on the globe. Defaults to all of it. */
+ x?: Bounds;
+ y?: Bounds;
+ /** Coastline colour. */
+ color?: Color;
+ /** Countries to pick out, by name or ISO code. */
+ highlight?: readonly string[];
+ highlightColor?: Color;
+ background?: Color;
+ grid?: boolean;
+}
+
+export function drawWorldMap(surface: Surface, options: WorldMapOptions = {}): void {
+ if (surface.empty) return;
+ const theme = surface.theme;
+ drawCanvas(surface, {
+ shapes: worldShapes({
+ color: options.color ?? theme.border,
+ highlight: options.highlight,
+ highlightColor: options.highlightColor ?? theme.accent,
+ }),
+ x: options.x ?? WORLD_X,
+ y: options.y ?? WORLD_Y,
+ background: options.background,
+ grid: options.grid,
+ });
+}
+
+/**
+ * The country under a cell of a map drawn with these bounds.
+ *
+ * Exposed so a caller can answer a hover as well as a click, and so the
+ * arithmetic that has to agree with the drawing lives in one place.
+ */
+export function countryAtCell(
+ column: number,
+ row: number,
+ width: number,
+ height: number,
+ options: WorldMapOptions = {},
+): CountryOutline | undefined {
+ const at = degreesAt(column, row, width, height, options.x ?? WORLD_X, options.y ?? WORLD_Y);
+ if (!at) return undefined;
+ return countryAt(at.lon, at.lat);
+}
diff --git a/packages/hqtui/test/world.test.ts b/packages/hqtui/test/world.test.ts
new file mode 100644
index 0000000..022eae8
--- /dev/null
+++ b/packages/hqtui/test/world.test.ts
@@ -0,0 +1,182 @@
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import { renderToScreen } from "../src/index.ts";
+import {
+ WORLD_COUNTRIES, WORLD_X, WORLD_Y, countryAt, countryBounds, degreesAt, findCountry,
+ worldShapes,
+} from "../src/graphics/world.ts";
+import { countryAtCell } from "../src/widgets/world.ts";
+
+test("world: the data covers the countries a map is expected to have", () => {
+ assert.ok(WORLD_COUNTRIES.length > 160, `only ${WORLD_COUNTRIES.length} countries`);
+ // Sorted by name, which is what makes the overlap tie-break below stable.
+ const names = WORLD_COUNTRIES.map((c) => c.name);
+ assert.deepEqual(names, [...names].sort((a, b) => a.localeCompare(b)));
+ for (const key of ["France", "Japan", "Brazil", "Kenya", "Australia"]) {
+ assert.ok(findCountry(key), `${key} is missing`);
+ }
+ // ISO codes work as keys too, which is what an app is likely to have.
+ assert.equal(findCountry("JP")?.name, "Japan");
+ assert.equal(findCountry("jp")?.name, "Japan", "the lookup is case-insensitive");
+});
+
+test("world: every ring is a closed list of finite coordinate pairs", () => {
+ for (const country of WORLD_COUNTRIES) {
+ for (const ring of country.rings) {
+ assert.equal(ring.length % 2, 0, `${country.name}: odd coordinate count`);
+ assert.ok(ring.length >= 6, `${country.name}: a ring needs three points`);
+ for (let i = 0; i + 1 < ring.length; i += 2) {
+ assert.ok(Number.isFinite(ring[i]) && Number.isFinite(ring[i + 1]));
+ assert.ok(ring[i] >= -180.5 && ring[i] <= 180.5, `${country.name}: longitude ${ring[i]}`);
+ assert.ok(ring[i + 1] >= -90.5 && ring[i + 1] <= 90.5, `${country.name}: latitude ${ring[i + 1]}`);
+ }
+ }
+ }
+});
+
+test("world: a point lands in the country that is actually there", () => {
+ // Capitals, which is the least arguable way to check a polygon lookup.
+ const cities: [string, number, number, string][] = [
+ ["Paris", 2.35, 48.86, "France"],
+ ["Tokyo", 139.7, 35.7, "Japan"],
+ ["Cairo", 31.2, 30.0, "Egypt"],
+ ["Brasilia", -47.9, -15.8, "Brazil"],
+ ["Canberra", 149.1, -35.3, "Australia"],
+ ["Denver", -105.0, 39.7, "United States of America"],
+ ["Moscow", 37.6, 55.75, "Russia"],
+ ["Delhi", 77.2, 28.6, "India"],
+ ];
+ for (const [city, lon, lat, expected] of cities) {
+ assert.equal(countryAt(lon, lat)?.name, expected, `${city} (${lon}, ${lat})`);
+ }
+});
+
+test("world: the open ocean is not a country", () => {
+ // A bounding-box lookup would answer Russia or Chile for half of these.
+ for (const [lon, lat] of [[-140, 0], [-30, 0], [-25, -40], [80, -40], [-150, 40]]) {
+ assert.equal(countryAt(lon, lat), undefined, `(${lon}, ${lat}) claimed land`);
+ }
+});
+
+test("world: nonsense coordinates are water, not a crash", () => {
+ assert.equal(countryAt(Number.NaN, 0), undefined);
+ assert.equal(countryAt(0, Number.POSITIVE_INFINITY), undefined);
+});
+
+test("world: a cell maps back to the degrees it was drawn from", () => {
+ // If this is off by even half a cell, every click lands somewhere the user
+ // did not point at, and nothing about the map would look wrong.
+ const w = 100;
+ const h = 26;
+ for (let row = 0; row < h; row++) {
+ for (let column = 0; column < w; column++) {
+ const at = degreesAt(column, row, w, h);
+ assert.ok(at, `no degrees for (${column}, ${row})`);
+ const px = ((at.lon - WORLD_X.min) / 360) * (w * 2 - 1);
+ const py = (1 - (at.lat - WORLD_Y.min) / 180) * (h * 4 - 1);
+ assert.equal(Math.floor(px / 2), column, `column ${column} came back as ${px / 2}`);
+ assert.equal(Math.floor(py / 4), row, `row ${row} came back as ${py / 4}`);
+ }
+ }
+});
+
+test("world: an empty map has no cells to click", () => {
+ assert.equal(degreesAt(0, 0, 0, 10), undefined);
+ assert.equal(degreesAt(0, 0, 10, 0), undefined);
+});
+
+test("world: clicking a cell over a country reports that country", () => {
+ // Big enough that a country is more than one cell wide, which is the case
+ // where the answer is unambiguous.
+ const w = 200;
+ const h = 50;
+ const cellOf = (lon: number, lat: number): [number, number] => [
+ Math.round((((lon + 180) / 360) * (w * 2 - 1) - 1) / 2),
+ Math.round(((1 - (lat + 90) / 180) * (h * 4 - 1) - 2) / 4),
+ ];
+ const cases: [string, number, number][] = [
+ ["Japan", 138.0, 36.5],
+ ["Brazil", -50.0, -10.0],
+ ["Australia", 134.0, -25.0],
+ ["Egypt", 29.0, 26.0],
+ ["India", 78.0, 22.0],
+ ];
+ for (const [expected, lon, lat] of cases) {
+ const [column, row] = cellOf(lon, lat);
+ assert.equal(countryAtCell(column, row, w, h)?.name, expected, `${expected} at (${lon}, ${lat})`);
+ }
+});
+
+test("world: zooming changes which country a given cell holds", () => {
+ // The same cell over two different windows is two different places, which is
+ // the whole reason the lookup takes the bounds.
+ const japan = countryBounds(findCountry("Japan")!);
+ // A cell that is Honshu once the map is zoomed to Japan, and Russia when the
+ // same map shows the whole globe.
+ const cell = { column: 74, row: 2 };
+ const whole = countryAtCell(cell.column, cell.row, 100, 26);
+ const zoomed = countryAtCell(cell.column, cell.row, 100, 26, japan);
+ assert.equal(zoomed?.name, "Japan");
+ assert.equal(whole?.name, "Russia");
+});
+
+test("world: a country's window contains it, with room around it", () => {
+ const japan = findCountry("Japan")!;
+ const bounds = countryBounds(japan);
+ let minLon = Infinity;
+ let maxLon = -Infinity;
+ for (const ring of japan.rings) {
+ for (let i = 0; i + 1 < ring.length; i += 2) {
+ minLon = Math.min(minLon, ring[i]);
+ maxLon = Math.max(maxLon, ring[i]);
+ }
+ }
+ assert.ok(bounds.x.min < minLon, "no margin on the left");
+ assert.ok(bounds.x.max > maxLon, "no margin on the right");
+});
+
+test("world: shapes are closed rings, so a coastline has no seam", () => {
+ const shapes = worldShapes();
+ assert.ok(shapes.length > 200, `only ${shapes.length} shapes`);
+ for (const shape of shapes) {
+ assert.equal(shape.type, "polyline");
+ if (shape.type !== "polyline") continue;
+ const first = shape.points[0];
+ const last = shape.points[shape.points.length - 1];
+ assert.deepEqual(first, last, "a ring that does not close leaves a gap");
+ }
+});
+
+test("world: highlighted countries are drawn in their own colour", () => {
+ const shapes = worldShapes({ color: 0x111111, highlight: ["JP"], highlightColor: 0x222222 });
+ const colours = new Set(shapes.map((s) => s.color));
+ assert.deepEqual([...colours].sort(), [0x111111, 0x222222]);
+});
+
+test("world: the map draws, and draws differently when zoomed", () => {
+ const whole = renderToScreen(({ ui }) => ui.worldMap({}), { width: 60, height: 16 }).text();
+ assert.ok(whole.trim().length > 0, "nothing was drawn");
+ const japan = countryBounds(findCountry("Japan")!);
+ const zoomed = renderToScreen(({ ui }) => ui.worldMap(japan), { width: 60, height: 16 }).text();
+ assert.notEqual(whole, zoomed);
+});
+
+test("world: a map with no handlers registers nothing to click", () => {
+ const plain = renderToScreen(({ ui }) => ui.worldMap({}), { width: 40, height: 12 });
+ assert.equal(plain.regions.length, 0);
+
+ let picked: string | undefined;
+ const live = renderToScreen(
+ ({ ui }) => ui.worldMap({ onSelect: (c) => { picked = c?.name; } }),
+ { width: 200, height: 50 },
+ );
+ assert.equal(live.regions.length, 1);
+ // A click over Australia comes back as Australia.
+ live.regions[0].onClick?.(173, 28, "left");
+ assert.equal(picked, "Australia");
+
+ // And a click on open water comes back as nothing, rather than the nearest
+ // bounding box.
+ live.regions[0].onClick?.(20, 25, "left");
+ assert.equal(picked, undefined);
+});
diff --git a/ports/conformance/fixtures/widgets.json b/ports/conformance/fixtures/widgets.json
index e0b7877..be6dbd2 100644
--- a/ports/conformance/fixtures/widgets.json
+++ b/ports/conformance/fixtures/widgets.json
@@ -5264,6 +5264,5706 @@
]
}
},
+ {
+ "name": "world",
+ "width": 60,
+ "height": 16,
+ "result": {
+ "width": 60,
+ "height": 16,
+ "chars": [
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 3,
+ 10432
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 5,
+ 10432
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10468
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 6,
+ 10432
+ ],
+ [
+ 1,
+ 10422
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10402
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10334
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10388
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10294
+ ],
+ [
+ 2,
+ 10276
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 5,
+ 10432
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10435
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10428
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 2,
+ 10249
+ ],
+ [
+ 2,
+ 10241
+ ],
+ [
+ 1,
+ 10337
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10300
+ ],
+ [
+ 1,
+ 10255
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10384
+ ],
+ [
+ 1,
+ 10413
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 1,
+ 10424
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 10,
+ 32
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10468
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10302
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 2,
+ 10276
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10297
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10295
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10416
+ ],
+ [
+ 1,
+ 10450
+ ],
+ [
+ 1,
+ 10322
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10322
+ ],
+ [
+ 1,
+ 10290
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 3,
+ 10276
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 2,
+ 10276
+ ],
+ [
+ 1,
+ 10260
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 1,
+ 10329
+ ],
+ [
+ 1,
+ 10486
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 12,
+ 32
+ ],
+ [
+ 1,
+ 10275
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10349
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 8,
+ 32
+ ],
+ [
+ 1,
+ 10425
+ ],
+ [
+ 1,
+ 10482
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 2,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 1,
+ 10288
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10285
+ ],
+ [
+ 1,
+ 10358
+ ],
+ [
+ 2,
+ 10242
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 15,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10430
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10311
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10454
+ ],
+ [
+ 1,
+ 10453
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 18,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 10294
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 1,
+ 10247
+ ],
+ [
+ 1,
+ 10470
+ ],
+ [
+ 1,
+ 10294
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10324
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10275
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10410
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 24,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10427
+ ],
+ [
+ 1,
+ 10477
+ ],
+ [
+ 2,
+ 10242
+ ],
+ [
+ 5,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 10268
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10468
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10291
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 24,
+ 32
+ ],
+ [
+ 1,
+ 10416
+ ],
+ [
+ 1,
+ 10298
+ ],
+ [
+ 1,
+ 10307
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10386
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 2,
+ 10368
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10402
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10261
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 1,
+ 10299
+ ],
+ [
+ 1,
+ 10359
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10288
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 20,
+ 32
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10332
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10388
+ ],
+ [
+ 1,
+ 10310
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10452
+ ],
+ [
+ 1,
+ 10247
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 2,
+ 10250
+ ],
+ [
+ 1,
+ 10283
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 1,
+ 10374
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 19,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10319
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10323
+ ],
+ [
+ 1,
+ 10465
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 10,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10247
+ ],
+ [
+ 21,
+ 32
+ ],
+ [
+ 1,
+ 10424
+ ],
+ [
+ 1,
+ 10311
+ ],
+ [
+ 1,
+ 10356
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 11,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 15,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 17,
+ 32
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 20,
+ 32
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 15,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 19,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 3,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 4,
+ 10432
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 7,
+ 10276
+ ],
+ [
+ 1,
+ 10292
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 3,
+ 10258
+ ],
+ [
+ 1,
+ 10486
+ ],
+ [
+ 1,
+ 10302
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 7,
+ 10249
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 11,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10377
+ ],
+ [
+ 1,
+ 10486
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10454
+ ],
+ [
+ 3,
+ 10450
+ ],
+ [
+ 2,
+ 10459
+ ],
+ [
+ 1,
+ 10434
+ ],
+ [
+ 11,
+ 10432
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 1,
+ 10449
+ ],
+ [
+ 2,
+ 10450
+ ],
+ [
+ 1,
+ 10442
+ ],
+ [
+ 2,
+ 10441
+ ],
+ [
+ 1,
+ 10433
+ ],
+ [
+ 30,
+ 10432
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 2,
+ 10450
+ ],
+ [
+ 1,
+ 10482
+ ]
+ ],
+ "fg": [
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 14,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 20,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 32,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 10,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 9,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 10,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 14,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 26,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 12,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 8,
+ 29806811
+ ],
+ [
+ 16,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 10,
+ 19148864
+ ],
+ [
+ 15,
+ 29806811
+ ],
+ [
+ 8,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 17,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 18,
+ 29806811
+ ],
+ [
+ 9,
+ 19148864
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 13,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 10,
+ 19148864
+ ],
+ [
+ 24,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 5,
+ 29806811
+ ],
+ [
+ 12,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 24,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 6,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 11,
+ 19148864
+ ],
+ [
+ 20,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 8,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 19,
+ 29806811
+ ],
+ [
+ 6,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 10,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 21,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 11,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 15,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 17,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 20,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 15,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 19,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 12,
+ 19148864
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 18,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 11,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 11,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 60,
+ 19148864
+ ]
+ ],
+ "bg": [
+ [
+ 960,
+ 17106698
+ ]
+ ],
+ "attrs": [
+ [
+ 960,
+ 0
+ ]
+ ],
+ "clusters": [],
+ "text": [
+ " ⢀⣀⣀⣀⢀⣀⣀⣀⣀⣀⢤⣤⣀⣀ ⢀⣀⣀ ⣀⣀ ⣀ ",
+ "⣄ ⢀⣀⣀⣀⣀⣀⣀⢶⢤⡤⠤⢀⡀⠛⠒⠈⠛⠓⢢⣄ ⢠⡞ ⠈⢀⡀⢀⣀ ⡀⠠⢔⠒⠂⣄⠤⠒⠒⠉⠁⠶⠤⠤⣀⠐⠤⠒⣀⣀⣀⣀⣀",
+ "⠋⠐⠂⣃⣀⡤⢼⣀⡀ ⠉⠉⠁⠁⡡⠐⠒⠉⠼⠏ ⠓⠤⠒⠉⠐⠚⠛ ⣀⢠⢐⢭⠎⢸⠛⠋⠉ ⠉⠈⠉⠁ ⣀⣀⣠⣤⣄⣀⠾",
+ " ⠈⠉⠁ ⠉⣠⠤⠤⢄⣀⣀⡀⠹⠃⢀⣀⣠⡄ ⠈⠷⠆⣀⠉⢰⣒⡒⢤⢠⡒⠲⠖⠉⠚⠤⠤⠤⠖⠤⠤⠔⠢⢄⡙⣶ ⠸⠊ ",
+ " ⠣⡀ ⠈⠓⡭⠋⠉ ⢹⣲⠂⢠⠈⠙⠈⠈⠁⠆⠰⠛⠭⡶⠂⠂ ⠑⠒⠒⠉⢤⢠⡄⠉⠛⠁ ",
+ " ⠈⠉⠑⠢⠒⠒⢾⡀ ⢀⡤⠊ ⠙⡀⠋⡇⠈⠁⠒⢀⡀⣖⣕⠁⠒⠤⢀⡄⡀ ⢀⡀⠈⠁ ",
+ " ⠈⠑⠚⠶⢀⡄⠂⡀⠂ ⠉⡀⠆⢀⣀⡀⢠⠋⣉⠇⣦⠶⠁ ⠘⡄⡔⠊⠣⠈⢪⡀⠈⢀ ",
+ " ⠈⠙⠈⢻⣭⠂⠂ ⠈⠃⠁⠃⠜⡀⠈⠋⣤⢀⠙⠁ ⠘⠓ ⠠⠳⠉⣠⠊⠈⡀ ",
+ " ⢰⠺⡃⡀ ⠈⠉⢒⠆ ⠑⠁⢀⢀⡄⢢ ⠑⠕⠤⠄⢀⠄⠻⡷⠐⠰⠄ ",
+ " ⠓⠎⢠⡀ ⡜ ⢠⠤⠸⢔⡆⠁⣔⠇ ⢀⠤⠊⠊⠫⠎⢆ ⣀ ",
+ " ⢀⡏⠁⣄⡀⠉ ⠠⡓⣡⠃ ⠈ ⠘⡄⢀⣀⢀ ⢀⠇ ",
+ " ⢸⡇⡴⠋⠁ ⠉ ⠈⠁ ⠉⠒⠚ ⡠⠖",
+ " ⠸⠤⠠⠄ ⠒ ⠈⠁ ",
+ " ⢀⣠⠄ ⣀ ⢀⣀⣀⣀⡀ ⢀⣀⣀⠤⣀⣀⣀⣀⠤⢄⣀⣀ ",
+ " ⢀⠠⣀⠤⠤⠤⠤⠤⠤⠤⠴⠖⠒⠒⠒⣶⠾⠚ ⢀⣀ ⣀⡠⠤⠖⠉⠉⠉⠉⠉⠉⠉ ⠉⠁ ⠚⠉⠁ ⠉⢉⣶⠂ ",
+ "⣖⣒⣒⣒⣛⣛⣂⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣉⣑⣒⣒⣊⣉⣉⣁⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣉⣒⣒⣲"
+ ]
+ }
+ },
+ {
+ "name": "world-zoom",
+ "width": 40,
+ "height": 12,
+ "result": {
+ "width": 40,
+ "height": 12,
+ "chars": [
+ [
+ 6,
+ 10258
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10332
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10310
+ ],
+ [
+ 1,
+ 10326
+ ],
+ [
+ 1,
+ 10293
+ ],
+ [
+ 13,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 1,
+ 10293
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 2,
+ 10249
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10439
+ ],
+ [
+ 1,
+ 10488
+ ],
+ [
+ 11,
+ 32
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 13,
+ 32
+ ],
+ [
+ 1,
+ 10326
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10352
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 24,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10279
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10480
+ ],
+ [
+ 1,
+ 10277
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10260
+ ],
+ [
+ 1,
+ 10362
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10326
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10325
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10332
+ ],
+ [
+ 1,
+ 10402
+ ],
+ [
+ 18,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 1,
+ 10285
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10260
+ ],
+ [
+ 1,
+ 10290
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 18,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10467
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 20,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10299
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10292
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 1,
+ 10260
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 23,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10310
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10275
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 30,
+ 32
+ ],
+ [
+ 1,
+ 10424
+ ],
+ [
+ 38,
+ 32
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 36,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10268
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 30,
+ 32
+ ]
+ ],
+ "fg": [
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 13,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 11,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 13,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 24,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 6,
+ 19148864
+ ],
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 11,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 18,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 18,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 20,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 12,
+ 19148864
+ ],
+ [
+ 23,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 30,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 38,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 36,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 30,
+ 29806811
+ ]
+ ],
+ "bg": [
+ [
+ 480,
+ 17106698
+ ]
+ ],
+ "attrs": [
+ [
+ 480,
+ 0
+ ]
+ ],
+ "clusters": [],
+ "text": [
+ "⠒⠒⠒⠒⠒⠒⠁ ⠈⠢⢄ ⣀⣀ ⡜⠈⡆⡖⠵ ",
+ " ⠈⣉⠵⠖⠂ ⠉⠉⢀⠎ ⡠⠊ ⣇⣸ ",
+ "⠤⠒⠊⠉ ⡖⠒⠊ ⡠⠊ ⡰⠑⠢⢄ ",
+ " ⢀⣀⡤⠧⠒⠒⠉ ⣰⠥⠤⠒⠊⠁ ",
+ " ⢀⡠⠔⡺⣀⡠⠤⡖⠉⡕⠉ ⡜⢢ ",
+ " ⠉⠒⠢⠭⣀ ⠸⢠⠔⠲⡀ ⣀⠤⠚ ⢠⠃ ",
+ " ⢀⠤⠊⠁ ⠘⡄⢀⣣ ⣀⠤⠒⠉ ⣠⠃ ",
+ " ⠑⡄ ⠉⠁⢠⠒⠻⠠⠴⠒⠢⠔⠒⠉ ",
+ " ⠈⡆ ⠣⠊ ",
+ " ⢸ ",
+ " ⡠⠃ ",
+ " ⣀⠜ ⣀⡄ "
+ ]
+ }
+ },
+ {
+ "name": "world-highlight",
+ "width": 60,
+ "height": 16,
+ "result": {
+ "width": 60,
+ "height": 16,
+ "chars": [
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 3,
+ 10432
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 5,
+ 10432
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10468
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 6,
+ 10432
+ ],
+ [
+ 1,
+ 10422
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10402
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10334
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10388
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10294
+ ],
+ [
+ 2,
+ 10276
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 5,
+ 10432
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10435
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10428
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 2,
+ 10249
+ ],
+ [
+ 2,
+ 10241
+ ],
+ [
+ 1,
+ 10337
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10300
+ ],
+ [
+ 1,
+ 10255
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10384
+ ],
+ [
+ 1,
+ 10413
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 1,
+ 10424
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 10,
+ 32
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10468
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10302
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 2,
+ 10276
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10297
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10295
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10416
+ ],
+ [
+ 1,
+ 10450
+ ],
+ [
+ 1,
+ 10322
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10322
+ ],
+ [
+ 1,
+ 10290
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 3,
+ 10276
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 2,
+ 10276
+ ],
+ [
+ 1,
+ 10260
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 1,
+ 10329
+ ],
+ [
+ 1,
+ 10486
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 12,
+ 32
+ ],
+ [
+ 1,
+ 10275
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 4,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10349
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 8,
+ 32
+ ],
+ [
+ 1,
+ 10425
+ ],
+ [
+ 1,
+ 10482
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 2,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 1,
+ 10288
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10285
+ ],
+ [
+ 1,
+ 10358
+ ],
+ [
+ 2,
+ 10242
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10404
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10267
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 15,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10274
+ ],
+ [
+ 2,
+ 10258
+ ],
+ [
+ 1,
+ 10430
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10340
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10311
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10454
+ ],
+ [
+ 1,
+ 10453
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 18,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 10294
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 1,
+ 10247
+ ],
+ [
+ 1,
+ 10470
+ ],
+ [
+ 1,
+ 10294
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10324
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10275
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10410
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 24,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10427
+ ],
+ [
+ 1,
+ 10477
+ ],
+ [
+ 2,
+ 10242
+ ],
+ [
+ 5,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 10268
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10468
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10265
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10291
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10250
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 24,
+ 32
+ ],
+ [
+ 1,
+ 10416
+ ],
+ [
+ 1,
+ 10298
+ ],
+ [
+ 1,
+ 10307
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10386
+ ],
+ [
+ 1,
+ 10246
+ ],
+ [
+ 6,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 2,
+ 10368
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10402
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10257
+ ],
+ [
+ 1,
+ 10261
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 1,
+ 10299
+ ],
+ [
+ 1,
+ 10359
+ ],
+ [
+ 1,
+ 10256
+ ],
+ [
+ 1,
+ 10288
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 20,
+ 32
+ ],
+ [
+ 1,
+ 10259
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10332
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10400
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10388
+ ],
+ [
+ 1,
+ 10310
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10452
+ ],
+ [
+ 1,
+ 10247
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 2,
+ 10250
+ ],
+ [
+ 1,
+ 10283
+ ],
+ [
+ 1,
+ 10254
+ ],
+ [
+ 1,
+ 10374
+ ],
+ [
+ 2,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 19,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10319
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 10436
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 9,
+ 32
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10323
+ ],
+ [
+ 1,
+ 10465
+ ],
+ [
+ 1,
+ 10243
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 10,
+ 32
+ ],
+ [
+ 1,
+ 10264
+ ],
+ [
+ 1,
+ 10308
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10247
+ ],
+ [
+ 21,
+ 32
+ ],
+ [
+ 1,
+ 10424
+ ],
+ [
+ 1,
+ 10311
+ ],
+ [
+ 1,
+ 10356
+ ],
+ [
+ 1,
+ 10251
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 11,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 15,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 17,
+ 32
+ ],
+ [
+ 1,
+ 10296
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 20,
+ 32
+ ],
+ [
+ 1,
+ 10258
+ ],
+ [
+ 15,
+ 32
+ ],
+ [
+ 1,
+ 10248
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 19,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10464
+ ],
+ [
+ 1,
+ 10244
+ ],
+ [
+ 14,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 3,
+ 10432
+ ],
+ [
+ 1,
+ 10304
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 4,
+ 10432
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10372
+ ],
+ [
+ 2,
+ 10432
+ ],
+ [
+ 7,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10272
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 7,
+ 10276
+ ],
+ [
+ 1,
+ 10292
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 3,
+ 10258
+ ],
+ [
+ 1,
+ 10486
+ ],
+ [
+ 1,
+ 10302
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10368
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10432
+ ],
+ [
+ 1,
+ 10336
+ ],
+ [
+ 1,
+ 10276
+ ],
+ [
+ 1,
+ 10262
+ ],
+ [
+ 7,
+ 10249
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 3,
+ 32
+ ],
+ [
+ 1,
+ 10266
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10241
+ ],
+ [
+ 11,
+ 32
+ ],
+ [
+ 1,
+ 10249
+ ],
+ [
+ 1,
+ 10377
+ ],
+ [
+ 1,
+ 10486
+ ],
+ [
+ 1,
+ 10242
+ ],
+ [
+ 1,
+ 32
+ ],
+ [
+ 1,
+ 10454
+ ],
+ [
+ 3,
+ 10450
+ ],
+ [
+ 2,
+ 10459
+ ],
+ [
+ 1,
+ 10434
+ ],
+ [
+ 11,
+ 10432
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 1,
+ 10449
+ ],
+ [
+ 2,
+ 10450
+ ],
+ [
+ 1,
+ 10442
+ ],
+ [
+ 2,
+ 10441
+ ],
+ [
+ 1,
+ 10433
+ ],
+ [
+ 30,
+ 10432
+ ],
+ [
+ 1,
+ 10441
+ ],
+ [
+ 2,
+ 10450
+ ],
+ [
+ 1,
+ 10482
+ ]
+ ],
+ "fg": [
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 14,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 20,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 32,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 10,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 9,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 10,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 14,
+ 19148864
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 26,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 12,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 4,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 8,
+ 29806811
+ ],
+ [
+ 16,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 8,
+ 19148864
+ ],
+ [
+ 2,
+ 22467805
+ ],
+ [
+ 15,
+ 29806811
+ ],
+ [
+ 8,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 17,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 2,
+ 22467805
+ ],
+ [
+ 18,
+ 29806811
+ ],
+ [
+ 9,
+ 19148864
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 13,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 10,
+ 19148864
+ ],
+ [
+ 24,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 5,
+ 29806811
+ ],
+ [
+ 12,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 24,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 1,
+ 22467805
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 4,
+ 22467805
+ ],
+ [
+ 6,
+ 29806811
+ ],
+ [
+ 6,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 11,
+ 19148864
+ ],
+ [
+ 20,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 1,
+ 22467805
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 8,
+ 19148864
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 7,
+ 19148864
+ ],
+ [
+ 2,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 19,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 22467805
+ ],
+ [
+ 9,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 10,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 21,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 11,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 15,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 17,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 20,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 15,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 19,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 14,
+ 29806811
+ ],
+ [
+ 1,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 5,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 12,
+ 19148864
+ ],
+ [
+ 7,
+ 29806811
+ ],
+ [
+ 18,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 11,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 2,
+ 19148864
+ ],
+ [
+ 3,
+ 29806811
+ ],
+ [
+ 3,
+ 19148864
+ ],
+ [
+ 11,
+ 29806811
+ ],
+ [
+ 4,
+ 19148864
+ ],
+ [
+ 1,
+ 29806811
+ ],
+ [
+ 60,
+ 19148864
+ ]
+ ],
+ "bg": [
+ [
+ 960,
+ 17106698
+ ]
+ ],
+ "attrs": [
+ [
+ 960,
+ 0
+ ]
+ ],
+ "clusters": [],
+ "text": [
+ " ⢀⣀⣀⣀⢀⣀⣀⣀⣀⣀⢤⣤⣀⣀ ⢀⣀⣀ ⣀⣀ ⣀ ",
+ "⣄ ⢀⣀⣀⣀⣀⣀⣀⢶⢤⡤⠤⢀⡀⠛⠒⠈⠛⠓⢢⣄ ⢠⡞ ⠈⢀⡀⢀⣀ ⡀⠠⢔⠒⠂⣄⠤⠒⠒⠉⠁⠶⠤⠤⣀⠐⠤⠒⣀⣀⣀⣀⣀",
+ "⠋⠐⠂⣃⣀⡤⢼⣀⡀ ⠉⠉⠁⠁⡡⠐⠒⠉⠼⠏ ⠓⠤⠒⠉⠐⠚⠛ ⣀⢠⢐⢭⠎⢸⠛⠋⠉ ⠉⠈⠉⠁ ⣀⣀⣠⣤⣄⣀⠾",
+ " ⠈⠉⠁ ⠉⣠⠤⠤⢄⣀⣀⡀⠹⠃⢀⣀⣠⡄ ⠈⠷⠆⣀⠉⢰⣒⡒⢤⢠⡒⠲⠖⠉⠚⠤⠤⠤⠖⠤⠤⠔⠢⢄⡙⣶ ⠸⠊ ",
+ " ⠣⡀ ⠈⠓⡭⠋⠉ ⢹⣲⠂⢠⠈⠙⠈⠈⠁⠆⠰⠛⠭⡶⠂⠂ ⠑⠒⠒⠉⢤⢠⡄⠉⠛⠁ ",
+ " ⠈⠉⠑⠢⠒⠒⢾⡀ ⢀⡤⠊ ⠙⡀⠋⡇⠈⠁⠒⢀⡀⣖⣕⠁⠒⠤⢀⡄⡀ ⢀⡀⠈⠁ ",
+ " ⠈⠑⠚⠶⢀⡄⠂⡀⠂ ⠉⡀⠆⢀⣀⡀⢠⠋⣉⠇⣦⠶⠁ ⠘⡄⡔⠊⠣⠈⢪⡀⠈⢀ ",
+ " ⠈⠙⠈⢻⣭⠂⠂ ⠈⠃⠁⠃⠜⡀⠈⠋⣤⢀⠙⠁ ⠘⠓ ⠠⠳⠉⣠⠊⠈⡀ ",
+ " ⢰⠺⡃⡀ ⠈⠉⢒⠆ ⠑⠁⢀⢀⡄⢢ ⠑⠕⠤⠄⢀⠄⠻⡷⠐⠰⠄ ",
+ " ⠓⠎⢠⡀ ⡜ ⢠⠤⠸⢔⡆⠁⣔⠇ ⢀⠤⠊⠊⠫⠎⢆ ⣀ ",
+ " ⢀⡏⠁⣄⡀⠉ ⠠⡓⣡⠃ ⠈ ⠘⡄⢀⣀⢀ ⢀⠇ ",
+ " ⢸⡇⡴⠋⠁ ⠉ ⠈⠁ ⠉⠒⠚ ⡠⠖",
+ " ⠸⠤⠠⠄ ⠒ ⠈⠁ ",
+ " ⢀⣠⠄ ⣀ ⢀⣀⣀⣀⡀ ⢀⣀⣀⠤⣀⣀⣀⣀⠤⢄⣀⣀ ",
+ " ⢀⠠⣀⠤⠤⠤⠤⠤⠤⠤⠴⠖⠒⠒⠒⣶⠾⠚ ⢀⣀ ⣀⡠⠤⠖⠉⠉⠉⠉⠉⠉⠉ ⠉⠁ ⠚⠉⠁ ⠉⢉⣶⠂ ",
+ "⣖⣒⣒⣒⣛⣛⣂⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣉⣑⣒⣒⣊⣉⣉⣁⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣉⣒⣒⣲"
+ ]
+ }
+ },
{
"name": "badge",
"width": 20,
diff --git a/ports/conformance/generate.ts b/ports/conformance/generate.ts
index 34222fb..5ec0f68 100644
Binary files a/ports/conformance/generate.ts and b/ports/conformance/generate.ts differ
diff --git a/ports/cpp/CMakeLists.txt b/ports/cpp/CMakeLists.txt
index bf46e4e..507b29c 100644
--- a/ports/cpp/CMakeLists.txt
+++ b/ports/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@ if(HQTUI_LTO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
endif()
add_library(hqtui_cpp INTERFACE)
-add_library(hqtui_cpp_widgets src/widgets.cpp src/scrollbar.cpp src/chart.cpp src/calendar.cpp src/canvas.cpp src/shadow.cpp)
+add_library(hqtui_cpp_widgets src/widgets.cpp src/scrollbar.cpp src/chart.cpp src/calendar.cpp src/canvas.cpp src/shadow.cpp src/world.cpp src/world_data.cpp)
set_target_properties(hqtui_cpp_widgets PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_features(hqtui_cpp_widgets PUBLIC cxx_std_17)
if(MSVC)
@@ -71,6 +71,8 @@ if(UNIX)
# keeps the published C++ snippets true.
add_executable(hqtui-widgets examples/widgets.cpp)
target_link_libraries(hqtui-widgets PRIVATE hqtui::cpp)
+ add_executable(hqtui-world-probe examples/world-probe.cpp)
+ target_link_libraries(hqtui-world-probe PRIVATE hqtui::cpp)
endif()
if(BUILD_TESTING)
find_package(Python3 COMPONENTS Interpreter)
diff --git a/ports/cpp/examples/world-probe.cpp b/ports/cpp/examples/world-probe.cpp
new file mode 100644
index 0000000..22b14cd
--- /dev/null
+++ b/ports/cpp/examples/world-probe.cpp
@@ -0,0 +1,45 @@
+/// Prints what the country lookup answers for a fixed set of points.
+///
+/// The same probe exists for every port, so "the ports agree about the world" is
+/// a diff rather than a hope.
+#include
+#include
+
+using namespace hqtui;
+
+int main() {
+ struct Place {
+ const char *name;
+ double lon, lat;
+ };
+ const Place places[] = {
+ {"Paris", 2.35, 48.86}, {"Tokyo", 139.7, 35.7},
+ {"Cairo", 31.2, 30.0}, {"Brasilia", -47.9, -15.8},
+ {"Canberra", 149.1, -35.3}, {"Denver", -105.0, 39.7},
+ {"Moscow", 37.6, 55.75}, {"Delhi", 77.2, 28.6},
+ {"Nairobi", 36.8, -1.3}, {"Pacific", -140.0, 0.0},
+ {"Atlantic", -30.0, 0.0}, {"SouthernOcean", 80.0, -40.0},
+ {"NorthPacific", -150.0, 40.0},
+ };
+ for (auto &place : places) {
+ const CountryOutline *found = country_at(place.lon, place.lat);
+ std::printf("%s %s\n", place.name, found ? found->name.c_str() : "-");
+ }
+
+ // The cell path, which has to agree with what the canvas drew.
+ const int cells[][2] = {{173, 28}, {74, 2}, {20, 25}, {88, 7}};
+ for (auto &cell : cells) {
+ const CountryOutline *found = country_at_cell(cell[0], cell[1], 200, 50);
+ std::printf("cell:%d,%d %s\n", cell[0], cell[1], found ? found->name.c_str() : "-");
+ }
+
+ // And the projection itself, so a drift shows up as a number rather than as a
+ // country that happens to still be right.
+ const int probes[][2] = {{0, 0}, {99, 25}, {50, 13}};
+ for (auto &cell : probes) {
+ double lon = 0, lat = 0;
+ degrees_at(cell[0], cell[1], 100, 26, Bounds{-180, 180}, Bounds{-90, 90}, &lon, &lat);
+ std::printf("degrees:%d,%d %.4f %.4f\n", cell[0], cell[1], lon, lat);
+ }
+ return 0;
+}
diff --git a/ports/cpp/include/hqtui/widgets.hpp b/ports/cpp/include/hqtui/widgets.hpp
index a5edc9c..e9f18e7 100644
--- a/ports/cpp/include/hqtui/widgets.hpp
+++ b/ports/cpp/include/hqtui/widgets.hpp
@@ -592,6 +592,51 @@ Projection canvas_projection(const Braille &, Bounds x, Bounds y);
/// A canvas drawn in the caller's own coordinates rather than in pixels.
void draw_canvas(Surface, const Canvas &);
+struct CountryOutline {
+ std::string name;
+ /// ISO 3166-1 alpha-2, where Natural Earth has one.
+ std::string iso;
+ /// Longitude and latitude, interleaved. More than one ring means more than
+ /// one landmass.
+ std::vector> rings;
+};
+/// The country outlines, built once on first use.
+const std::vector &world_countries();
+struct WorldShapeOptions {
+ /// Colour for countries with nothing special about them.
+ Color color = 0;
+ /// Countries to pick out, by name or ISO code.
+ std::vector highlight;
+ Color highlight_color = 0;
+};
+/// The world as canvas shapes, one closed polyline per landmass.
+std::vector world_shapes(const WorldShapeOptions & = {});
+/// The country containing a point, or null for open water.
+const CountryOutline *country_at(double lon, double lat);
+/// Look a country up by name or ISO code.
+const CountryOutline *find_country(std::string_view key);
+/// The window a country fills, with a little room around it.
+void country_bounds(const CountryOutline &, double margin, Bounds *x, Bounds *y);
+/// The degrees under a terminal cell, given the window the map was drawn with.
+bool degrees_at(int column, int row, int width, int height, Bounds x, Bounds y, double *lon,
+ double *lat);
+struct WorldMap {
+ /// The window on the globe. Unset means all of it.
+ std::optional x, y;
+ /// Coastline colour.
+ Color color = 0;
+ /// Countries to pick out, by name or ISO code.
+ std::vector highlight;
+ Color highlight_color = 0;
+ std::optional background;
+ bool grid = false;
+};
+/// A world map drawn in degrees.
+void draw_world_map(Surface, const WorldMap & = {});
+/// The country under a cell of a map drawn with these bounds.
+const CountryOutline *country_at_cell(int column, int row, int width, int height,
+ const WorldMap & = {});
+
struct Shadow {
/// How far the shadow falls.
int offset_x = 1, offset_y = 1;
@@ -1075,6 +1120,10 @@ class UI {
void shapes(Canvas o, Constraint size = fr()) {
draw([=](Surface s) { draw_canvas(s, o); }, size);
}
+ /// A world map, and the country under whatever gets clicked.
+ void world_map(WorldMap o, Constraint size = fr()) {
+ draw([=](Surface s) { draw_world_map(s, o); }, size);
+ }
void sparkline(Sparkline o) {
draw([=](Surface s) { draw_sparkline(s, o); }, cells(1));
}
diff --git a/ports/cpp/src/world.cpp b/ports/cpp/src/world.cpp
new file mode 100644
index 0000000..f2b1f36
--- /dev/null
+++ b/ports/cpp/src/world.cpp
@@ -0,0 +1,169 @@
+/// The world, as shapes for the canvas, and the lookup that makes it clickable.
+///
+/// The canvas already draws in the caller's own coordinates, and longitude and
+/// latitude are just another pair of axes -- so a map is a list of polylines in
+/// degrees, and nothing here needs a projection of its own beyond deciding
+/// which window on the globe to show.
+///
+/// The interesting half 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 testing it against the outlines.
+/// Doing it that way rather than with bounding boxes is what makes the answer
+/// right: Russia's bounding box covers most of the northern hemisphere, and
+/// Chile's covers Argentina.
+#include
+
+namespace hqtui {
+namespace {
+
+bool equals_ignoring_case(std::string_view a, std::string_view b) {
+ if (a.size() != b.size())
+ return false;
+ for (std::size_t i = 0; i < a.size(); i++)
+ if (std::tolower((unsigned char)a[i]) != std::tolower((unsigned char)b[i]))
+ return false;
+ return true;
+}
+
+/// Match on either the name or the ISO code, case-insensitively.
+bool matches(const CountryOutline &country, const std::vector &keys) {
+ for (auto &key : keys) {
+ if (key.empty())
+ continue;
+ if (equals_ignoring_case(country.name, key))
+ return true;
+ if (!country.iso.empty() && equals_ignoring_case(country.iso, key))
+ return true;
+ }
+ return false;
+}
+
+/// Whether a point is inside a ring, by ray casting.
+///
+/// The ring is a flat list of interleaved coordinates, so this walks it two at a
+/// time rather than allocating a pair per vertex -- it runs once per country per
+/// click, and there are a couple of thousand vertices.
+bool inside_ring(const std::vector &ring, double lon, double lat) {
+ bool inside = false;
+ std::size_t n = ring.size() / 2;
+ if (n == 0)
+ return false;
+ std::size_t j = n - 1;
+ for (std::size_t i = 0; i < n; i++) {
+ double xi = ring[i * 2], yi = ring[i * 2 + 1];
+ double xj = ring[j * 2], yj = ring[j * 2 + 1];
+ if ((yi > lat) != (yj > lat) && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi)
+ inside = !inside;
+ j = i;
+ }
+ return inside;
+}
+
+} // namespace
+
+std::vector world_shapes(const WorldShapeOptions &o) {
+ std::vector shapes;
+ for (auto &country : world_countries()) {
+ bool picked = !o.highlight.empty() && matches(country, o.highlight);
+ Color color = picked && o.highlight_color ? o.highlight_color : o.color;
+ for (auto &ring : country.rings) {
+ Shape shape;
+ shape.kind = HQ_SHAPE_POLYLINE;
+ shape.color = color;
+ std::size_t n = ring.size() / 2;
+ shape.points.reserve(n + 1);
+ for (std::size_t i = 0; i < n; i++)
+ shape.points.push_back({ring[i * 2], ring[i * 2 + 1]});
+ // Closed: the last point joins the first, or every country has a gap in
+ // its coastline where the ring started.
+ if (n > 0)
+ shape.points.push_back(shape.points.front());
+ shapes.push_back(std::move(shape));
+ }
+ }
+ return shapes;
+}
+
+const CountryOutline *country_at(double lon, double lat) {
+ if (!std::isfinite(lon) || !std::isfinite(lat))
+ return nullptr;
+ for (auto &country : world_countries())
+ for (auto &ring : country.rings)
+ if (inside_ring(ring, lon, lat))
+ return &country;
+ return nullptr;
+}
+
+const CountryOutline *find_country(std::string_view key) {
+ std::vector keys{std::string(key)};
+ for (auto &country : world_countries())
+ if (matches(country, keys))
+ return &country;
+ return nullptr;
+}
+
+void country_bounds(const CountryOutline &country, double margin, Bounds *x, Bounds *y) {
+ double min_lon = INFINITY, max_lon = -INFINITY;
+ double min_lat = INFINITY, max_lat = -INFINITY;
+ for (auto &ring : country.rings)
+ for (std::size_t i = 0; i + 1 < ring.size(); i += 2) {
+ min_lon = std::min(min_lon, ring[i]);
+ max_lon = std::max(max_lon, ring[i]);
+ min_lat = std::min(min_lat, ring[i + 1]);
+ max_lat = std::max(max_lat, ring[i + 1]);
+ }
+ if (!std::isfinite(min_lon)) {
+ *x = Bounds{-180, 180};
+ *y = Bounds{-90, 90};
+ return;
+ }
+ // A single-point country would give a zero-width window, which cannot be
+ // mapped onto anything.
+ double pad_x = std::max((max_lon - min_lon) * margin, 1.0);
+ double pad_y = std::max((max_lat - min_lat) * margin, 1.0);
+ *x = Bounds{min_lon - pad_x, max_lon + pad_x};
+ *y = Bounds{min_lat - pad_y, max_lat + pad_y};
+}
+
+bool degrees_at(int column, int row, int width, int height, Bounds x, Bounds y, double *lon,
+ double *lat) {
+ if (width <= 0 || height <= 0)
+ return false;
+ // The canvas is 2x4 Braille pixels per cell, and it spans its bounds across
+ // `pixels - 1`, so the inverse has to use the same denominators or a click
+ // drifts from what was drawn.
+ double px = std::max(1, width * 2 - 1);
+ double py = std::max(1, height * 4 - 1);
+ *lon = x.min + ((column * 2 + 1) / px) * (x.max - x.min);
+ *lat = y.min + (1 - (row * 4 + 2) / py) * (y.max - y.min);
+ return true;
+}
+
+void draw_world_map(Surface s, const WorldMap &o) {
+ if (s.rect().width <= 0 || s.rect().height <= 0)
+ return;
+ auto &t = theme(s);
+ WorldShapeOptions shapes;
+ shapes.color = o.color ? o.color : t.border;
+ shapes.highlight = o.highlight;
+ shapes.highlight_color = o.highlight_color ? o.highlight_color : t.accent;
+
+ Canvas canvas;
+ canvas.shapes = world_shapes(shapes);
+ canvas.x = o.x.value_or(Bounds{-180, 180});
+ canvas.y = o.y.value_or(Bounds{-90, 90});
+ canvas.background = o.background;
+ canvas.grid = o.grid;
+ draw_canvas(s, canvas);
+}
+
+const CountryOutline *country_at_cell(int column, int row, int width, int height,
+ const WorldMap &o) {
+ double lon = 0, lat = 0;
+ if (!degrees_at(column, row, width, height, o.x.value_or(Bounds{-180, 180}),
+ o.y.value_or(Bounds{-90, 90}), &lon, &lat))
+ return nullptr;
+ return country_at(lon, lat);
+}
+
+} // namespace hqtui
diff --git a/ports/cpp/src/world_data.cpp b/ports/cpp/src/world_data.cpp
new file mode 100644
index 0000000..ec897a7
--- /dev/null
+++ b/ports/cpp/src/world_data.cpp
@@ -0,0 +1,505 @@
+/// Country outlines, flattened for a terminal.
+///
+/// Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's
+/// 1:110m Admin 0 countries, which is public domain. Do not edit by hand.
+///
+/// Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --
+/// rather than a list of pairs, because at 1982 points the nested form
+/// costs a container per coordinate for no gain. A country has more than one
+/// ring when it is more than one landmass.
+///
+/// 171 countries, 1982 points, simplified at 1 degrees.
+#include
+
+namespace hqtui {
+namespace {
+
+const double POOL[] = {
+ 66.5,37.4,70.8,38.5,71.8,36.7,75.2,37.1,71.3,36.1,69.3,31.9,
+ 66.3,29.9,60.9,29.8,61.2,35.7,66.5,37.4,21.0,40.8,19.4,40.3,
+ 19.7,42.7,21.0,40.8,-8.7,27.4,-8.7,28.8,-1.3,32.3,-1.2,35.7,
+ 8.4,36.9,7.5,34.1,9.8,29.4,9.3,26.1,12.0,23.5,3.2,19.1,
+ -8.7,27.4,12.3,-6.1,16.3,-5.9,17.5,-8.1,21.7,-7.3,22.2,-11.1,
+ 24.0,-11.2,24.0,-12.9,21.9,-12.9,23.2,-17.5,11.7,-17.3,13.7,-11.3,
+ 12.3,-6.1,-48.7,-78.0,-43.9,-78.5,-43.3,-80.0,-54.2,-80.6,-48.7,-78.0,
+ -66.3,-80.3,-59.6,-80.0,-66.3,-80.3,-73.9,-71.3,-70.3,-68.9,-68.3,-71.4,
+ -75.0,-72.1,-73.9,-71.3,-102.3,-71.9,-96.2,-72.5,-102.3,-71.9,-122.6,-73.7,
+ -118.7,-73.5,-122.6,-73.7,-127.3,-73.5,-124.0,-73.9,-127.3,-73.5,-163.7,-78.6,
+ -159.2,-79.5,-163.7,-78.6,180.0,-84.7,180.0,-90.0,-180.0,-90.0,-179.1,-84.1,
+ -143.1,-85.0,-153.6,-83.7,-152.9,-82.0,-156.8,-81.1,-146.4,-80.3,-155.3,-79.1,
+ -158.4,-76.9,-151.3,-77.4,-146.1,-76.5,-146.2,-75.4,-135.2,-74.3,-100.1,-74.9,
+ -103.7,-72.6,-74.9,-73.9,-67.4,-72.5,-67.7,-67.3,-57.8,-63.3,-65.7,-68.0,
+ -61.8,-70.7,-60.8,-73.7,-70.6,-76.6,-77.2,-76.7,-73.7,-77.9,-78.0,-79.2,
+ -58.2,-83.2,-28.5,-80.3,-35.6,-79.5,-35.8,-78.3,-17.5,-75.1,-15.4,-73.1,
+ -6.9,-70.9,27.1,-70.5,33.9,-68.5,38.6,-69.8,54.5,-65.8,61.4,-68.0,
+ 68.9,-67.9,69.7,-69.2,67.9,-71.9,69.9,-72.3,73.9,-69.9,88.0,-66.2,
+ 95.8,-67.4,102.8,-65.6,106.2,-66.9,113.6,-65.9,119.8,-67.3,135.1,-65.3,
+ 137.5,-67.0,145.5,-66.9,171.2,-71.7,163.6,-76.2,167.0,-78.8,161.8,-79.2,
+ 159.8,-80.9,169.4,-83.8,180.0,-84.7,-68.6,-52.6,-65.0,-54.7,-68.6,-54.9,
+ -68.6,-52.6,-57.6,-30.2,-58.5,-34.4,-56.8,-36.9,-62.3,-38.8,-62.7,-41.0,
+ -65.1,-41.1,-63.5,-42.6,-67.3,-45.6,-65.6,-47.2,-69.1,-50.7,-68.1,-52.3,
+ -71.9,-52.0,-73.4,-49.3,-71.2,-44.8,-72.1,-42.3,-68.4,-24.5,-66.3,-21.8,
+ -62.8,-22.0,-57.8,-25.2,-58.6,-27.1,-55.7,-27.4,-54.1,-25.5,-53.6,-26.9,
+ -57.6,-30.2,46.5,38.8,43.6,41.1,45.6,40.8,46.5,38.8,147.7,-40.8,
+ 147.9,-43.2,146.0,-43.5,144.7,-40.7,147.7,-40.8,126.1,-32.2,118.0,-35.1,
+ 115.0,-34.2,113.7,-22.5,120.9,-19.7,125.7,-14.2,129.6,-15.0,132.4,-11.1,
+ 136.5,-11.9,135.5,-15.0,140.2,-17.7,142.5,-10.7,146.4,-19.0,150.7,-22.4,
+ 153.6,-28.1,150.0,-37.4,146.3,-39.0,140.6,-38.0,138.2,-34.4,136.8,-35.3,
+ 137.8,-32.9,136.0,-34.9,131.3,-31.5,126.1,-32.2,17.0,48.1,14.6,46.4,
+ 9.5,47.1,12.9,47.5,13.6,48.9,17.0,48.1,46.4,41.9,50.4,40.3,
+ 48.9,38.3,45.6,39.9,45.0,41.2,46.4,41.9,-78.2,25.2,-77.5,23.8,
+ -78.2,25.2,92.7,22.0,92.4,20.7,91.4,22.8,89.0,22.1,88.6,26.4,
+ 92.4,25.0,91.2,23.5,92.7,22.0,28.2,56.2,30.9,55.6,32.7,53.4,
+ 31.8,52.1,23.5,51.6,23.5,53.9,28.2,56.2,6.2,50.8,5.7,49.5,
+ 2.5,51.1,6.2,50.8,-89.1,17.8,-88.1,18.3,-88.9,15.9,-89.1,17.8,
+ 2.7,6.3,0.8,10.5,2.8,12.2,2.7,6.3,91.7,27.8,88.8,27.1,
+ 91.7,27.8,-69.5,-11.0,-65.3,-9.8,-65.4,-11.6,-60.5,-13.8,-60.2,-16.3,
+ -58.2,-16.3,-57.9,-20.0,-61.8,-19.6,-62.7,-22.2,-67.8,-22.9,-69.5,-11.0,
+ 18.6,42.7,16.0,45.2,19.4,44.9,18.6,42.7,29.4,-22.1,25.7,-25.5,
+ 21.6,-26.7,19.9,-24.8,20.9,-18.3,25.3,-17.7,29.4,-22.1,-53.4,-33.8,
+ -53.8,-32.0,-57.6,-30.2,-53.6,-26.1,-55.8,-22.4,-57.9,-22.1,-58.2,-16.3,
+ -60.2,-16.3,-60.5,-13.8,-65.4,-11.6,-65.3,-9.8,-70.5,-11.0,-70.5,-9.5,
+ -72.2,-10.1,-74.0,-7.5,-72.9,-5.3,-69.9,-4.3,-69.8,1.7,-65.5,0.8,
+ -63.4,2.2,-64.8,4.1,-60.7,5.2,-59.0,1.3,-52.9,2.1,-51.3,4.2,
+ -50.4,-0.1,-44.6,-2.7,-40.0,-2.9,-35.6,-5.1,-34.7,-7.3,-38.7,-13.1,
+ -40.9,-21.9,-47.6,-24.9,-48.9,-28.7,-53.4,-33.8,22.7,44.2,28.6,43.7,
+ 28.0,42.0,23.0,41.3,22.7,44.2,-5.4,10.4,-4.3,13.2,-1.1,15.0,
+ 2.2,12.6,0.9,11.0,-2.9,11.0,-2.8,9.6,-5.4,10.4,30.5,-2.4,
+ 29.3,-4.5,29.0,-2.8,30.5,-2.4,102.6,12.2,103.0,14.2,107.6,13.5,
+ 106.2,11.0,103.5,10.6,102.6,12.2,14.5,12.9,14.5,4.7,15.9,1.7,
+ 9.6,2.3,8.8,5.5,11.7,7.0,14.5,12.9,-122.8,49.0,-127.4,50.8,
+ -130.5,54.3,-130.0,55.9,-135.5,59.8,-137.5,58.9,-141.0,60.3,-141.0,69.7,
+ -136.5,68.9,-128.1,70.5,-113.5,67.7,-106.1,68.8,-101.5,67.6,-97.7,68.6,
+ -96.1,67.3,-94.2,69.1,-96.5,70.1,-95.2,71.9,-87.4,67.2,-85.5,69.9,
+ -82.6,69.7,-81.4,67.1,-85.8,66.6,-90.7,63.6,-94.7,58.9,-92.3,57.1,
+ -82.3,55.1,-79.9,51.2,-78.6,52.6,-79.8,54.7,-76.5,56.5,-78.5,58.8,
+ -77.3,59.9,-78.1,62.3,-73.8,62.4,-69.6,61.1,-67.6,58.2,-64.6,60.3,
+ -61.8,56.3,-57.3,54.6,-55.7,52.1,-60.0,50.2,-66.4,50.2,-71.1,46.8,
+ -65.1,49.2,-64.5,46.2,-60.5,47.0,-59.8,45.9,-65.4,43.5,-66.2,44.5,
+ -64.4,45.3,-67.1,45.1,-69.2,47.4,-71.5,45.0,-82.4,41.7,-82.6,45.3,
+ -88.4,48.3,-122.8,49.0,-84.0,62.5,-81.9,62.9,-84.0,62.5,-79.8,72.8,
+ -80.8,73.7,-76.3,72.8,-79.8,72.8,-93.6,75.0,-96.8,74.9,-93.6,75.0,
+ -93.8,77.5,-96.4,77.8,-93.8,77.5,-96.8,78.8,-95.6,78.4,-98.6,78.9,
+ -96.8,78.8,-88.2,74.4,-97.1,76.8,-79.8,74.9,-88.2,74.4,-111.3,78.2,
+ -109.9,78.0,-113.5,77.7,-111.3,78.2,-111.0,78.8,-109.7,78.6,-112.5,78.4,
+ -111.0,78.8,-55.6,51.3,-56.8,49.8,-53.5,49.2,-53.1,46.7,-59.3,47.6,
+ -55.6,51.3,-83.9,65.1,-80.1,63.7,-87.2,63.5,-85.9,65.7,-83.9,65.1,
+ -78.8,72.4,-68.8,70.5,-67.0,69.2,-68.8,68.7,-61.9,66.9,-63.9,65.0,
+ -68.0,66.3,-64.7,63.4,-68.8,63.7,-66.2,61.9,-68.9,62.3,-78.6,64.6,
+ -74.0,65.5,-73.3,68.1,-79.0,70.2,-88.7,70.4,-90.2,72.2,-85.8,73.8,
+ -85.8,72.5,-82.3,73.8,-78.8,72.4,-94.5,74.1,-90.5,73.9,-95.4,72.1,
+ -96.0,73.4,-94.5,74.1,-122.9,76.1,-116.2,77.6,-122.9,76.1,-132.7,54.0,
+ -131.2,52.2,-132.7,54.0,-105.5,79.3,-99.7,77.9,-105.5,79.3,-123.5,48.5,
+ -128.4,50.8,-123.5,48.5,-121.5,74.4,-115.5,73.5,-123.1,70.9,-125.9,71.9,
+ -123.9,73.7,-124.9,74.3,-121.5,74.4,-107.8,75.8,-105.7,75.5,-117.7,75.2,
+ -115.4,76.5,-107.8,75.8,-106.5,73.1,-101.1,69.6,-113.3,68.5,-117.3,70.0,
+ -112.4,70.4,-119.4,71.6,-115.2,73.3,-108.2,71.7,-108.4,73.1,-106.5,73.1,
+ -100.4,72.7,-101.5,73.4,-97.4,73.8,-96.5,72.6,-98.4,71.3,-102.5,72.5,
+ -100.4,72.7,-106.6,73.6,-104.5,73.4,-106.6,73.6,-98.5,76.7,-98.2,75.0,
+ -102.5,75.6,-98.5,76.7,-96.0,80.6,-92.4,81.3,-85.8,79.3,-92.9,78.3,
+ -96.0,80.6,-91.6,81.9,-61.8,82.6,-76.9,79.3,-75.4,78.5,-80.6,76.2,
+ -89.5,76.5,-88.3,77.9,-85.0,77.5,-88.0,78.4,-85.1,79.3,-86.9,80.3,
+ -81.8,80.5,-91.6,81.9,-75.2,67.4,-77.2,67.6,-75.2,67.4,-96.3,69.5,
+ -99.8,69.4,-96.3,69.5,-64.5,49.9,-61.8,49.1,-64.5,49.9,-64.0,47.0,
+ -62.0,46.4,-64.0,47.0,27.4,5.2,22.4,4.0,19.5,5.0,16.0,2.3,
+ 14.5,5.5,15.3,7.4,22.9,11.1,27.4,5.2,23.8,19.6,23.9,15.6,
+ 21.9,12.6,22.9,11.1,15.3,7.4,13.5,14.4,15.9,20.4,14.9,22.9,
+ 23.8,19.6,-68.6,-52.6,-68.6,-54.9,-67.0,-54.9,-68.1,-55.6,-74.7,-52.8,
+ -71.1,-54.1,-68.6,-52.6,-69.6,-17.6,-67.0,-23.0,-70.5,-31.4,-69.8,-34.2,
+ -72.1,-42.3,-71.2,-44.8,-73.4,-49.3,-71.9,-52.0,-68.6,-52.3,-71.4,-53.9,
+ -74.9,-52.3,-75.6,-48.7,-74.1,-46.9,-75.6,-46.6,-72.7,-42.4,-74.3,-43.2,
+ -69.6,-17.6,109.5,18.2,108.6,19.4,110.8,20.1,109.5,18.2,80.3,42.3,
+ 80.0,44.9,87.8,49.3,91.0,46.9,90.9,45.3,96.3,42.7,109.2,42.5,
+ 111.9,45.1,119.7,46.7,115.5,48.1,122.2,53.4,125.9,52.8,131.0,47.8,
+ 135.0,48.5,133.1,45.1,131.0,45.0,130.6,42.4,121.1,38.9,121.6,40.9,
+ 117.5,38.7,122.4,37.5,119.2,34.9,121.9,31.7,121.7,28.2,118.7,24.5,
+ 110.4,20.3,105.3,23.4,101.7,22.3,101.8,21.2,99.2,22.1,97.6,23.9,
+ 98.7,27.5,96.1,29.5,88.8,27.3,78.7,31.5,78.9,34.3,73.7,39.4,
+ 80.3,42.3,-66.9,1.3,-69.8,1.7,-69.9,-4.3,-70.0,-2.7,-77.4,0.4,
+ -79.0,1.7,-77.1,3.8,-77.5,8.5,-71.4,12.4,-73.3,9.2,-72.0,7.0,
+ -67.3,6.1,-66.9,1.3,18.5,3.5,16.0,-3.5,11.9,-5.0,11.5,-2.8,
+ 14.4,-1.3,13.1,2.3,15.9,1.7,18.5,3.5,-82.5,9.6,-83.0,8.2,
+ -85.9,10.9,-82.5,9.6,-8.0,10.2,-2.8,9.6,-2.9,5.0,-7.7,4.4,
+ -8.0,10.2,16.6,46.5,19.4,45.2,15.8,44.8,18.5,42.5,13.7,45.1,
+ 16.6,46.5,-82.3,23.2,-74.2,20.3,-77.8,19.9,-81.8,22.6,-85.0,21.9,
+ -82.3,23.2,32.7,35.1,34.0,35.0,32.7,35.1,15.0,51.1,18.9,49.5,
+ 12.5,49.5,15.0,51.1,29.3,-4.5,30.7,-8.3,28.7,-8.5,28.4,-11.8,
+ 29.7,-13.3,22.2,-11.1,21.7,-7.3,17.5,-8.1,16.3,-5.9,12.2,-5.8,
+ 16.0,-3.5,19.5,5.0,29.7,4.6,31.2,2.2,29.3,-4.5,9.9,55.0,
+ 8.1,56.5,10.6,57.7,9.9,55.0,12.4,56.1,12.1,54.8,11.0,55.4,
+ 12.4,56.1,42.4,12.5,42.8,10.9,42.4,12.5,-71.7,18.0,-71.6,19.9,
+ -68.3,18.6,-71.7,18.0,-75.4,-0.2,-78.6,-4.5,-80.4,-4.4,-80.1,0.8,
+ -75.4,-0.2,36.9,22.0,25.0,22.0,25.2,31.6,34.3,31.2,34.2,27.8,
+ 32.3,29.8,36.9,22.0,-89.4,14.4,-87.9,13.1,-90.1,13.7,-89.4,14.4,
+ 9.6,2.3,11.3,1.1,9.5,1.0,9.6,2.3,36.4,14.4,38.4,18.0,
+ 43.1,12.7,36.4,14.4,28.0,59.5,27.3,57.5,23.3,59.2,28.0,59.5,
+ 32.1,-26.7,31.0,-25.7,32.1,-26.7,47.8,8.0,45.0,5.0,39.6,3.4,
+ 36.2,4.4,33.0,7.8,37.9,15.0,41.6,13.5,43.7,9.2,47.8,8.0,
+ -61.2,-51.8,-57.7,-51.5,-61.2,-51.8,28.6,69.1,31.1,62.4,28.1,60.5,
+ 21.3,60.7,21.5,63.2,25.4,65.1,20.6,69.1,24.7,68.6,27.7,70.2,
+ 28.6,69.1,68.9,-48.6,70.6,-49.3,68.7,-49.8,68.9,-48.6,-51.7,4.2,
+ -52.9,2.1,-54.5,2.3,-54.0,5.8,-51.7,4.2,6.2,49.5,8.1,49.0,
+ 6.0,46.7,7.4,43.7,1.8,42.3,-1.9,43.4,-1.2,46.0,-4.6,48.7,
+ -1.6,48.6,-1.9,49.8,2.5,51.1,6.2,49.5,8.7,42.6,9.2,41.4,
+ 8.7,42.6,11.3,2.3,14.3,1.2,14.4,-1.3,11.1,-4.0,8.8,-1.1,
+ 11.3,2.3,-16.7,13.6,-13.8,13.5,-16.7,13.6,40.0,43.4,46.6,41.2,
+ 41.6,41.5,40.0,43.4,14.1,53.8,15.0,51.1,12.2,50.3,12.9,47.5,
+ 7.5,47.6,8.1,49.0,6.0,50.1,7.1,53.7,9.9,55.0,14.1,53.8,
+ 0.0,11.0,1.1,5.9,-2.9,5.0,-2.9,11.0,0.0,11.0,26.3,35.3,
+ 23.5,35.3,26.3,35.3,23.0,41.3,26.6,41.6,22.6,40.3,24.0,37.7,
+ 22.5,36.4,20.2,39.6,23.0,41.3,-46.8,82.6,-27.1,83.5,-20.8,82.7,
+ -31.9,82.2,-12.2,81.3,-20.0,80.2,-17.7,80.1,-19.7,78.8,-18.5,77.0,
+ -21.7,76.6,-19.4,74.3,-24.8,72.3,-21.8,70.7,-25.5,71.4,-26.4,70.2,
+ -22.3,70.1,-39.8,65.5,-43.4,60.1,-48.3,60.9,-51.6,63.6,-54.0,67.2,
+ -50.9,69.9,-54.7,69.6,-54.4,70.8,-51.4,70.6,-55.8,71.7,-54.7,72.6,
+ -58.6,75.5,-68.5,76.1,-71.4,77.0,-66.8,77.4,-73.3,78.0,-65.7,79.4,
+ -68.0,80.1,-62.7,81.8,-44.5,81.7,-46.8,82.6,-92.2,14.5,-90.5,16.1,
+ -91.0,17.8,-89.1,17.8,-88.2,15.7,-89.4,14.4,-92.2,14.5,-13.7,12.6,
+ -9.1,12.3,-8.3,7.7,-11.1,10.0,-13.2,8.9,-15.1,11.0,-13.7,12.6,
+ -16.7,12.4,-13.7,11.8,-15.1,11.0,-16.7,12.4,-56.5,1.9,-59.6,1.8,
+ -61.4,6.0,-59.8,8.4,-57.1,6.0,-58.0,4.1,-56.5,1.9,-71.7,19.7,
+ -71.7,18.0,-74.5,18.3,-71.7,19.7,-83.1,15.0,-87.3,13.0,-89.4,14.4,
+ -87.9,15.9,-83.1,15.0,22.1,48.4,21.0,46.3,16.2,46.9,17.0,48.1,
+ 22.1,48.4,-14.5,66.5,-13.6,65.1,-18.7,63.5,-22.8,64.0,-21.8,64.4,
+ -24.0,64.9,-22.2,65.4,-24.3,65.6,-14.5,66.5,97.3,28.3,92.7,22.0,
+ 91.2,23.5,92.4,25.0,88.6,26.4,88.9,21.7,80.3,15.9,79.9,10.4,
+ 77.5,8.0,72.6,21.4,70.5,20.9,68.2,23.7,71.0,24.4,69.5,26.9,
+ 75.3,32.3,73.7,34.3,77.8,35.5,78.7,31.5,81.1,30.2,80.1,28.8,
+ 83.3,27.4,88.1,26.4,88.7,28.1,92.0,26.8,96.1,29.5,97.3,28.3,
+ 141.0,-2.6,141.0,-9.1,137.6,-8.4,137.9,-5.4,133.0,-4.1,132.0,-2.8,
+ 133.7,-2.2,130.5,-0.9,134.0,-0.8,135.5,-3.4,137.4,-1.7,141.0,-2.6,
+ 125.0,-8.9,123.5,-10.2,125.0,-8.9,117.9,4.1,119.0,0.9,116.1,-4.0,
+ 110.2,-2.9,109.7,2.0,110.5,0.8,113.8,1.2,115.9,4.3,117.9,4.1,
+ 129.4,-2.8,130.8,-3.9,127.9,-3.4,129.4,-2.8,127.9,2.2,128.1,-0.9,
+ 127.9,2.2,122.9,0.9,125.2,1.4,120.0,-0.5,123.3,-0.6,121.5,-1.9,
+ 123.2,-5.3,121.0,-2.6,119.8,-5.7,118.8,-2.8,119.8,0.2,122.9,0.9,
+ 120.3,-10.3,119.0,-9.6,120.3,-10.3,121.3,-8.5,122.9,-8.1,119.9,-8.8,
+ 121.3,-8.5,118.3,-8.4,116.7,-9.0,118.3,-8.4,108.5,-6.4,115.7,-8.4,
+ 105.4,-6.9,108.5,-6.4,104.4,-1.1,106.1,-3.1,105.8,-5.9,102.6,-4.2,
+ 95.3,5.5,97.5,5.2,104.4,-1.1,48.6,29.9,45.4,34.0,46.1,35.7,
+ 44.1,39.4,48.1,39.6,50.8,36.9,56.6,38.1,61.1,36.5,60.9,29.8,
+ 63.3,26.8,61.5,25.1,57.4,25.7,48.6,29.9,39.2,32.2,41.3,36.4,
+ 44.8,37.2,48.6,29.9,44.7,29.2,39.2,32.2,-6.2,53.9,-6.8,52.3,
+ -10.0,51.8,-7.6,55.1,-6.2,53.9,35.7,32.7,34.9,29.5,34.3,31.2,
+ 35.7,32.7,10.4,46.9,13.8,46.5,12.6,44.1,18.3,39.8,16.9,40.4,
+ 15.7,37.9,15.4,40.0,10.2,43.9,7.4,43.7,6.8,46.0,10.4,46.9,
+ 14.8,38.1,15.1,36.6,12.4,37.6,14.8,38.1,8.7,40.9,9.8,40.5,
+ 8.8,38.9,8.7,40.9,-77.6,18.5,-76.2,17.9,-77.6,18.5,141.9,39.2,
+ 140.3,35.1,135.8,33.5,135.1,34.6,131.0,33.9,132.0,33.1,130.2,31.4,
+ 129.4,33.3,139.4,38.2,140.3,41.2,141.9,39.2,144.6,44.0,145.5,43.3,
+ 140.0,41.6,142.0,45.6,144.6,44.0,132.4,33.5,134.8,33.8,132.4,33.5,
+ 35.5,32.4,38.8,33.4,39.2,32.2,37.0,31.5,38.0,30.5,36.1,29.2,
+ 34.9,29.5,35.5,32.4,87.4,49.2,80.0,44.9,80.3,42.3,74.2,43.3,
+ 68.6,40.7,64.9,43.7,62.0,43.5,58.5,45.6,55.9,45.0,56.0,41.3,
+ 52.5,41.8,50.3,44.6,53.0,45.3,53.0,46.9,49.1,46.4,46.5,48.4,
+ 50.8,51.7,61.3,50.8,60.0,52.0,61.4,54.0,69.1,55.4,73.4,53.5,
+ 76.9,54.5,80.0,50.9,87.4,49.2,39.2,-4.7,33.9,-0.9,35.3,5.5,
+ 38.1,3.6,41.9,3.9,41.6,-1.7,39.2,-4.7,20.6,41.9,20.6,43.2,
+ 21.8,42.7,20.6,41.9,48.0,30.0,48.4,28.6,46.6,29.1,48.0,30.0,
+ 71.0,42.3,74.2,43.3,80.3,42.3,73.7,39.4,69.5,39.5,73.1,40.9,
+ 71.0,42.3,107.4,14.2,105.2,14.3,104.0,18.2,101.1,17.5,100.1,20.4,
+ 101.7,22.3,104.4,20.8,103.9,19.3,107.4,14.2,27.3,57.5,28.2,56.2,
+ 26.5,55.6,21.1,56.0,22.5,57.8,27.3,57.5,35.8,33.3,36.4,34.6,
+ 35.8,33.3,29.0,-29.0,28.1,-30.5,27.0,-29.9,29.0,-29.0,-8.4,7.7,
+ -7.7,4.4,-11.4,6.8,-10.2,8.4,-8.4,7.7,25.0,22.0,23.8,19.6,
+ 10.3,24.4,10.0,31.4,11.5,33.1,19.1,30.3,20.9,32.7,24.9,31.9,
+ 25.0,22.0,26.5,55.6,23.5,53.9,21.1,56.0,26.5,55.6,49.5,-12.5,
+ 50.4,-15.7,47.1,-24.9,45.4,-25.6,43.3,-22.8,44.0,-17.4,49.5,-12.5,
+ 32.8,-9.2,35.7,-14.6,35.0,-16.8,32.7,-13.7,32.8,-9.2,100.1,6.5,
+ 103.0,5.5,104.2,1.3,101.4,2.8,100.1,6.5,117.9,4.1,115.9,4.3,
+ 114.6,1.4,109.8,1.3,115.3,4.3,116.7,6.9,119.2,5.4,117.9,4.1,
+ -11.5,12.4,-11.7,15.4,-5.5,15.5,-6.5,25.0,4.3,19.2,3.6,15.6,
+ -4.0,13.5,-5.4,10.4,-11.5,12.4,-17.1,21.0,-12.9,21.3,-12.0,25.9,
+ -8.7,25.9,-8.7,27.4,-4.9,25.0,-6.5,25.0,-5.5,15.5,-12.2,14.6,
+ -14.6,16.6,-16.5,16.1,-17.1,21.0,-117.1,32.5,-106.5,31.8,-103.9,29.3,
+ -101.7,29.8,-97.1,25.9,-97.9,22.4,-95.9,18.8,-91.4,18.9,-90.3,21.0,
+ -87.1,21.5,-87.8,18.3,-91.0,17.8,-90.5,16.1,-92.2,14.5,-103.5,18.3,
+ -113.1,31.2,-114.9,31.4,-109.9,22.8,-115.1,27.7,-114.2,28.6,-117.1,32.5,
+ 26.6,48.2,30.0,46.4,28.2,45.5,26.6,48.2,87.8,49.3,92.2,50.8,
+ 97.3,49.7,98.9,52.0,108.5,49.3,116.7,49.9,115.7,47.7,119.8,47.0,
+ 105.0,41.6,96.3,42.7,90.9,45.3,91.0,46.9,87.8,49.3,20.1,42.6,
+ 18.5,42.5,20.1,42.6,-2.2,35.2,-1.3,32.3,-8.7,28.8,-8.8,27.1,
+ -11.4,26.9,-14.8,21.5,-17.0,21.4,-14.4,26.3,-9.6,29.9,-8.7,33.2,
+ -5.9,35.8,-2.2,35.2,34.6,-11.5,40.3,-10.3,40.8,-14.7,34.8,-19.8,
+ 35.5,-24.1,32.1,-26.7,31.2,-22.3,32.8,-16.7,30.2,-14.8,33.2,-14.0,
+ 35.0,-16.8,34.6,-11.5,100.1,20.4,97.4,18.4,99.6,11.9,98.6,9.9,
+ 97.2,16.9,94.2,16.0,92.3,21.5,97.3,28.3,98.7,27.5,97.6,23.9,
+ 101.2,21.8,100.1,20.4,32.7,35.1,34.6,35.7,32.7,35.1,19.9,-24.8,
+ 19.9,-28.5,16.3,-28.6,11.7,-17.3,25.1,-17.6,20.9,-18.3,19.9,-24.8,
+ 88.1,27.9,87.2,26.4,80.1,28.8,81.5,30.4,88.1,27.9,6.9,53.5,
+ 6.2,50.8,3.3,51.3,6.9,53.5,165.8,-21.1,167.1,-22.2,164.0,-20.1,
+ 165.8,-21.1,176.9,-40.1,174.7,-41.3,174.7,-37.4,172.6,-34.5,176.0,-37.6,
+ 178.5,-37.7,176.9,-40.1,169.7,-43.6,172.8,-40.5,174.2,-41.3,170.6,-45.9,
+ 166.7,-46.2,169.7,-43.6,-83.7,10.9,-87.7,12.9,-83.1,15.0,-83.7,10.9,
+ 14.9,22.9,15.9,20.4,13.5,14.4,14.2,12.5,5.4,13.9,3.6,11.7,
+ 1.0,12.9,0.4,14.9,3.6,15.6,4.3,19.2,12.0,23.5,14.9,22.9,
+ 2.7,6.3,4.4,13.7,13.1,13.6,14.6,12.1,11.7,7.0,8.5,4.8,
+ 5.9,4.3,2.7,6.3,130.6,42.4,127.5,39.8,128.2,38.4,124.7,38.1,
+ 125.1,40.6,130.6,42.4,22.4,42.3,23.0,41.3,20.6,41.1,22.4,42.3,
+ 15.1,79.7,21.5,79.0,15.9,76.8,10.4,79.7,15.1,79.7,31.1,69.6,
+ 18.0,68.6,12.6,64.1,11.0,58.9,5.7,58.6,5.0,62.0,19.2,69.8,
+ 28.2,71.2,31.1,69.6,27.4,80.1,17.4,80.3,27.4,80.1,24.7,77.9,
+ 20.7,77.7,24.7,77.9,55.2,22.7,56.4,24.9,59.8,22.3,57.7,18.9,
+ 53.1,16.7,52.0,19.0,55.0,20.0,55.2,22.7,77.8,35.5,73.7,34.3,
+ 75.3,32.3,69.5,26.9,71.0,24.4,61.5,25.1,63.3,26.8,60.9,29.8,
+ 66.3,29.9,71.8,36.5,75.2,37.1,77.8,35.5,-77.4,8.7,-77.9,7.2,
+ -79.1,9.0,-80.9,7.2,-82.9,9.5,-77.4,8.7,141.0,-2.6,147.6,-6.1,
+ 147.2,-7.4,150.7,-10.6,144.7,-7.6,141.0,-9.1,141.0,-2.6,152.6,-3.7,
+ 152.8,-4.8,150.7,-2.7,152.6,-3.7,151.3,-5.8,148.3,-5.7,152.1,-4.1,
+ 151.3,-5.8,154.8,-5.3,155.9,-6.8,154.8,-5.3,-58.2,-20.2,-57.9,-22.1,
+ -54.3,-24.0,-55.7,-27.4,-58.6,-27.1,-57.8,-25.2,-62.7,-22.2,-61.8,-19.6,
+ -58.2,-20.2,-69.9,-4.3,-72.9,-5.3,-74.0,-7.5,-68.7,-12.6,-70.4,-18.3,
+ -76.0,-14.6,-81.4,-4.7,-80.3,-3.4,-78.6,-4.5,-75.1,-0.1,-73.1,-2.3,
+ -70.0,-2.7,-69.9,-4.3,122.6,10.0,124.1,11.2,123.0,9.0,122.6,10.0,
+ 126.4,8.4,125.4,5.6,123.6,7.8,121.9,7.2,125.4,9.8,126.4,8.4,
+ 118.5,9.3,117.2,8.4,119.5,11.4,118.5,9.3,122.3,18.2,121.7,14.3,
+ 124.1,12.5,119.9,15.4,120.7,18.5,122.3,18.2,125.5,12.2,124.8,10.1,
+ 124.3,12.6,125.5,12.2,23.5,53.9,24.0,50.7,22.8,49.0,16.2,50.4,
+ 14.1,53.0,17.6,54.9,23.5,53.9,-9.0,41.9,-6.4,41.4,-7.9,36.8,
+ -9.5,38.7,-9.0,41.9,-66.3,18.5,-67.2,17.9,-66.3,18.5,50.8,24.8,
+ 51.3,26.1,50.8,24.8,28.2,45.5,29.6,45.3,28.6,43.7,22.9,43.8,
+ 20.2,46.1,26.6,48.2,28.2,45.5,49.1,46.4,46.7,44.6,47.8,41.2,
+ 36.7,45.2,40.1,49.6,31.8,52.1,32.7,53.4,30.9,55.6,27.3,57.5,
+ 29.1,60.0,28.1,60.5,31.5,62.9,30.0,63.6,28.6,69.1,32.1,69.9,
+ 41.1,67.5,38.4,66.0,33.2,66.6,37.0,63.8,37.2,65.1,43.9,66.1,
+ 43.5,68.6,46.3,68.3,46.3,66.7,53.7,68.9,59.9,68.3,60.6,69.9,
+ 68.5,68.1,66.7,71.0,69.9,73.0,72.8,72.2,71.8,71.4,73.7,68.4,
+ 71.3,66.3,72.4,66.2,75.1,67.8,73.1,71.4,74.7,72.8,76.4,71.2,
+ 81.5,71.8,80.5,73.6,104.4,77.7,114.1,75.8,109.4,74.2,127.0,73.6,
+ 131.3,70.8,139.9,71.5,139.1,72.4,140.5,72.8,159.0,70.9,160.9,69.4,
+ 180.0,69.0,180.0,65.0,177.4,64.6,179.2,62.3,170.3,59.9,163.5,59.9,
+ 162.0,58.2,163.2,57.6,162.1,54.9,156.8,51.0,155.9,56.8,164.5,62.6,
+ 160.1,60.5,156.7,61.4,154.2,59.8,155.0,59.1,142.2,59.0,135.1,54.7,
+ 141.3,53.1,140.1,48.4,134.9,43.4,130.8,42.2,131.0,45.0,133.1,45.1,
+ 135.0,48.5,131.0,47.8,123.6,53.5,120.2,52.8,117.9,49.5,108.5,49.3,
+ 98.9,52.0,97.3,49.7,92.2,50.8,87.4,49.2,80.0,50.9,76.9,54.5,
+ 73.4,53.5,69.1,55.4,61.4,54.0,60.0,52.0,61.3,50.8,50.8,51.7,
+ 47.5,50.5,46.5,48.4,49.1,46.4,93.8,81.0,100.2,79.8,97.8,78.8,
+ 91.2,80.3,93.8,81.0,102.8,79.3,105.4,78.7,99.4,77.9,102.8,79.3,
+ 138.8,76.1,145.1,75.6,137.0,75.3,138.8,76.1,148.2,75.3,150.7,75.1,
+ 146.1,75.2,148.2,75.3,139.9,73.4,143.6,73.2,139.9,73.4,44.8,80.6,
+ 51.5,80.7,44.8,80.6,22.7,54.3,19.7,54.4,22.7,54.3,53.5,73.7,
+ 61.2,76.3,68.9,76.5,58.5,74.3,55.4,72.4,57.5,70.7,51.6,71.5,
+ 53.5,73.7,142.9,53.7,144.7,49.0,143.2,49.3,143.5,46.1,142.1,46.0,
+ 141.7,53.3,142.9,53.7,-174.9,67.2,-169.9,66.0,-173.0,64.3,-178.7,66.1,
+ -180.0,65.0,-180.0,69.0,-174.9,67.2,-178.7,70.9,-180.0,71.5,-177.6,71.3,
+ -178.7,70.9,33.4,46.0,36.5,45.5,33.9,44.4,32.5,45.3,33.4,46.0,
+ 30.4,-1.1,29.0,-2.8,30.4,-1.1,30.8,3.5,23.9,8.6,25.8,10.4,
+ 31.4,9.8,33.2,12.2,33.0,7.8,35.3,5.5,30.8,3.5,35.0,29.4,
+ 39.2,32.2,47.5,29.0,52.0,23.0,55.2,22.7,55.0,20.0,47.0,16.9,
+ 43.4,17.6,42.8,16.3,35.0,29.4,-16.7,13.6,-17.6,14.7,-14.6,16.6,
+ -11.5,12.4,-16.7,12.4,-13.8,13.5,-16.7,13.6,18.8,45.9,22.7,44.6,
+ 22.5,42.5,19.2,43.5,18.8,45.9,-13.2,8.9,-11.1,10.0,-10.2,8.4,
+ -11.4,6.8,-13.2,8.9,22.6,49.1,16.9,48.5,22.6,49.1,13.8,46.5,
+ 16.6,46.5,15.3,45.5,13.8,46.5,159.6,-8.0,158.2,-7.4,159.6,-8.0,
+ 41.6,-1.7,41.0,2.8,45.0,5.0,48.9,9.5,48.9,11.4,51.1,12.0,
+ 48.6,5.3,41.6,-1.7,48.9,11.4,47.8,8.0,42.6,10.6,48.9,11.4,
+ 16.3,-28.6,19.9,-28.5,19.9,-24.8,21.6,-26.7,25.7,-25.5,29.4,-22.1,
+ 31.2,-22.3,31.9,-24.4,30.7,-26.7,32.8,-26.7,28.2,-32.8,20.1,-34.8,
+ 18.4,-34.1,16.3,-28.6,126.2,37.7,128.3,38.6,129.1,35.1,126.5,34.4,
+ 126.2,37.7,-7.5,37.1,-6.4,41.4,-9.4,43.0,3.0,42.5,-2.1,36.7,
+ -7.5,37.1,81.8,7.5,80.3,6.0,80.1,9.8,81.8,7.5,24.6,8.2,
+ 21.9,12.6,25.0,22.0,36.9,22.0,38.4,18.0,34.0,8.7,32.7,12.2,
+ 31.4,9.8,25.1,10.3,24.6,8.2,-54.5,2.3,-56.5,1.9,-57.6,3.3,
+ -57.1,6.0,-54.0,5.8,-54.5,2.3,11.0,58.9,12.6,61.3,11.9,63.1,
+ 16.8,68.0,20.6,69.1,23.5,67.9,23.9,66.0,17.8,62.7,17.1,61.3,
+ 18.8,60.1,15.9,56.1,12.9,55.4,11.0,58.9,9.6,47.5,10.4,46.5,
+ 6.0,46.3,9.6,47.5,35.7,32.7,36.7,36.8,42.3,37.2,41.0,34.4,
+ 35.7,32.7,121.8,24.4,120.7,22.0,120.1,23.6,121.8,24.4,67.8,37.1,
+ 67.7,39.6,70.7,41.0,69.5,39.5,73.7,39.4,75.0,37.4,71.8,36.7,
+ 70.8,38.5,67.8,37.1,33.9,-0.9,39.2,-4.7,39.5,-10.9,34.6,-11.5,
+ 29.6,-6.5,30.4,-1.1,33.9,-0.9,105.2,14.3,103.0,14.2,102.6,12.2,
+ 100.1,13.4,99.2,9.2,102.1,6.2,101.2,5.7,98.2,8.4,99.6,11.9,
+ 97.4,18.4,100.1,20.4,101.1,17.5,104.7,17.4,105.2,14.3,125.0,-8.9,
+ 127.3,-8.4,125.0,-8.9,0.9,11.0,1.1,5.9,0.9,11.0,9.5,30.3,
+ 7.5,34.1,9.5,37.3,11.0,37.1,10.1,34.3,11.5,33.1,9.5,30.3,
+ 44.8,37.2,29.7,36.1,27.6,36.7,26.2,39.5,33.5,42.0,42.6,41.6,
+ 44.8,39.7,44.8,37.2,26.1,41.8,29.0,41.3,26.4,40.2,26.1,41.8,
+ 52.5,41.8,57.1,41.3,58.6,42.8,66.5,37.4,62.2,35.3,57.3,38.0,
+ 53.9,37.2,52.7,40.0,54.7,41.0,52.5,41.8,33.9,-0.9,29.6,-1.3,
+ 31.2,3.8,34.5,3.6,33.9,-0.9,31.8,52.1,40.1,49.6,39.7,47.9,
+ 35.0,45.7,31.7,46.7,28.7,45.3,30.0,46.4,28.7,48.1,22.1,48.4,
+ 23.5,51.6,31.8,52.1,51.6,24.2,56.3,25.7,55.0,22.5,51.6,24.2,
+ -6.2,53.9,-7.6,55.1,-6.2,53.9,-3.1,53.4,-6.1,56.8,-5.0,58.6,
+ -2.0,57.7,-3.1,56.0,1.7,52.7,1.4,51.3,-5.8,50.2,-3.4,51.4,
+ -5.3,52.0,-4.6,53.5,-3.1,53.4,-122.8,49.0,-88.4,48.3,-82.6,45.3,
+ -82.7,41.7,-71.5,45.0,-69.2,47.4,-67.0,44.8,-70.1,43.7,-70.0,41.6,
+ -75.5,39.5,-75.9,37.2,-76.3,39.2,-77.0,38.2,-75.7,35.6,-81.3,31.4,
+ -80.4,25.2,-83.7,29.9,-86.4,30.4,-94.7,29.5,-97.5,25.8,-101.0,29.4,
+ -103.9,29.3,-106.5,31.8,-117.1,32.5,-120.6,34.6,-124.4,40.3,-124.7,48.2,
+ -122.6,47.1,-122.8,49.0,-166.5,60.4,-165.6,59.9,-167.5,60.2,-166.5,60.4,
+ -153.2,58.0,-152.1,57.6,-154.5,57.0,-153.2,58.0,-141.0,69.7,-141.0,60.3,
+ -137.5,58.9,-135.5,59.8,-130.0,55.9,-130.5,54.8,-134.1,58.1,-139.9,59.5,
+ -147.1,60.9,-151.7,59.2,-150.6,61.3,-158.4,56.0,-164.9,54.6,-157.0,58.9,
+ -162.0,58.7,-165.3,60.5,-165.7,62.1,-160.8,64.8,-168.1,65.7,-161.7,66.1,
+ -166.2,68.9,-156.6,71.4,-141.0,69.7,-171.7,63.8,-168.7,63.3,-171.7,63.8,
+ -57.6,-30.2,-53.8,-32.0,-53.8,-34.4,-58.4,-33.9,-57.6,-30.2,56.0,41.3,
+ 55.9,45.0,58.5,45.6,62.0,43.5,64.9,43.7,68.3,40.7,71.0,42.3,
+ 73.1,40.9,67.7,39.6,67.8,37.1,58.6,42.8,56.0,41.3,-60.7,5.2,
+ -64.8,4.1,-63.4,2.2,-66.3,0.7,-67.8,2.8,-67.3,6.1,-72.0,7.0,
+ -72.9,10.5,-71.3,11.8,-71.3,9.1,-69.9,12.2,-68.2,10.6,-61.9,10.7,
+ -59.8,8.4,-60.7,5.2,104.3,10.5,107.5,12.3,107.6,15.2,102.2,22.5,
+ 105.3,23.4,108.1,21.6,105.7,19.1,108.9,15.3,109.2,11.7,105.2,8.6,
+ 104.3,10.5,-8.7,27.7,-8.7,25.9,-12.0,25.9,-12.9,21.3,-17.1,21.0,
+ -14.8,21.5,-11.4,26.9,-8.7,27.7,52.0,19.0,52.2,15.6,43.5,12.6,
+ 43.4,17.6,47.0,16.9,52.0,19.0,30.7,-8.3,33.2,-9.7,33.2,-14.0,
+ 27.0,-17.9,23.2,-17.5,21.9,-12.9,24.0,-12.9,23.9,-10.9,29.7,-13.3,
+ 28.4,-9.2,30.7,-8.3,31.2,-22.3,28.0,-21.5,25.3,-17.7,30.3,-15.5,
+ 32.8,-16.7,31.2,-22.3,
+};
+
+struct Span {
+ int at, length;
+};
+
+const Span RINGS[] = {
+ {0,20},{20,8},{28,22},{50,24},{74,10},{84,6},{90,10},{100,6},
+ {106,6},{112,6},{118,6},{124,122},{246,8},{254,48},{302,8},{310,10},
+ {320,48},{368,12},{380,12},{392,6},{398,16},{414,14},{428,8},{436,8},
+ {444,8},{452,6},{458,22},{480,8},{488,14},{502,70},{572,10},{582,16},
+ {598,8},{606,12},{618,14},{632,116},{748,6},{754,8},{762,6},{768,6},
+ {774,8},{782,8},{790,8},{798,8},{806,12},{818,10},{828,42},{870,10},
+ {880,6},{886,6},{892,6},{898,6},{904,14},{918,10},{928,20},{948,14},
+ {962,6},{968,8},{976,10},{986,26},{1012,6},{1018,6},{1024,6},{1030,6},
+ {1036,16},{1052,18},{1070,14},{1084,34},{1118,8},{1126,76},{1202,26},{1228,16},
+ {1244,8},{1252,10},{1262,12},{1274,12},{1286,6},{1292,8},{1300,30},{1330,8},
+ {1338,8},{1346,6},{1352,8},{1360,10},{1370,14},{1384,8},{1392,8},{1400,8},
+ {1408,8},{1416,6},{1422,18},{1440,6},{1446,20},{1466,8},{1474,10},{1484,24},
+ {1508,6},{1514,12},{1526,6},{1532,8},{1540,20},{1560,10},{1570,6},{1576,14},
+ {1590,74},{1664,14},{1678,14},{1692,8},{1700,14},{1714,8},{1722,10},{1732,10},
+ {1742,18},{1760,52},{1812,24},{1836,6},{1842,18},{1860,8},{1868,6},{1874,22},
+ {1896,6},{1902,8},{1910,6},{1916,8},{1924,14},{1938,26},{1964,12},{1976,10},
+ {1986,8},{1994,22},{2016,8},{2024,8},{2032,6},{2038,22},{2060,10},{2070,6},
+ {2076,16},{2092,50},{2142,14},{2156,8},{2164,8},{2172,14},{2186,18},{2204,12},
+ {2216,6},{2222,8},{2230,10},{2240,18},{2258,8},{2266,14},{2280,10},{2290,10},
+ {2300,16},{2316,18},{2334,24},{2358,42},{2400,8},{2408,26},{2434,6},{2440,24},
+ {2464,24},{2488,24},{2512,6},{2518,14},{2532,10},{2542,8},{2550,8},{2558,14},
+ {2572,12},{2584,8},{2592,24},{2616,16},{2632,12},{2644,8},{2652,10},{2662,18},
+ {2680,6},{2686,6},{2692,16},{2708,24},{2732,12},{2744,14},{2758,8},{2766,8},
+ {2774,6},{2780,18},{2798,26},{2824,8},{2832,12},{2844,8},{2852,12},{2864,8},
+ {2872,14},{2886,10},{2896,6},{2902,6},{2908,14},{2922,192},{3114,10},{3124,8},
+ {3132,8},{3140,8},{3148,6},{3154,6},{3160,6},{3166,16},{3182,14},{3196,14},
+ {3210,8},{3218,10},{3228,6},{3234,16},{3250,20},{3270,14},{3284,10},{3294,10},
+ {3304,6},{3310,8},{3318,6},{3324,16},{3340,8},{3348,28},{3376,10},{3386,12},
+ {3398,8},{3406,20},{3426,12},{3438,26},{3464,8},{3472,10},{3482,8},{3490,18},
+ {3508,14},{3522,28},{3550,6},{3556,6},{3562,14},{3576,16},{3592,8},{3600,20},
+ {3620,10},{3630,22},{3652,8},{3660,6},{3666,24},{3690,58},{3748,8},{3756,8},
+ {3764,46},{3810,6},{3816,10},{3826,24},{3850,30},{3880,22},{3902,16},{3918,12},
+ {3930,22},{3952,12},
+};
+
+const Span COUNTRIES[] = {
+ {0,1},{1,1},{2,1},{3,1},{4,8},{12,2},{14,1},{15,2},
+ {17,1},{18,1},{19,1},{20,1},{21,1},{22,1},{23,1},{24,1},
+ {25,1},{26,1},{27,1},{28,1},{29,1},{30,1},{31,1},{32,1},
+ {33,1},{34,1},{35,29},{64,1},{65,1},{66,2},{68,2},{70,1},
+ {71,1},{72,1},{73,1},{74,1},{75,1},{76,1},{77,1},{78,1},
+ {79,2},{81,1},{82,1},{83,1},{84,1},{85,1},{86,1},{87,1},
+ {88,1},{89,1},{90,1},{91,1},{92,1},{93,1},{94,3},{97,1},
+ {98,1},{99,1},{100,1},{101,1},{102,2},{104,1},{105,1},{106,1},
+ {107,1},{108,1},{109,1},{110,1},{111,1},{112,1},{113,1},{114,11},
+ {125,1},{126,1},{127,1},{128,1},{129,3},{132,1},{133,3},{136,1},
+ {137,1},{138,1},{139,1},{140,1},{141,1},{142,1},{143,1},{144,1},
+ {145,1},{146,1},{147,1},{148,1},{149,1},{150,1},{151,2},{153,1},
+ {154,1},{155,1},{156,1},{157,1},{158,1},{159,1},{160,1},{161,1},
+ {162,1},{163,1},{164,1},{165,1},{166,1},{167,2},{169,1},{170,1},
+ {171,1},{172,1},{173,1},{174,4},{178,1},{179,1},{180,1},{181,4},
+ {185,1},{186,1},{187,5},{192,1},{193,1},{194,1},{195,1},{196,1},
+ {197,13},{210,1},{211,1},{212,1},{213,1},{214,1},{215,1},{216,1},
+ {217,1},{218,1},{219,1},{220,1},{221,1},{222,1},{223,1},{224,1},
+ {225,1},{226,1},{227,1},{228,1},{229,1},{230,1},{231,1},{232,1},
+ {233,1},{234,1},{235,1},{236,1},{237,2},{239,1},{240,1},{241,1},
+ {242,1},{243,2},{245,5},{250,1},{251,1},{252,1},{253,1},{254,1},
+ {255,1},{256,1},{257,1},
+};
+
+const char *const NAMES[] = {
+ "Afghanistan","Albania","Algeria","Angola",
+ "Antarctica","Argentina","Armenia","Australia",
+ "Austria","Azerbaijan","Bahamas","Bangladesh",
+ "Belarus","Belgium","Belize","Benin",
+ "Bhutan","Bolivia","Bosnia and Herz.","Botswana",
+ "Brazil","Bulgaria","Burkina Faso","Burundi",
+ "Cambodia","Cameroon","Canada","Central African Rep.",
+ "Chad","Chile","China","Colombia",
+ "Congo","Costa Rica","Côte d'Ivoire","Croatia",
+ "Cuba","Cyprus","Czechia","Dem. Rep. Congo",
+ "Denmark","Djibouti","Dominican Rep.","Ecuador",
+ "Egypt","El Salvador","Eq. Guinea","Eritrea",
+ "Estonia","eSwatini","Ethiopia","Falkland Is.",
+ "Finland","Fr. S. Antarctic Lands","France","Gabon",
+ "Gambia","Georgia","Germany","Ghana",
+ "Greece","Greenland","Guatemala","Guinea",
+ "Guinea-Bissau","Guyana","Haiti","Honduras",
+ "Hungary","Iceland","India","Indonesia",
+ "Iran","Iraq","Ireland","Israel",
+ "Italy","Jamaica","Japan","Jordan",
+ "Kazakhstan","Kenya","Kosovo","Kuwait",
+ "Kyrgyzstan","Laos","Latvia","Lebanon",
+ "Lesotho","Liberia","Libya","Lithuania",
+ "Madagascar","Malawi","Malaysia","Mali",
+ "Mauritania","Mexico","Moldova","Mongolia",
+ "Montenegro","Morocco","Mozambique","Myanmar",
+ "N. Cyprus","Namibia","Nepal","Netherlands",
+ "New Caledonia","New Zealand","Nicaragua","Niger",
+ "Nigeria","North Korea","North Macedonia","Norway",
+ "Oman","Pakistan","Panama","Papua New Guinea",
+ "Paraguay","Peru","Philippines","Poland",
+ "Portugal","Puerto Rico","Qatar","Romania",
+ "Russia","Rwanda","S. Sudan","Saudi Arabia",
+ "Senegal","Serbia","Sierra Leone","Slovakia",
+ "Slovenia","Solomon Is.","Somalia","Somaliland",
+ "South Africa","South Korea","Spain","Sri Lanka",
+ "Sudan","Suriname","Sweden","Switzerland",
+ "Syria","Taiwan","Tajikistan","Tanzania",
+ "Thailand","Timor-Leste","Togo","Tunisia",
+ "Turkey","Turkmenistan","Uganda","Ukraine",
+ "United Arab Emirates","United Kingdom","United States of America","Uruguay",
+ "Uzbekistan","Venezuela","Vietnam","W. Sahara",
+ "Yemen","Zambia","Zimbabwe",
+};
+
+const char *const ISO[] = {
+ "AF","AL","DZ","AO","AQ","AR","AM","AU","AT","AZ","BS","BD",
+ "BY","BE","BZ","BJ","BT","BO","BA","BW","BR","BG","BF","BI",
+ "KH","CM","CA","CF","TD","CL","CN","CO","CG","CR","CI","HR",
+ "CU","CY","CZ","CD","DK","DJ","DO","EC","EG","SV","GQ","ER",
+ "EE","SZ","ET","FK","FI","TF","FR","GA","GM","GE","DE","GH",
+ "GR","GL","GT","GN","GW","GY","HT","HN","HU","IS","IN","ID",
+ "IR","IQ","IE","IL","IT","JM","JP","JO","KZ","KE","XK","KW",
+ "KG","LA","LV","LB","LS","LR","LY","LT","MG","MW","MY","ML",
+ "MR","MX","MD","MN","ME","MA","MZ","MM","-99","NA","NP","NL",
+ "NC","NZ","NI","NE","NG","KP","MK","NO","OM","PK","PA","PG",
+ "PY","PE","PH","PL","PT","PR","QA","RO","RU","RW","SS","SA",
+ "SN","RS","SL","SK","SI","SB","SO","-99","ZA","KR","ES","LK",
+ "SD","SR","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TN",
+ "TR","TM","UG","UA","AE","GB","US","UY","UZ","VE","VN","EH",
+ "YE","ZM","ZW",
+};
+
+} // namespace
+
+/// Built once, on first use: the pool above is plain static data, and this
+/// turns it into the shape the rest of the code wants without a static
+/// initialiser that has to run before main.
+const std::vector &world_countries() {
+ static const std::vector countries = [] {
+ std::vector out;
+ out.reserve(171);
+ for (std::size_t i = 0; i < 171; i++) {
+ CountryOutline c;
+ c.name = NAMES[i];
+ c.iso = ISO[i];
+ const Span &cs = COUNTRIES[i];
+ for (int r = 0; r < cs.length; r++) {
+ const Span &rs = RINGS[cs.at + r];
+ c.rings.emplace_back(POOL + rs.at, POOL + rs.at + rs.length);
+ }
+ out.push_back(std::move(c));
+ }
+ return out;
+ }();
+ return countries;
+}
+
+} // namespace hqtui
diff --git a/ports/cpp/tests/conformance_widgets.cpp b/ports/cpp/tests/conformance_widgets.cpp
index d76bbc9..6557701 100644
--- a/ports/cpp/tests/conformance_widgets.cpp
+++ b/ports/cpp/tests/conformance_widgets.cpp
@@ -251,6 +251,23 @@ bool draw_scene(const std::string &name, Surface s) {
draw_shadow(s, Rect{2, 1, 6, 2}, sh);
return true;
}
+ if (name == "world") {
+ draw_world_map(s, {});
+ return true;
+ }
+ if (name == "world-zoom") {
+ WorldMap m;
+ m.x = Bounds{112, 156};
+ m.y = Bounds{24, 50};
+ draw_world_map(s, m);
+ return true;
+ }
+ if (name == "world-highlight") {
+ WorldMap m;
+ m.highlight = {"Brazil", "JP"};
+ draw_world_map(s, m);
+ return true;
+ }
if (name == "badge") {
{
Badge badge;
diff --git a/ports/go/conformance_widgets_test.go b/ports/go/conformance_widgets_test.go
index ff59e13..cc9c083 100644
--- a/ports/go/conformance_widgets_test.go
+++ b/ports/go/conformance_widgets_test.go
@@ -129,6 +129,13 @@ func drawWidgetScene(t *testing.T, name string, s Surface) {
solid := RGB(0x10, 0x14, 0x18)
DrawShadow(s, Rect{X: 2, Y: 1, Width: 6, Height: 2},
ShadowOptions{OffsetX: 1, OffsetY: 1, Color: &solid})
+ case "world":
+ DrawWorldMap(s, WorldMapOptions{})
+ case "world-zoom":
+ zx, zy := Bounds{112, 156}, Bounds{24, 50}
+ DrawWorldMap(s, WorldMapOptions{X: &zx, Y: &zy})
+ case "world-highlight":
+ DrawWorldMap(s, WorldMapOptions{Highlight: []string{"Brazil", "JP"}})
case "badge":
DrawBadge(s, BadgeOptions{Text: "LIVE"})
case "badge-outline":
diff --git a/ports/go/examples/world-probe/main.go b/ports/go/examples/world-probe/main.go
new file mode 100644
index 0000000..784ec61
--- /dev/null
+++ b/ports/go/examples/world-probe/main.go
@@ -0,0 +1,56 @@
+// Prints what the country lookup answers for a fixed set of points.
+//
+// The same probe exists for every port, so "the ports agree about the world" is
+// a diff rather than a hope.
+package main
+
+import (
+ "fmt"
+
+ hqtui "github.com/profullstack/hqtui/ports/go"
+)
+
+func main() {
+ places := []struct {
+ name string
+ lon, lat float64
+ }{
+ {"Paris", 2.35, 48.86},
+ {"Tokyo", 139.7, 35.7},
+ {"Cairo", 31.2, 30.0},
+ {"Brasilia", -47.9, -15.8},
+ {"Canberra", 149.1, -35.3},
+ {"Denver", -105.0, 39.7},
+ {"Moscow", 37.6, 55.75},
+ {"Delhi", 77.2, 28.6},
+ {"Nairobi", 36.8, -1.3},
+ {"Pacific", -140.0, 0.0},
+ {"Atlantic", -30.0, 0.0},
+ {"SouthernOcean", 80.0, -40.0},
+ {"NorthPacific", -150.0, 40.0},
+ }
+ for _, p := range places {
+ name := "-"
+ if c := hqtui.CountryAt(p.lon, p.lat); c != nil {
+ name = c.Name
+ }
+ fmt.Printf("%s %s\n", p.name, name)
+ }
+
+ // The cell path, which has to agree with what the canvas drew.
+ cells := [][2]int{{173, 28}, {74, 2}, {20, 25}, {88, 7}}
+ for _, cell := range cells {
+ name := "-"
+ if c := hqtui.CountryAtCell(cell[0], cell[1], 200, 50, hqtui.WorldMapOptions{}); c != nil {
+ name = c.Name
+ }
+ fmt.Printf("cell:%d,%d %s\n", cell[0], cell[1], name)
+ }
+
+ // And the projection itself, so a drift shows up as a number rather than as
+ // a country that happens to still be right.
+ for _, cell := range [][2]int{{0, 0}, {99, 25}, {50, 13}} {
+ lon, lat, _ := hqtui.DegreesAt(cell[0], cell[1], 100, 26, hqtui.WorldX, hqtui.WorldY)
+ fmt.Printf("degrees:%d,%d %.4f %.4f\n", cell[0], cell[1], lon, lat)
+ }
+}
diff --git a/ports/go/ui.go b/ports/go/ui.go
index da9fc56..d59d074 100644
--- a/ports/go/ui.go
+++ b/ports/go/ui.go
@@ -523,6 +523,19 @@ func (c *Container) Shapes(o CanvasOptions, layout ...Layout) *Container {
return c.add(c.filling(firstLayout(layout)), func(s Surface) { DrawCanvas(s, o) })
}
+// WorldMap draws a world map and reports the country under whatever is clicked.
+//
+// The click is answered by turning the cell back into degrees and testing it
+// against the outlines, so the answer is the country actually under the cursor.
+// Bounding boxes would be cheaper and wrong: Russia's covers most of the
+// northern hemisphere and Chile's covers Argentina.
+func (c *Container) WorldMap(o WorldMapOptions, h ScrollHandlers, layout ...Layout) *Container {
+ return c.add(c.filling(firstLayout(layout)), func(s Surface) {
+ DrawWorldMap(s, o)
+ c.attachScroll(s, h, 0)
+ })
+}
+
func (c *Container) Sparkline(o SparklineWidgetOptions, layout ...Layout) *Container {
return c.add(c.leaf(firstLayout(layout), 1), func(s Surface) { DrawSparkline(s, o) })
}
diff --git a/ports/go/widgets_world.go b/ports/go/widgets_world.go
new file mode 100644
index 0000000..c367f53
--- /dev/null
+++ b/ports/go/widgets_world.go
@@ -0,0 +1,71 @@
+package hqtui
+
+// A world map you can click.
+//
+// The drawing is the canvas doing what it already does — polylines in the
+// caller's own coordinates, which for a map are degrees. What this adds is the
+// other direction: turning a click back into a country.
+
+type WorldMapOptions struct {
+ // X and Y are the window on the globe. Nil means all of it.
+ X *Bounds
+ Y *Bounds
+ // Color is the coastline colour.
+ Color *Color
+ // Highlight names countries to pick out, by name or ISO code.
+ Highlight []string
+ HighlightColor *Color
+ Background *Color
+ Grid bool
+}
+
+func (o WorldMapOptions) window() (Bounds, Bounds) {
+ x, y := WorldX, WorldY
+ if o.X != nil {
+ x = *o.X
+ }
+ if o.Y != nil {
+ y = *o.Y
+ }
+ return x, y
+}
+
+func DrawWorldMap(s Surface, o WorldMapOptions) {
+ if s.IsEmpty() {
+ return
+ }
+ theme := s.Theme
+ color := theme.Border
+ if o.Color != nil {
+ color = *o.Color
+ }
+ highlight := theme.Accent
+ if o.HighlightColor != nil {
+ highlight = *o.HighlightColor
+ }
+ x, y := o.window()
+ DrawCanvas(s, CanvasOptions{
+ Shapes: WorldShapes(WorldShapeOptions{
+ Color: &color,
+ Highlight: o.Highlight,
+ HighlightColor: &highlight,
+ }),
+ X: &x,
+ Y: &y,
+ Background: o.Background,
+ Grid: o.Grid,
+ })
+}
+
+// CountryAtCell is the country under a cell of a map drawn with these bounds.
+//
+// Exposed so a caller can answer a hover as well as a click, and so the
+// arithmetic that has to agree with the drawing lives in one place.
+func CountryAtCell(column, row, width, height int, o WorldMapOptions) *CountryOutline {
+ x, y := o.window()
+ lon, lat, ok := DegreesAt(column, row, width, height, x, y)
+ if !ok {
+ return nil
+ }
+ return CountryAt(lon, lat)
+}
diff --git a/ports/go/world.go b/ports/go/world.go
new file mode 100644
index 0000000..a3e5d38
--- /dev/null
+++ b/ports/go/world.go
@@ -0,0 +1,178 @@
+package hqtui
+
+import (
+ "math"
+ "strings"
+)
+
+// The world, as shapes for the canvas, and the lookup that makes it clickable.
+//
+// The canvas already draws in the caller's own coordinates, and longitude and
+// latitude are just another pair of axes — so a map is a list of polylines in
+// degrees, and nothing here needs a projection of its own beyond deciding which
+// window on the globe to show.
+//
+// The interesting half 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 testing it against the outlines. Doing
+// it that way rather than with bounding boxes is what makes the answer right:
+// Russia's bounding box covers most of the northern hemisphere, and Chile's
+// covers Argentina.
+
+// WorldX and WorldY are the whole globe, which is what a map shows unless told
+// otherwise.
+var (
+ WorldX = Bounds{Min: -180, Max: 180}
+ WorldY = Bounds{Min: -90, Max: 90}
+)
+
+type WorldShapeOptions struct {
+ // Color is for countries with nothing special about them.
+ Color *Color
+ // Highlight names countries to pick out, by name or ISO code.
+ Highlight []string
+ HighlightColor *Color
+}
+
+// countryMatches matches on either the name or the ISO code, case-insensitively.
+func countryMatches(c CountryOutline, keys []string) bool {
+ for _, key := range keys {
+ if key == "" {
+ continue
+ }
+ if strings.EqualFold(c.Name, key) {
+ return true
+ }
+ if c.ISO != "" && strings.EqualFold(c.ISO, key) {
+ return true
+ }
+ }
+ return false
+}
+
+// WorldShapes is the world as canvas shapes, one polyline per landmass.
+//
+// Polylines rather than scattered points: the outlines are closed rings, so
+// joining them draws a coastline instead of a dotted suggestion of one, and it
+// reads at a fraction of the resolution dots would need.
+func WorldShapes(o WorldShapeOptions) []Shape {
+ shapes := make([]Shape, 0, 300)
+ for _, country := range WorldCountries {
+ color := o.Color
+ if len(o.Highlight) > 0 && countryMatches(country, o.Highlight) && o.HighlightColor != nil {
+ color = o.HighlightColor
+ }
+ for _, ring := range country.Rings {
+ points := make([]Point, 0, len(ring)/2+1)
+ for i := 0; i+1 < len(ring); i += 2 {
+ points = append(points, Point{X: ring[i], Y: ring[i+1]})
+ }
+ // Closed: the last point joins the first, or every country has a gap
+ // in its coastline where the ring started.
+ if len(points) > 0 {
+ points = append(points, points[0])
+ }
+ shapes = append(shapes, Shape{Kind: ShapePolyline, Points: points, Color: color})
+ }
+ }
+ return shapes
+}
+
+// insideRing reports whether a point is inside a ring, by ray casting.
+//
+// The ring is a flat list of interleaved coordinates, so this walks it two at a
+// time rather than allocating a pair per vertex — it runs once per country per
+// click, and there are a couple of thousand vertices.
+func insideRing(ring []float64, lon, lat float64) bool {
+ inside := false
+ n := len(ring) / 2
+ if n == 0 {
+ return false
+ }
+ j := n - 1
+ for i := 0; i < n; i++ {
+ xi, yi := ring[i*2], ring[i*2+1]
+ xj, yj := ring[j*2], ring[j*2+1]
+ if (yi > lat) != (yj > lat) && lon < (xj-xi)*(lat-yi)/(yj-yi)+xi {
+ inside = !inside
+ }
+ j = i
+ }
+ return inside
+}
+
+// CountryAt is the country containing a point, or nil for open water.
+//
+// Where outlines overlap — and at this resolution simplified borders do overlap
+// — the first match wins, which is stable because the data is sorted by name.
+func CountryAt(lon, lat float64) *CountryOutline {
+ if math.IsNaN(lon) || math.IsNaN(lat) || math.IsInf(lon, 0) || math.IsInf(lat, 0) {
+ return nil
+ }
+ for i := range WorldCountries {
+ for _, ring := range WorldCountries[i].Rings {
+ if insideRing(ring, lon, lat) {
+ return &WorldCountries[i]
+ }
+ }
+ }
+ return nil
+}
+
+// FindCountry looks a country up by name or ISO code.
+func FindCountry(key string) *CountryOutline {
+ keys := []string{key}
+ for i := range WorldCountries {
+ if countryMatches(WorldCountries[i], keys) {
+ return &WorldCountries[i]
+ }
+ }
+ return nil
+}
+
+// CountryBounds is the window a country fills, with a little room around it.
+//
+// For zooming a map to a country: the bounding box alone puts the coastline
+// flat against the edge of the panel, which reads as though the country has
+// been cut off rather than framed.
+func CountryBounds(country *CountryOutline, margin float64) (Bounds, Bounds) {
+ minLon, maxLon := math.Inf(1), math.Inf(-1)
+ minLat, maxLat := math.Inf(1), math.Inf(-1)
+ for _, ring := range country.Rings {
+ for i := 0; i+1 < len(ring); i += 2 {
+ minLon = math.Min(minLon, ring[i])
+ maxLon = math.Max(maxLon, ring[i])
+ minLat = math.Min(minLat, ring[i+1])
+ maxLat = math.Max(maxLat, ring[i+1])
+ }
+ }
+ if math.IsInf(minLon, 0) {
+ return WorldX, WorldY
+ }
+ // A single-point country would give a zero-width window, which cannot be
+ // mapped onto anything.
+ padX := math.Max((maxLon-minLon)*margin, 1)
+ padY := math.Max((maxLat-minLat)*margin, 1)
+ return Bounds{Min: minLon - padX, Max: maxLon + padX},
+ Bounds{Min: minLat - padY, Max: maxLat + padY}
+}
+
+// DegreesAt is the degrees under a terminal cell, given the window the map was
+// drawn with.
+//
+// The inverse of what the canvas does on the way in, taken at the centre of the
+// cell: a click lands on a whole cell, and the centre is the only point in it
+// that is not arbitrarily nearer one neighbour than the other.
+func DegreesAt(column, row, width, height int, x, y Bounds) (lon, lat float64, ok bool) {
+ if width <= 0 || height <= 0 {
+ return 0, 0, false
+ }
+ // The canvas is 2x4 Braille pixels per cell, and it spans its bounds across
+ // `pixels - 1`, so the inverse has to use the same denominators or a click
+ // drifts from what was drawn.
+ px := math.Max(1, float64(width*2-1))
+ py := math.Max(1, float64(height*4-1))
+ lon = x.Min + (float64(column*2+1)/px)*(x.Max-x.Min)
+ lat = y.Min + (1-float64(row*4+2)/py)*(y.Max-y.Min)
+ return lon, lat, true
+}
diff --git a/ports/go/world_data.go b/ports/go/world_data.go
new file mode 100644
index 0000000..853223b
--- /dev/null
+++ b/ports/go/world_data.go
@@ -0,0 +1,1308 @@
+package hqtui
+
+// Country outlines, flattened for a terminal.
+//
+// Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's
+// 1:110m Admin 0 countries, which is public domain. Do not edit by hand.
+//
+// Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --
+// rather than a list of pairs, because at 1982 points the nested form
+// costs a container per coordinate for no gain. A country has more than one
+// ring when it is more than one landmass.
+//
+// 171 countries, 1982 points, simplified at 1 degrees.
+
+type CountryOutline struct {
+ Name string
+ // ISO 3166-1 alpha-2, where Natural Earth has one.
+ ISO string
+ // Rings are longitude and latitude, interleaved.
+ Rings [][]float64
+}
+
+var WorldCountries = []CountryOutline{
+ {
+ Name: "Afghanistan",
+ ISO: "AF",
+ Rings: [][]float64{
+ {66.5,37.4,70.8,38.5,71.8,36.7,75.2,37.1,71.3,36.1,69.3,31.9,66.3,29.9,60.9,29.8,61.2,35.7,66.5,37.4},
+ },
+ },
+ {
+ Name: "Albania",
+ ISO: "AL",
+ Rings: [][]float64{
+ {21,40.8,19.4,40.3,19.7,42.7,21,40.8},
+ },
+ },
+ {
+ Name: "Algeria",
+ ISO: "DZ",
+ Rings: [][]float64{
+ {-8.7,27.4,-8.7,28.8,-1.3,32.3,-1.2,35.7,8.4,36.9,7.5,34.1,9.8,29.4,9.3,26.1,12,23.5,3.2,19.1,-8.7,27.4},
+ },
+ },
+ {
+ Name: "Angola",
+ ISO: "AO",
+ Rings: [][]float64{
+ {12.3,-6.1,16.3,-5.9,17.5,-8.1,21.7,-7.3,22.2,-11.1,24,-11.2,24,-12.9,21.9,-12.9,23.2,-17.5,11.7,-17.3,13.7,-11.3,12.3,-6.1},
+ },
+ },
+ {
+ Name: "Antarctica",
+ ISO: "AQ",
+ Rings: [][]float64{
+ {-48.7,-78,-43.9,-78.5,-43.3,-80,-54.2,-80.6,-48.7,-78},
+ {-66.3,-80.3,-59.6,-80,-66.3,-80.3},
+ {-73.9,-71.3,-70.3,-68.9,-68.3,-71.4,-75,-72.1,-73.9,-71.3},
+ {-102.3,-71.9,-96.2,-72.5,-102.3,-71.9},
+ {-122.6,-73.7,-118.7,-73.5,-122.6,-73.7},
+ {-127.3,-73.5,-124,-73.9,-127.3,-73.5},
+ {-163.7,-78.6,-159.2,-79.5,-163.7,-78.6},
+ {180,-84.7,180,-90,-180,-90,-179.1,-84.1,-143.1,-85,-153.6,-83.7,-152.9,-82,-156.8,-81.1,-146.4,-80.3,-155.3,-79.1,-158.4,-76.9,-151.3,-77.4,-146.1,-76.5,-146.2,-75.4,-135.2,-74.3,-100.1,-74.9,-103.7,-72.6,-74.9,-73.9,-67.4,-72.5,-67.7,-67.3,-57.8,-63.3,-65.7,-68,-61.8,-70.7,-60.8,-73.7,-70.6,-76.6,-77.2,-76.7,-73.7,-77.9,-78,-79.2,-58.2,-83.2,-28.5,-80.3,-35.6,-79.5,-35.8,-78.3,-17.5,-75.1,-15.4,-73.1,-6.9,-70.9,27.1,-70.5,33.9,-68.5,38.6,-69.8,54.5,-65.8,61.4,-68,68.9,-67.9,69.7,-69.2,67.9,-71.9,69.9,-72.3,73.9,-69.9,88,-66.2,95.8,-67.4,102.8,-65.6,106.2,-66.9,113.6,-65.9,119.8,-67.3,135.1,-65.3,137.5,-67,145.5,-66.9,171.2,-71.7,163.6,-76.2,167,-78.8,161.8,-79.2,159.8,-80.9,169.4,-83.8,180,-84.7},
+ },
+ },
+ {
+ Name: "Argentina",
+ ISO: "AR",
+ Rings: [][]float64{
+ {-68.6,-52.6,-65,-54.7,-68.6,-54.9,-68.6,-52.6},
+ {-57.6,-30.2,-58.5,-34.4,-56.8,-36.9,-62.3,-38.8,-62.7,-41,-65.1,-41.1,-63.5,-42.6,-67.3,-45.6,-65.6,-47.2,-69.1,-50.7,-68.1,-52.3,-71.9,-52,-73.4,-49.3,-71.2,-44.8,-72.1,-42.3,-68.4,-24.5,-66.3,-21.8,-62.8,-22,-57.8,-25.2,-58.6,-27.1,-55.7,-27.4,-54.1,-25.5,-53.6,-26.9,-57.6,-30.2},
+ },
+ },
+ {
+ Name: "Armenia",
+ ISO: "AM",
+ Rings: [][]float64{
+ {46.5,38.8,43.6,41.1,45.6,40.8,46.5,38.8},
+ },
+ },
+ {
+ Name: "Australia",
+ ISO: "AU",
+ Rings: [][]float64{
+ {147.7,-40.8,147.9,-43.2,146,-43.5,144.7,-40.7,147.7,-40.8},
+ {126.1,-32.2,118,-35.1,115,-34.2,113.7,-22.5,120.9,-19.7,125.7,-14.2,129.6,-15,132.4,-11.1,136.5,-11.9,135.5,-15,140.2,-17.7,142.5,-10.7,146.4,-19,150.7,-22.4,153.6,-28.1,150,-37.4,146.3,-39,140.6,-38,138.2,-34.4,136.8,-35.3,137.8,-32.9,136,-34.9,131.3,-31.5,126.1,-32.2},
+ },
+ },
+ {
+ Name: "Austria",
+ ISO: "AT",
+ Rings: [][]float64{
+ {17,48.1,14.6,46.4,9.5,47.1,12.9,47.5,13.6,48.9,17,48.1},
+ },
+ },
+ {
+ Name: "Azerbaijan",
+ ISO: "AZ",
+ Rings: [][]float64{
+ {46.4,41.9,50.4,40.3,48.9,38.3,45.6,39.9,45,41.2,46.4,41.9},
+ },
+ },
+ {
+ Name: "Bahamas",
+ ISO: "BS",
+ Rings: [][]float64{
+ {-78.2,25.2,-77.5,23.8,-78.2,25.2},
+ },
+ },
+ {
+ Name: "Bangladesh",
+ ISO: "BD",
+ Rings: [][]float64{
+ {92.7,22,92.4,20.7,91.4,22.8,89,22.1,88.6,26.4,92.4,25,91.2,23.5,92.7,22},
+ },
+ },
+ {
+ Name: "Belarus",
+ ISO: "BY",
+ Rings: [][]float64{
+ {28.2,56.2,30.9,55.6,32.7,53.4,31.8,52.1,23.5,51.6,23.5,53.9,28.2,56.2},
+ },
+ },
+ {
+ Name: "Belgium",
+ ISO: "BE",
+ Rings: [][]float64{
+ {6.2,50.8,5.7,49.5,2.5,51.1,6.2,50.8},
+ },
+ },
+ {
+ Name: "Belize",
+ ISO: "BZ",
+ Rings: [][]float64{
+ {-89.1,17.8,-88.1,18.3,-88.9,15.9,-89.1,17.8},
+ },
+ },
+ {
+ Name: "Benin",
+ ISO: "BJ",
+ Rings: [][]float64{
+ {2.7,6.3,0.8,10.5,2.8,12.2,2.7,6.3},
+ },
+ },
+ {
+ Name: "Bhutan",
+ ISO: "BT",
+ Rings: [][]float64{
+ {91.7,27.8,88.8,27.1,91.7,27.8},
+ },
+ },
+ {
+ Name: "Bolivia",
+ ISO: "BO",
+ Rings: [][]float64{
+ {-69.5,-11,-65.3,-9.8,-65.4,-11.6,-60.5,-13.8,-60.2,-16.3,-58.2,-16.3,-57.9,-20,-61.8,-19.6,-62.7,-22.2,-67.8,-22.9,-69.5,-11},
+ },
+ },
+ {
+ Name: "Bosnia and Herz.",
+ ISO: "BA",
+ Rings: [][]float64{
+ {18.6,42.7,16,45.2,19.4,44.9,18.6,42.7},
+ },
+ },
+ {
+ Name: "Botswana",
+ ISO: "BW",
+ Rings: [][]float64{
+ {29.4,-22.1,25.7,-25.5,21.6,-26.7,19.9,-24.8,20.9,-18.3,25.3,-17.7,29.4,-22.1},
+ },
+ },
+ {
+ Name: "Brazil",
+ ISO: "BR",
+ Rings: [][]float64{
+ {-53.4,-33.8,-53.8,-32,-57.6,-30.2,-53.6,-26.1,-55.8,-22.4,-57.9,-22.1,-58.2,-16.3,-60.2,-16.3,-60.5,-13.8,-65.4,-11.6,-65.3,-9.8,-70.5,-11,-70.5,-9.5,-72.2,-10.1,-74,-7.5,-72.9,-5.3,-69.9,-4.3,-69.8,1.7,-65.5,0.8,-63.4,2.2,-64.8,4.1,-60.7,5.2,-59,1.3,-52.9,2.1,-51.3,4.2,-50.4,-0.1,-44.6,-2.7,-40,-2.9,-35.6,-5.1,-34.7,-7.3,-38.7,-13.1,-40.9,-21.9,-47.6,-24.9,-48.9,-28.7,-53.4,-33.8},
+ },
+ },
+ {
+ Name: "Bulgaria",
+ ISO: "BG",
+ Rings: [][]float64{
+ {22.7,44.2,28.6,43.7,28,42,23,41.3,22.7,44.2},
+ },
+ },
+ {
+ Name: "Burkina Faso",
+ ISO: "BF",
+ Rings: [][]float64{
+ {-5.4,10.4,-4.3,13.2,-1.1,15,2.2,12.6,0.9,11,-2.9,11,-2.8,9.6,-5.4,10.4},
+ },
+ },
+ {
+ Name: "Burundi",
+ ISO: "BI",
+ Rings: [][]float64{
+ {30.5,-2.4,29.3,-4.5,29,-2.8,30.5,-2.4},
+ },
+ },
+ {
+ Name: "Cambodia",
+ ISO: "KH",
+ Rings: [][]float64{
+ {102.6,12.2,103,14.2,107.6,13.5,106.2,11,103.5,10.6,102.6,12.2},
+ },
+ },
+ {
+ Name: "Cameroon",
+ ISO: "CM",
+ Rings: [][]float64{
+ {14.5,12.9,14.5,4.7,15.9,1.7,9.6,2.3,8.8,5.5,11.7,7,14.5,12.9},
+ },
+ },
+ {
+ Name: "Canada",
+ ISO: "CA",
+ Rings: [][]float64{
+ {-122.8,49,-127.4,50.8,-130.5,54.3,-130,55.9,-135.5,59.8,-137.5,58.9,-141,60.3,-141,69.7,-136.5,68.9,-128.1,70.5,-113.5,67.7,-106.1,68.8,-101.5,67.6,-97.7,68.6,-96.1,67.3,-94.2,69.1,-96.5,70.1,-95.2,71.9,-87.4,67.2,-85.5,69.9,-82.6,69.7,-81.4,67.1,-85.8,66.6,-90.7,63.6,-94.7,58.9,-92.3,57.1,-82.3,55.1,-79.9,51.2,-78.6,52.6,-79.8,54.7,-76.5,56.5,-78.5,58.8,-77.3,59.9,-78.1,62.3,-73.8,62.4,-69.6,61.1,-67.6,58.2,-64.6,60.3,-61.8,56.3,-57.3,54.6,-55.7,52.1,-60,50.2,-66.4,50.2,-71.1,46.8,-65.1,49.2,-64.5,46.2,-60.5,47,-59.8,45.9,-65.4,43.5,-66.2,44.5,-64.4,45.3,-67.1,45.1,-69.2,47.4,-71.5,45,-82.4,41.7,-82.6,45.3,-88.4,48.3,-122.8,49},
+ {-84,62.5,-81.9,62.9,-84,62.5},
+ {-79.8,72.8,-80.8,73.7,-76.3,72.8,-79.8,72.8},
+ {-93.6,75,-96.8,74.9,-93.6,75},
+ {-93.8,77.5,-96.4,77.8,-93.8,77.5},
+ {-96.8,78.8,-95.6,78.4,-98.6,78.9,-96.8,78.8},
+ {-88.2,74.4,-97.1,76.8,-79.8,74.9,-88.2,74.4},
+ {-111.3,78.2,-109.9,78,-113.5,77.7,-111.3,78.2},
+ {-111,78.8,-109.7,78.6,-112.5,78.4,-111,78.8},
+ {-55.6,51.3,-56.8,49.8,-53.5,49.2,-53.1,46.7,-59.3,47.6,-55.6,51.3},
+ {-83.9,65.1,-80.1,63.7,-87.2,63.5,-85.9,65.7,-83.9,65.1},
+ {-78.8,72.4,-68.8,70.5,-67,69.2,-68.8,68.7,-61.9,66.9,-63.9,65,-68,66.3,-64.7,63.4,-68.8,63.7,-66.2,61.9,-68.9,62.3,-78.6,64.6,-74,65.5,-73.3,68.1,-79,70.2,-88.7,70.4,-90.2,72.2,-85.8,73.8,-85.8,72.5,-82.3,73.8,-78.8,72.4},
+ {-94.5,74.1,-90.5,73.9,-95.4,72.1,-96,73.4,-94.5,74.1},
+ {-122.9,76.1,-116.2,77.6,-122.9,76.1},
+ {-132.7,54,-131.2,52.2,-132.7,54},
+ {-105.5,79.3,-99.7,77.9,-105.5,79.3},
+ {-123.5,48.5,-128.4,50.8,-123.5,48.5},
+ {-121.5,74.4,-115.5,73.5,-123.1,70.9,-125.9,71.9,-123.9,73.7,-124.9,74.3,-121.5,74.4},
+ {-107.8,75.8,-105.7,75.5,-117.7,75.2,-115.4,76.5,-107.8,75.8},
+ {-106.5,73.1,-101.1,69.6,-113.3,68.5,-117.3,70,-112.4,70.4,-119.4,71.6,-115.2,73.3,-108.2,71.7,-108.4,73.1,-106.5,73.1},
+ {-100.4,72.7,-101.5,73.4,-97.4,73.8,-96.5,72.6,-98.4,71.3,-102.5,72.5,-100.4,72.7},
+ {-106.6,73.6,-104.5,73.4,-106.6,73.6},
+ {-98.5,76.7,-98.2,75,-102.5,75.6,-98.5,76.7},
+ {-96,80.6,-92.4,81.3,-85.8,79.3,-92.9,78.3,-96,80.6},
+ {-91.6,81.9,-61.8,82.6,-76.9,79.3,-75.4,78.5,-80.6,76.2,-89.5,76.5,-88.3,77.9,-85,77.5,-88,78.4,-85.1,79.3,-86.9,80.3,-81.8,80.5,-91.6,81.9},
+ {-75.2,67.4,-77.2,67.6,-75.2,67.4},
+ {-96.3,69.5,-99.8,69.4,-96.3,69.5},
+ {-64.5,49.9,-61.8,49.1,-64.5,49.9},
+ {-64,47,-62,46.4,-64,47},
+ },
+ },
+ {
+ Name: "Central African Rep.",
+ ISO: "CF",
+ Rings: [][]float64{
+ {27.4,5.2,22.4,4,19.5,5,16,2.3,14.5,5.5,15.3,7.4,22.9,11.1,27.4,5.2},
+ },
+ },
+ {
+ Name: "Chad",
+ ISO: "TD",
+ Rings: [][]float64{
+ {23.8,19.6,23.9,15.6,21.9,12.6,22.9,11.1,15.3,7.4,13.5,14.4,15.9,20.4,14.9,22.9,23.8,19.6},
+ },
+ },
+ {
+ Name: "Chile",
+ ISO: "CL",
+ Rings: [][]float64{
+ {-68.6,-52.6,-68.6,-54.9,-67,-54.9,-68.1,-55.6,-74.7,-52.8,-71.1,-54.1,-68.6,-52.6},
+ {-69.6,-17.6,-67,-23,-70.5,-31.4,-69.8,-34.2,-72.1,-42.3,-71.2,-44.8,-73.4,-49.3,-71.9,-52,-68.6,-52.3,-71.4,-53.9,-74.9,-52.3,-75.6,-48.7,-74.1,-46.9,-75.6,-46.6,-72.7,-42.4,-74.3,-43.2,-69.6,-17.6},
+ },
+ },
+ {
+ Name: "China",
+ ISO: "CN",
+ Rings: [][]float64{
+ {109.5,18.2,108.6,19.4,110.8,20.1,109.5,18.2},
+ {80.3,42.3,80,44.9,87.8,49.3,91,46.9,90.9,45.3,96.3,42.7,109.2,42.5,111.9,45.1,119.7,46.7,115.5,48.1,122.2,53.4,125.9,52.8,131,47.8,135,48.5,133.1,45.1,131,45,130.6,42.4,121.1,38.9,121.6,40.9,117.5,38.7,122.4,37.5,119.2,34.9,121.9,31.7,121.7,28.2,118.7,24.5,110.4,20.3,105.3,23.4,101.7,22.3,101.8,21.2,99.2,22.1,97.6,23.9,98.7,27.5,96.1,29.5,88.8,27.3,78.7,31.5,78.9,34.3,73.7,39.4,80.3,42.3},
+ },
+ },
+ {
+ Name: "Colombia",
+ ISO: "CO",
+ Rings: [][]float64{
+ {-66.9,1.3,-69.8,1.7,-69.9,-4.3,-70,-2.7,-77.4,0.4,-79,1.7,-77.1,3.8,-77.5,8.5,-71.4,12.4,-73.3,9.2,-72,7,-67.3,6.1,-66.9,1.3},
+ },
+ },
+ {
+ Name: "Congo",
+ ISO: "CG",
+ Rings: [][]float64{
+ {18.5,3.5,16,-3.5,11.9,-5,11.5,-2.8,14.4,-1.3,13.1,2.3,15.9,1.7,18.5,3.5},
+ },
+ },
+ {
+ Name: "Costa Rica",
+ ISO: "CR",
+ Rings: [][]float64{
+ {-82.5,9.6,-83,8.2,-85.9,10.9,-82.5,9.6},
+ },
+ },
+ {
+ Name: "Côte d'Ivoire",
+ ISO: "CI",
+ Rings: [][]float64{
+ {-8,10.2,-2.8,9.6,-2.9,5,-7.7,4.4,-8,10.2},
+ },
+ },
+ {
+ Name: "Croatia",
+ ISO: "HR",
+ Rings: [][]float64{
+ {16.6,46.5,19.4,45.2,15.8,44.8,18.5,42.5,13.7,45.1,16.6,46.5},
+ },
+ },
+ {
+ Name: "Cuba",
+ ISO: "CU",
+ Rings: [][]float64{
+ {-82.3,23.2,-74.2,20.3,-77.8,19.9,-81.8,22.6,-85,21.9,-82.3,23.2},
+ },
+ },
+ {
+ Name: "Cyprus",
+ ISO: "CY",
+ Rings: [][]float64{
+ {32.7,35.1,34,35,32.7,35.1},
+ },
+ },
+ {
+ Name: "Czechia",
+ ISO: "CZ",
+ Rings: [][]float64{
+ {15,51.1,18.9,49.5,12.5,49.5,15,51.1},
+ },
+ },
+ {
+ Name: "Dem. Rep. Congo",
+ ISO: "CD",
+ Rings: [][]float64{
+ {29.3,-4.5,30.7,-8.3,28.7,-8.5,28.4,-11.8,29.7,-13.3,22.2,-11.1,21.7,-7.3,17.5,-8.1,16.3,-5.9,12.2,-5.8,16,-3.5,19.5,5,29.7,4.6,31.2,2.2,29.3,-4.5},
+ },
+ },
+ {
+ Name: "Denmark",
+ ISO: "DK",
+ Rings: [][]float64{
+ {9.9,55,8.1,56.5,10.6,57.7,9.9,55},
+ {12.4,56.1,12.1,54.8,11,55.4,12.4,56.1},
+ },
+ },
+ {
+ Name: "Djibouti",
+ ISO: "DJ",
+ Rings: [][]float64{
+ {42.4,12.5,42.8,10.9,42.4,12.5},
+ },
+ },
+ {
+ Name: "Dominican Rep.",
+ ISO: "DO",
+ Rings: [][]float64{
+ {-71.7,18,-71.6,19.9,-68.3,18.6,-71.7,18},
+ },
+ },
+ {
+ Name: "Ecuador",
+ ISO: "EC",
+ Rings: [][]float64{
+ {-75.4,-0.2,-78.6,-4.5,-80.4,-4.4,-80.1,0.8,-75.4,-0.2},
+ },
+ },
+ {
+ Name: "Egypt",
+ ISO: "EG",
+ Rings: [][]float64{
+ {36.9,22,25,22,25.2,31.6,34.3,31.2,34.2,27.8,32.3,29.8,36.9,22},
+ },
+ },
+ {
+ Name: "El Salvador",
+ ISO: "SV",
+ Rings: [][]float64{
+ {-89.4,14.4,-87.9,13.1,-90.1,13.7,-89.4,14.4},
+ },
+ },
+ {
+ Name: "Eq. Guinea",
+ ISO: "GQ",
+ Rings: [][]float64{
+ {9.6,2.3,11.3,1.1,9.5,1,9.6,2.3},
+ },
+ },
+ {
+ Name: "Eritrea",
+ ISO: "ER",
+ Rings: [][]float64{
+ {36.4,14.4,38.4,18,43.1,12.7,36.4,14.4},
+ },
+ },
+ {
+ Name: "Estonia",
+ ISO: "EE",
+ Rings: [][]float64{
+ {28,59.5,27.3,57.5,23.3,59.2,28,59.5},
+ },
+ },
+ {
+ Name: "eSwatini",
+ ISO: "SZ",
+ Rings: [][]float64{
+ {32.1,-26.7,31,-25.7,32.1,-26.7},
+ },
+ },
+ {
+ Name: "Ethiopia",
+ ISO: "ET",
+ Rings: [][]float64{
+ {47.8,8,45,5,39.6,3.4,36.2,4.4,33,7.8,37.9,15,41.6,13.5,43.7,9.2,47.8,8},
+ },
+ },
+ {
+ Name: "Falkland Is.",
+ ISO: "FK",
+ Rings: [][]float64{
+ {-61.2,-51.8,-57.7,-51.5,-61.2,-51.8},
+ },
+ },
+ {
+ Name: "Finland",
+ ISO: "FI",
+ Rings: [][]float64{
+ {28.6,69.1,31.1,62.4,28.1,60.5,21.3,60.7,21.5,63.2,25.4,65.1,20.6,69.1,24.7,68.6,27.7,70.2,28.6,69.1},
+ },
+ },
+ {
+ Name: "Fr. S. Antarctic Lands",
+ ISO: "TF",
+ Rings: [][]float64{
+ {68.9,-48.6,70.6,-49.3,68.7,-49.8,68.9,-48.6},
+ },
+ },
+ {
+ Name: "France",
+ ISO: "FR",
+ Rings: [][]float64{
+ {-51.7,4.2,-52.9,2.1,-54.5,2.3,-54,5.8,-51.7,4.2},
+ {6.2,49.5,8.1,49,6,46.7,7.4,43.7,1.8,42.3,-1.9,43.4,-1.2,46,-4.6,48.7,-1.6,48.6,-1.9,49.8,2.5,51.1,6.2,49.5},
+ {8.7,42.6,9.2,41.4,8.7,42.6},
+ },
+ },
+ {
+ Name: "Gabon",
+ ISO: "GA",
+ Rings: [][]float64{
+ {11.3,2.3,14.3,1.2,14.4,-1.3,11.1,-4,8.8,-1.1,11.3,2.3},
+ },
+ },
+ {
+ Name: "Gambia",
+ ISO: "GM",
+ Rings: [][]float64{
+ {-16.7,13.6,-13.8,13.5,-16.7,13.6},
+ },
+ },
+ {
+ Name: "Georgia",
+ ISO: "GE",
+ Rings: [][]float64{
+ {40,43.4,46.6,41.2,41.6,41.5,40,43.4},
+ },
+ },
+ {
+ Name: "Germany",
+ ISO: "DE",
+ Rings: [][]float64{
+ {14.1,53.8,15,51.1,12.2,50.3,12.9,47.5,7.5,47.6,8.1,49,6,50.1,7.1,53.7,9.9,55,14.1,53.8},
+ },
+ },
+ {
+ Name: "Ghana",
+ ISO: "GH",
+ Rings: [][]float64{
+ {0,11,1.1,5.9,-2.9,5,-2.9,11,0,11},
+ },
+ },
+ {
+ Name: "Greece",
+ ISO: "GR",
+ Rings: [][]float64{
+ {26.3,35.3,23.5,35.3,26.3,35.3},
+ {23,41.3,26.6,41.6,22.6,40.3,24,37.7,22.5,36.4,20.2,39.6,23,41.3},
+ },
+ },
+ {
+ Name: "Greenland",
+ ISO: "GL",
+ Rings: [][]float64{
+ {-46.8,82.6,-27.1,83.5,-20.8,82.7,-31.9,82.2,-12.2,81.3,-20,80.2,-17.7,80.1,-19.7,78.8,-18.5,77,-21.7,76.6,-19.4,74.3,-24.8,72.3,-21.8,70.7,-25.5,71.4,-26.4,70.2,-22.3,70.1,-39.8,65.5,-43.4,60.1,-48.3,60.9,-51.6,63.6,-54,67.2,-50.9,69.9,-54.7,69.6,-54.4,70.8,-51.4,70.6,-55.8,71.7,-54.7,72.6,-58.6,75.5,-68.5,76.1,-71.4,77,-66.8,77.4,-73.3,78,-65.7,79.4,-68,80.1,-62.7,81.8,-44.5,81.7,-46.8,82.6},
+ },
+ },
+ {
+ Name: "Guatemala",
+ ISO: "GT",
+ Rings: [][]float64{
+ {-92.2,14.5,-90.5,16.1,-91,17.8,-89.1,17.8,-88.2,15.7,-89.4,14.4,-92.2,14.5},
+ },
+ },
+ {
+ Name: "Guinea",
+ ISO: "GN",
+ Rings: [][]float64{
+ {-13.7,12.6,-9.1,12.3,-8.3,7.7,-11.1,10,-13.2,8.9,-15.1,11,-13.7,12.6},
+ },
+ },
+ {
+ Name: "Guinea-Bissau",
+ ISO: "GW",
+ Rings: [][]float64{
+ {-16.7,12.4,-13.7,11.8,-15.1,11,-16.7,12.4},
+ },
+ },
+ {
+ Name: "Guyana",
+ ISO: "GY",
+ Rings: [][]float64{
+ {-56.5,1.9,-59.6,1.8,-61.4,6,-59.8,8.4,-57.1,6,-58,4.1,-56.5,1.9},
+ },
+ },
+ {
+ Name: "Haiti",
+ ISO: "HT",
+ Rings: [][]float64{
+ {-71.7,19.7,-71.7,18,-74.5,18.3,-71.7,19.7},
+ },
+ },
+ {
+ Name: "Honduras",
+ ISO: "HN",
+ Rings: [][]float64{
+ {-83.1,15,-87.3,13,-89.4,14.4,-87.9,15.9,-83.1,15},
+ },
+ },
+ {
+ Name: "Hungary",
+ ISO: "HU",
+ Rings: [][]float64{
+ {22.1,48.4,21,46.3,16.2,46.9,17,48.1,22.1,48.4},
+ },
+ },
+ {
+ Name: "Iceland",
+ ISO: "IS",
+ Rings: [][]float64{
+ {-14.5,66.5,-13.6,65.1,-18.7,63.5,-22.8,64,-21.8,64.4,-24,64.9,-22.2,65.4,-24.3,65.6,-14.5,66.5},
+ },
+ },
+ {
+ Name: "India",
+ ISO: "IN",
+ Rings: [][]float64{
+ {97.3,28.3,92.7,22,91.2,23.5,92.4,25,88.6,26.4,88.9,21.7,80.3,15.9,79.9,10.4,77.5,8,72.6,21.4,70.5,20.9,68.2,23.7,71,24.4,69.5,26.9,75.3,32.3,73.7,34.3,77.8,35.5,78.7,31.5,81.1,30.2,80.1,28.8,83.3,27.4,88.1,26.4,88.7,28.1,92,26.8,96.1,29.5,97.3,28.3},
+ },
+ },
+ {
+ Name: "Indonesia",
+ ISO: "ID",
+ Rings: [][]float64{
+ {141,-2.6,141,-9.1,137.6,-8.4,137.9,-5.4,133,-4.1,132,-2.8,133.7,-2.2,130.5,-0.9,134,-0.8,135.5,-3.4,137.4,-1.7,141,-2.6},
+ {125,-8.9,123.5,-10.2,125,-8.9},
+ {117.9,4.1,119,0.9,116.1,-4,110.2,-2.9,109.7,2,110.5,0.8,113.8,1.2,115.9,4.3,117.9,4.1},
+ {129.4,-2.8,130.8,-3.9,127.9,-3.4,129.4,-2.8},
+ {127.9,2.2,128.1,-0.9,127.9,2.2},
+ {122.9,0.9,125.2,1.4,120,-0.5,123.3,-0.6,121.5,-1.9,123.2,-5.3,121,-2.6,119.8,-5.7,118.8,-2.8,119.8,0.2,122.9,0.9},
+ {120.3,-10.3,119,-9.6,120.3,-10.3},
+ {121.3,-8.5,122.9,-8.1,119.9,-8.8,121.3,-8.5},
+ {118.3,-8.4,116.7,-9,118.3,-8.4},
+ {108.5,-6.4,115.7,-8.4,105.4,-6.9,108.5,-6.4},
+ {104.4,-1.1,106.1,-3.1,105.8,-5.9,102.6,-4.2,95.3,5.5,97.5,5.2,104.4,-1.1},
+ },
+ },
+ {
+ Name: "Iran",
+ ISO: "IR",
+ Rings: [][]float64{
+ {48.6,29.9,45.4,34,46.1,35.7,44.1,39.4,48.1,39.6,50.8,36.9,56.6,38.1,61.1,36.5,60.9,29.8,63.3,26.8,61.5,25.1,57.4,25.7,48.6,29.9},
+ },
+ },
+ {
+ Name: "Iraq",
+ ISO: "IQ",
+ Rings: [][]float64{
+ {39.2,32.2,41.3,36.4,44.8,37.2,48.6,29.9,44.7,29.2,39.2,32.2},
+ },
+ },
+ {
+ Name: "Ireland",
+ ISO: "IE",
+ Rings: [][]float64{
+ {-6.2,53.9,-6.8,52.3,-10,51.8,-7.6,55.1,-6.2,53.9},
+ },
+ },
+ {
+ Name: "Israel",
+ ISO: "IL",
+ Rings: [][]float64{
+ {35.7,32.7,34.9,29.5,34.3,31.2,35.7,32.7},
+ },
+ },
+ {
+ Name: "Italy",
+ ISO: "IT",
+ Rings: [][]float64{
+ {10.4,46.9,13.8,46.5,12.6,44.1,18.3,39.8,16.9,40.4,15.7,37.9,15.4,40,10.2,43.9,7.4,43.7,6.8,46,10.4,46.9},
+ {14.8,38.1,15.1,36.6,12.4,37.6,14.8,38.1},
+ {8.7,40.9,9.8,40.5,8.8,38.9,8.7,40.9},
+ },
+ },
+ {
+ Name: "Jamaica",
+ ISO: "JM",
+ Rings: [][]float64{
+ {-77.6,18.5,-76.2,17.9,-77.6,18.5},
+ },
+ },
+ {
+ Name: "Japan",
+ ISO: "JP",
+ Rings: [][]float64{
+ {141.9,39.2,140.3,35.1,135.8,33.5,135.1,34.6,131,33.9,132,33.1,130.2,31.4,129.4,33.3,139.4,38.2,140.3,41.2,141.9,39.2},
+ {144.6,44,145.5,43.3,140,41.6,142,45.6,144.6,44},
+ {132.4,33.5,134.8,33.8,132.4,33.5},
+ },
+ },
+ {
+ Name: "Jordan",
+ ISO: "JO",
+ Rings: [][]float64{
+ {35.5,32.4,38.8,33.4,39.2,32.2,37,31.5,38,30.5,36.1,29.2,34.9,29.5,35.5,32.4},
+ },
+ },
+ {
+ Name: "Kazakhstan",
+ ISO: "KZ",
+ Rings: [][]float64{
+ {87.4,49.2,80,44.9,80.3,42.3,74.2,43.3,68.6,40.7,64.9,43.7,62,43.5,58.5,45.6,55.9,45,56,41.3,52.5,41.8,50.3,44.6,53,45.3,53,46.9,49.1,46.4,46.5,48.4,50.8,51.7,61.3,50.8,60,52,61.4,54,69.1,55.4,73.4,53.5,76.9,54.5,80,50.9,87.4,49.2},
+ },
+ },
+ {
+ Name: "Kenya",
+ ISO: "KE",
+ Rings: [][]float64{
+ {39.2,-4.7,33.9,-0.9,35.3,5.5,38.1,3.6,41.9,3.9,41.6,-1.7,39.2,-4.7},
+ },
+ },
+ {
+ Name: "Kosovo",
+ ISO: "XK",
+ Rings: [][]float64{
+ {20.6,41.9,20.6,43.2,21.8,42.7,20.6,41.9},
+ },
+ },
+ {
+ Name: "Kuwait",
+ ISO: "KW",
+ Rings: [][]float64{
+ {48,30,48.4,28.6,46.6,29.1,48,30},
+ },
+ },
+ {
+ Name: "Kyrgyzstan",
+ ISO: "KG",
+ Rings: [][]float64{
+ {71,42.3,74.2,43.3,80.3,42.3,73.7,39.4,69.5,39.5,73.1,40.9,71,42.3},
+ },
+ },
+ {
+ Name: "Laos",
+ ISO: "LA",
+ Rings: [][]float64{
+ {107.4,14.2,105.2,14.3,104,18.2,101.1,17.5,100.1,20.4,101.7,22.3,104.4,20.8,103.9,19.3,107.4,14.2},
+ },
+ },
+ {
+ Name: "Latvia",
+ ISO: "LV",
+ Rings: [][]float64{
+ {27.3,57.5,28.2,56.2,26.5,55.6,21.1,56,22.5,57.8,27.3,57.5},
+ },
+ },
+ {
+ Name: "Lebanon",
+ ISO: "LB",
+ Rings: [][]float64{
+ {35.8,33.3,36.4,34.6,35.8,33.3},
+ },
+ },
+ {
+ Name: "Lesotho",
+ ISO: "LS",
+ Rings: [][]float64{
+ {29,-29,28.1,-30.5,27,-29.9,29,-29},
+ },
+ },
+ {
+ Name: "Liberia",
+ ISO: "LR",
+ Rings: [][]float64{
+ {-8.4,7.7,-7.7,4.4,-11.4,6.8,-10.2,8.4,-8.4,7.7},
+ },
+ },
+ {
+ Name: "Libya",
+ ISO: "LY",
+ Rings: [][]float64{
+ {25,22,23.8,19.6,10.3,24.4,10,31.4,11.5,33.1,19.1,30.3,20.9,32.7,24.9,31.9,25,22},
+ },
+ },
+ {
+ Name: "Lithuania",
+ ISO: "LT",
+ Rings: [][]float64{
+ {26.5,55.6,23.5,53.9,21.1,56,26.5,55.6},
+ },
+ },
+ {
+ Name: "Madagascar",
+ ISO: "MG",
+ Rings: [][]float64{
+ {49.5,-12.5,50.4,-15.7,47.1,-24.9,45.4,-25.6,43.3,-22.8,44,-17.4,49.5,-12.5},
+ },
+ },
+ {
+ Name: "Malawi",
+ ISO: "MW",
+ Rings: [][]float64{
+ {32.8,-9.2,35.7,-14.6,35,-16.8,32.7,-13.7,32.8,-9.2},
+ },
+ },
+ {
+ Name: "Malaysia",
+ ISO: "MY",
+ Rings: [][]float64{
+ {100.1,6.5,103,5.5,104.2,1.3,101.4,2.8,100.1,6.5},
+ {117.9,4.1,115.9,4.3,114.6,1.4,109.8,1.3,115.3,4.3,116.7,6.9,119.2,5.4,117.9,4.1},
+ },
+ },
+ {
+ Name: "Mali",
+ ISO: "ML",
+ Rings: [][]float64{
+ {-11.5,12.4,-11.7,15.4,-5.5,15.5,-6.5,25,4.3,19.2,3.6,15.6,-4,13.5,-5.4,10.4,-11.5,12.4},
+ },
+ },
+ {
+ Name: "Mauritania",
+ ISO: "MR",
+ Rings: [][]float64{
+ {-17.1,21,-12.9,21.3,-12,25.9,-8.7,25.9,-8.7,27.4,-4.9,25,-6.5,25,-5.5,15.5,-12.2,14.6,-14.6,16.6,-16.5,16.1,-17.1,21},
+ },
+ },
+ {
+ Name: "Mexico",
+ ISO: "MX",
+ Rings: [][]float64{
+ {-117.1,32.5,-106.5,31.8,-103.9,29.3,-101.7,29.8,-97.1,25.9,-97.9,22.4,-95.9,18.8,-91.4,18.9,-90.3,21,-87.1,21.5,-87.8,18.3,-91,17.8,-90.5,16.1,-92.2,14.5,-103.5,18.3,-113.1,31.2,-114.9,31.4,-109.9,22.8,-115.1,27.7,-114.2,28.6,-117.1,32.5},
+ },
+ },
+ {
+ Name: "Moldova",
+ ISO: "MD",
+ Rings: [][]float64{
+ {26.6,48.2,30,46.4,28.2,45.5,26.6,48.2},
+ },
+ },
+ {
+ Name: "Mongolia",
+ ISO: "MN",
+ Rings: [][]float64{
+ {87.8,49.3,92.2,50.8,97.3,49.7,98.9,52,108.5,49.3,116.7,49.9,115.7,47.7,119.8,47,105,41.6,96.3,42.7,90.9,45.3,91,46.9,87.8,49.3},
+ },
+ },
+ {
+ Name: "Montenegro",
+ ISO: "ME",
+ Rings: [][]float64{
+ {20.1,42.6,18.5,42.5,20.1,42.6},
+ },
+ },
+ {
+ Name: "Morocco",
+ ISO: "MA",
+ Rings: [][]float64{
+ {-2.2,35.2,-1.3,32.3,-8.7,28.8,-8.8,27.1,-11.4,26.9,-14.8,21.5,-17,21.4,-14.4,26.3,-9.6,29.9,-8.7,33.2,-5.9,35.8,-2.2,35.2},
+ },
+ },
+ {
+ Name: "Mozambique",
+ ISO: "MZ",
+ Rings: [][]float64{
+ {34.6,-11.5,40.3,-10.3,40.8,-14.7,34.8,-19.8,35.5,-24.1,32.1,-26.7,31.2,-22.3,32.8,-16.7,30.2,-14.8,33.2,-14,35,-16.8,34.6,-11.5},
+ },
+ },
+ {
+ Name: "Myanmar",
+ ISO: "MM",
+ Rings: [][]float64{
+ {100.1,20.4,97.4,18.4,99.6,11.9,98.6,9.9,97.2,16.9,94.2,16,92.3,21.5,97.3,28.3,98.7,27.5,97.6,23.9,101.2,21.8,100.1,20.4},
+ },
+ },
+ {
+ Name: "N. Cyprus",
+ ISO: "-99",
+ Rings: [][]float64{
+ {32.7,35.1,34.6,35.7,32.7,35.1},
+ },
+ },
+ {
+ Name: "Namibia",
+ ISO: "NA",
+ Rings: [][]float64{
+ {19.9,-24.8,19.9,-28.5,16.3,-28.6,11.7,-17.3,25.1,-17.6,20.9,-18.3,19.9,-24.8},
+ },
+ },
+ {
+ Name: "Nepal",
+ ISO: "NP",
+ Rings: [][]float64{
+ {88.1,27.9,87.2,26.4,80.1,28.8,81.5,30.4,88.1,27.9},
+ },
+ },
+ {
+ Name: "Netherlands",
+ ISO: "NL",
+ Rings: [][]float64{
+ {6.9,53.5,6.2,50.8,3.3,51.3,6.9,53.5},
+ },
+ },
+ {
+ Name: "New Caledonia",
+ ISO: "NC",
+ Rings: [][]float64{
+ {165.8,-21.1,167.1,-22.2,164,-20.1,165.8,-21.1},
+ },
+ },
+ {
+ Name: "New Zealand",
+ ISO: "NZ",
+ Rings: [][]float64{
+ {176.9,-40.1,174.7,-41.3,174.7,-37.4,172.6,-34.5,176,-37.6,178.5,-37.7,176.9,-40.1},
+ {169.7,-43.6,172.8,-40.5,174.2,-41.3,170.6,-45.9,166.7,-46.2,169.7,-43.6},
+ },
+ },
+ {
+ Name: "Nicaragua",
+ ISO: "NI",
+ Rings: [][]float64{
+ {-83.7,10.9,-87.7,12.9,-83.1,15,-83.7,10.9},
+ },
+ },
+ {
+ Name: "Niger",
+ ISO: "NE",
+ Rings: [][]float64{
+ {14.9,22.9,15.9,20.4,13.5,14.4,14.2,12.5,5.4,13.9,3.6,11.7,1,12.9,0.4,14.9,3.6,15.6,4.3,19.2,12,23.5,14.9,22.9},
+ },
+ },
+ {
+ Name: "Nigeria",
+ ISO: "NG",
+ Rings: [][]float64{
+ {2.7,6.3,4.4,13.7,13.1,13.6,14.6,12.1,11.7,7,8.5,4.8,5.9,4.3,2.7,6.3},
+ },
+ },
+ {
+ Name: "North Korea",
+ ISO: "KP",
+ Rings: [][]float64{
+ {130.6,42.4,127.5,39.8,128.2,38.4,124.7,38.1,125.1,40.6,130.6,42.4},
+ },
+ },
+ {
+ Name: "North Macedonia",
+ ISO: "MK",
+ Rings: [][]float64{
+ {22.4,42.3,23,41.3,20.6,41.1,22.4,42.3},
+ },
+ },
+ {
+ Name: "Norway",
+ ISO: "NO",
+ Rings: [][]float64{
+ {15.1,79.7,21.5,79,15.9,76.8,10.4,79.7,15.1,79.7},
+ {31.1,69.6,18,68.6,12.6,64.1,11,58.9,5.7,58.6,5,62,19.2,69.8,28.2,71.2,31.1,69.6},
+ {27.4,80.1,17.4,80.3,27.4,80.1},
+ {24.7,77.9,20.7,77.7,24.7,77.9},
+ },
+ },
+ {
+ Name: "Oman",
+ ISO: "OM",
+ Rings: [][]float64{
+ {55.2,22.7,56.4,24.9,59.8,22.3,57.7,18.9,53.1,16.7,52,19,55,20,55.2,22.7},
+ },
+ },
+ {
+ Name: "Pakistan",
+ ISO: "PK",
+ Rings: [][]float64{
+ {77.8,35.5,73.7,34.3,75.3,32.3,69.5,26.9,71,24.4,61.5,25.1,63.3,26.8,60.9,29.8,66.3,29.9,71.8,36.5,75.2,37.1,77.8,35.5},
+ },
+ },
+ {
+ Name: "Panama",
+ ISO: "PA",
+ Rings: [][]float64{
+ {-77.4,8.7,-77.9,7.2,-79.1,9,-80.9,7.2,-82.9,9.5,-77.4,8.7},
+ },
+ },
+ {
+ Name: "Papua New Guinea",
+ ISO: "PG",
+ Rings: [][]float64{
+ {141,-2.6,147.6,-6.1,147.2,-7.4,150.7,-10.6,144.7,-7.6,141,-9.1,141,-2.6},
+ {152.6,-3.7,152.8,-4.8,150.7,-2.7,152.6,-3.7},
+ {151.3,-5.8,148.3,-5.7,152.1,-4.1,151.3,-5.8},
+ {154.8,-5.3,155.9,-6.8,154.8,-5.3},
+ },
+ },
+ {
+ Name: "Paraguay",
+ ISO: "PY",
+ Rings: [][]float64{
+ {-58.2,-20.2,-57.9,-22.1,-54.3,-24,-55.7,-27.4,-58.6,-27.1,-57.8,-25.2,-62.7,-22.2,-61.8,-19.6,-58.2,-20.2},
+ },
+ },
+ {
+ Name: "Peru",
+ ISO: "PE",
+ Rings: [][]float64{
+ {-69.9,-4.3,-72.9,-5.3,-74,-7.5,-68.7,-12.6,-70.4,-18.3,-76,-14.6,-81.4,-4.7,-80.3,-3.4,-78.6,-4.5,-75.1,-0.1,-73.1,-2.3,-70,-2.7,-69.9,-4.3},
+ },
+ },
+ {
+ Name: "Philippines",
+ ISO: "PH",
+ Rings: [][]float64{
+ {122.6,10,124.1,11.2,123,9,122.6,10},
+ {126.4,8.4,125.4,5.6,123.6,7.8,121.9,7.2,125.4,9.8,126.4,8.4},
+ {118.5,9.3,117.2,8.4,119.5,11.4,118.5,9.3},
+ {122.3,18.2,121.7,14.3,124.1,12.5,119.9,15.4,120.7,18.5,122.3,18.2},
+ {125.5,12.2,124.8,10.1,124.3,12.6,125.5,12.2},
+ },
+ },
+ {
+ Name: "Poland",
+ ISO: "PL",
+ Rings: [][]float64{
+ {23.5,53.9,24,50.7,22.8,49,16.2,50.4,14.1,53,17.6,54.9,23.5,53.9},
+ },
+ },
+ {
+ Name: "Portugal",
+ ISO: "PT",
+ Rings: [][]float64{
+ {-9,41.9,-6.4,41.4,-7.9,36.8,-9.5,38.7,-9,41.9},
+ },
+ },
+ {
+ Name: "Puerto Rico",
+ ISO: "PR",
+ Rings: [][]float64{
+ {-66.3,18.5,-67.2,17.9,-66.3,18.5},
+ },
+ },
+ {
+ Name: "Qatar",
+ ISO: "QA",
+ Rings: [][]float64{
+ {50.8,24.8,51.3,26.1,50.8,24.8},
+ },
+ },
+ {
+ Name: "Romania",
+ ISO: "RO",
+ Rings: [][]float64{
+ {28.2,45.5,29.6,45.3,28.6,43.7,22.9,43.8,20.2,46.1,26.6,48.2,28.2,45.5},
+ },
+ },
+ {
+ Name: "Russia",
+ ISO: "RU",
+ Rings: [][]float64{
+ {49.1,46.4,46.7,44.6,47.8,41.2,36.7,45.2,40.1,49.6,31.8,52.1,32.7,53.4,30.9,55.6,27.3,57.5,29.1,60,28.1,60.5,31.5,62.9,30,63.6,28.6,69.1,32.1,69.9,41.1,67.5,38.4,66,33.2,66.6,37,63.8,37.2,65.1,43.9,66.1,43.5,68.6,46.3,68.3,46.3,66.7,53.7,68.9,59.9,68.3,60.6,69.9,68.5,68.1,66.7,71,69.9,73,72.8,72.2,71.8,71.4,73.7,68.4,71.3,66.3,72.4,66.2,75.1,67.8,73.1,71.4,74.7,72.8,76.4,71.2,81.5,71.8,80.5,73.6,104.4,77.7,114.1,75.8,109.4,74.2,127,73.6,131.3,70.8,139.9,71.5,139.1,72.4,140.5,72.8,159,70.9,160.9,69.4,180,69,180,65,177.4,64.6,179.2,62.3,170.3,59.9,163.5,59.9,162,58.2,163.2,57.6,162.1,54.9,156.8,51,155.9,56.8,164.5,62.6,160.1,60.5,156.7,61.4,154.2,59.8,155,59.1,142.2,59,135.1,54.7,141.3,53.1,140.1,48.4,134.9,43.4,130.8,42.2,131,45,133.1,45.1,135,48.5,131,47.8,123.6,53.5,120.2,52.8,117.9,49.5,108.5,49.3,98.9,52,97.3,49.7,92.2,50.8,87.4,49.2,80,50.9,76.9,54.5,73.4,53.5,69.1,55.4,61.4,54,60,52,61.3,50.8,50.8,51.7,47.5,50.5,46.5,48.4,49.1,46.4},
+ {93.8,81,100.2,79.8,97.8,78.8,91.2,80.3,93.8,81},
+ {102.8,79.3,105.4,78.7,99.4,77.9,102.8,79.3},
+ {138.8,76.1,145.1,75.6,137,75.3,138.8,76.1},
+ {148.2,75.3,150.7,75.1,146.1,75.2,148.2,75.3},
+ {139.9,73.4,143.6,73.2,139.9,73.4},
+ {44.8,80.6,51.5,80.7,44.8,80.6},
+ {22.7,54.3,19.7,54.4,22.7,54.3},
+ {53.5,73.7,61.2,76.3,68.9,76.5,58.5,74.3,55.4,72.4,57.5,70.7,51.6,71.5,53.5,73.7},
+ {142.9,53.7,144.7,49,143.2,49.3,143.5,46.1,142.1,46,141.7,53.3,142.9,53.7},
+ {-174.9,67.2,-169.9,66,-173,64.3,-178.7,66.1,-180,65,-180,69,-174.9,67.2},
+ {-178.7,70.9,-180,71.5,-177.6,71.3,-178.7,70.9},
+ {33.4,46,36.5,45.5,33.9,44.4,32.5,45.3,33.4,46},
+ },
+ },
+ {
+ Name: "Rwanda",
+ ISO: "RW",
+ Rings: [][]float64{
+ {30.4,-1.1,29,-2.8,30.4,-1.1},
+ },
+ },
+ {
+ Name: "S. Sudan",
+ ISO: "SS",
+ Rings: [][]float64{
+ {30.8,3.5,23.9,8.6,25.8,10.4,31.4,9.8,33.2,12.2,33,7.8,35.3,5.5,30.8,3.5},
+ },
+ },
+ {
+ Name: "Saudi Arabia",
+ ISO: "SA",
+ Rings: [][]float64{
+ {35,29.4,39.2,32.2,47.5,29,52,23,55.2,22.7,55,20,47,16.9,43.4,17.6,42.8,16.3,35,29.4},
+ },
+ },
+ {
+ Name: "Senegal",
+ ISO: "SN",
+ Rings: [][]float64{
+ {-16.7,13.6,-17.6,14.7,-14.6,16.6,-11.5,12.4,-16.7,12.4,-13.8,13.5,-16.7,13.6},
+ },
+ },
+ {
+ Name: "Serbia",
+ ISO: "RS",
+ Rings: [][]float64{
+ {18.8,45.9,22.7,44.6,22.5,42.5,19.2,43.5,18.8,45.9},
+ },
+ },
+ {
+ Name: "Sierra Leone",
+ ISO: "SL",
+ Rings: [][]float64{
+ {-13.2,8.9,-11.1,10,-10.2,8.4,-11.4,6.8,-13.2,8.9},
+ },
+ },
+ {
+ Name: "Slovakia",
+ ISO: "SK",
+ Rings: [][]float64{
+ {22.6,49.1,16.9,48.5,22.6,49.1},
+ },
+ },
+ {
+ Name: "Slovenia",
+ ISO: "SI",
+ Rings: [][]float64{
+ {13.8,46.5,16.6,46.5,15.3,45.5,13.8,46.5},
+ },
+ },
+ {
+ Name: "Solomon Is.",
+ ISO: "SB",
+ Rings: [][]float64{
+ {159.6,-8,158.2,-7.4,159.6,-8},
+ },
+ },
+ {
+ Name: "Somalia",
+ ISO: "SO",
+ Rings: [][]float64{
+ {41.6,-1.7,41,2.8,45,5,48.9,9.5,48.9,11.4,51.1,12,48.6,5.3,41.6,-1.7},
+ },
+ },
+ {
+ Name: "Somaliland",
+ ISO: "-99",
+ Rings: [][]float64{
+ {48.9,11.4,47.8,8,42.6,10.6,48.9,11.4},
+ },
+ },
+ {
+ Name: "South Africa",
+ ISO: "ZA",
+ Rings: [][]float64{
+ {16.3,-28.6,19.9,-28.5,19.9,-24.8,21.6,-26.7,25.7,-25.5,29.4,-22.1,31.2,-22.3,31.9,-24.4,30.7,-26.7,32.8,-26.7,28.2,-32.8,20.1,-34.8,18.4,-34.1,16.3,-28.6},
+ },
+ },
+ {
+ Name: "South Korea",
+ ISO: "KR",
+ Rings: [][]float64{
+ {126.2,37.7,128.3,38.6,129.1,35.1,126.5,34.4,126.2,37.7},
+ },
+ },
+ {
+ Name: "Spain",
+ ISO: "ES",
+ Rings: [][]float64{
+ {-7.5,37.1,-6.4,41.4,-9.4,43,3,42.5,-2.1,36.7,-7.5,37.1},
+ },
+ },
+ {
+ Name: "Sri Lanka",
+ ISO: "LK",
+ Rings: [][]float64{
+ {81.8,7.5,80.3,6,80.1,9.8,81.8,7.5},
+ },
+ },
+ {
+ Name: "Sudan",
+ ISO: "SD",
+ Rings: [][]float64{
+ {24.6,8.2,21.9,12.6,25,22,36.9,22,38.4,18,34,8.7,32.7,12.2,31.4,9.8,25.1,10.3,24.6,8.2},
+ },
+ },
+ {
+ Name: "Suriname",
+ ISO: "SR",
+ Rings: [][]float64{
+ {-54.5,2.3,-56.5,1.9,-57.6,3.3,-57.1,6,-54,5.8,-54.5,2.3},
+ },
+ },
+ {
+ Name: "Sweden",
+ ISO: "SE",
+ Rings: [][]float64{
+ {11,58.9,12.6,61.3,11.9,63.1,16.8,68,20.6,69.1,23.5,67.9,23.9,66,17.8,62.7,17.1,61.3,18.8,60.1,15.9,56.1,12.9,55.4,11,58.9},
+ },
+ },
+ {
+ Name: "Switzerland",
+ ISO: "CH",
+ Rings: [][]float64{
+ {9.6,47.5,10.4,46.5,6,46.3,9.6,47.5},
+ },
+ },
+ {
+ Name: "Syria",
+ ISO: "SY",
+ Rings: [][]float64{
+ {35.7,32.7,36.7,36.8,42.3,37.2,41,34.4,35.7,32.7},
+ },
+ },
+ {
+ Name: "Taiwan",
+ ISO: "TW",
+ Rings: [][]float64{
+ {121.8,24.4,120.7,22,120.1,23.6,121.8,24.4},
+ },
+ },
+ {
+ Name: "Tajikistan",
+ ISO: "TJ",
+ Rings: [][]float64{
+ {67.8,37.1,67.7,39.6,70.7,41,69.5,39.5,73.7,39.4,75,37.4,71.8,36.7,70.8,38.5,67.8,37.1},
+ },
+ },
+ {
+ Name: "Tanzania",
+ ISO: "TZ",
+ Rings: [][]float64{
+ {33.9,-0.9,39.2,-4.7,39.5,-10.9,34.6,-11.5,29.6,-6.5,30.4,-1.1,33.9,-0.9},
+ },
+ },
+ {
+ Name: "Thailand",
+ ISO: "TH",
+ Rings: [][]float64{
+ {105.2,14.3,103,14.2,102.6,12.2,100.1,13.4,99.2,9.2,102.1,6.2,101.2,5.7,98.2,8.4,99.6,11.9,97.4,18.4,100.1,20.4,101.1,17.5,104.7,17.4,105.2,14.3},
+ },
+ },
+ {
+ Name: "Timor-Leste",
+ ISO: "TL",
+ Rings: [][]float64{
+ {125,-8.9,127.3,-8.4,125,-8.9},
+ },
+ },
+ {
+ Name: "Togo",
+ ISO: "TG",
+ Rings: [][]float64{
+ {0.9,11,1.1,5.9,0.9,11},
+ },
+ },
+ {
+ Name: "Tunisia",
+ ISO: "TN",
+ Rings: [][]float64{
+ {9.5,30.3,7.5,34.1,9.5,37.3,11,37.1,10.1,34.3,11.5,33.1,9.5,30.3},
+ },
+ },
+ {
+ Name: "Turkey",
+ ISO: "TR",
+ Rings: [][]float64{
+ {44.8,37.2,29.7,36.1,27.6,36.7,26.2,39.5,33.5,42,42.6,41.6,44.8,39.7,44.8,37.2},
+ {26.1,41.8,29,41.3,26.4,40.2,26.1,41.8},
+ },
+ },
+ {
+ Name: "Turkmenistan",
+ ISO: "TM",
+ Rings: [][]float64{
+ {52.5,41.8,57.1,41.3,58.6,42.8,66.5,37.4,62.2,35.3,57.3,38,53.9,37.2,52.7,40,54.7,41,52.5,41.8},
+ },
+ },
+ {
+ Name: "Uganda",
+ ISO: "UG",
+ Rings: [][]float64{
+ {33.9,-0.9,29.6,-1.3,31.2,3.8,34.5,3.6,33.9,-0.9},
+ },
+ },
+ {
+ Name: "Ukraine",
+ ISO: "UA",
+ Rings: [][]float64{
+ {31.8,52.1,40.1,49.6,39.7,47.9,35,45.7,31.7,46.7,28.7,45.3,30,46.4,28.7,48.1,22.1,48.4,23.5,51.6,31.8,52.1},
+ },
+ },
+ {
+ Name: "United Arab Emirates",
+ ISO: "AE",
+ Rings: [][]float64{
+ {51.6,24.2,56.3,25.7,55,22.5,51.6,24.2},
+ },
+ },
+ {
+ Name: "United Kingdom",
+ ISO: "GB",
+ Rings: [][]float64{
+ {-6.2,53.9,-7.6,55.1,-6.2,53.9},
+ {-3.1,53.4,-6.1,56.8,-5,58.6,-2,57.7,-3.1,56,1.7,52.7,1.4,51.3,-5.8,50.2,-3.4,51.4,-5.3,52,-4.6,53.5,-3.1,53.4},
+ },
+ },
+ {
+ Name: "United States of America",
+ ISO: "US",
+ Rings: [][]float64{
+ {-122.8,49,-88.4,48.3,-82.6,45.3,-82.7,41.7,-71.5,45,-69.2,47.4,-67,44.8,-70.1,43.7,-70,41.6,-75.5,39.5,-75.9,37.2,-76.3,39.2,-77,38.2,-75.7,35.6,-81.3,31.4,-80.4,25.2,-83.7,29.9,-86.4,30.4,-94.7,29.5,-97.5,25.8,-101,29.4,-103.9,29.3,-106.5,31.8,-117.1,32.5,-120.6,34.6,-124.4,40.3,-124.7,48.2,-122.6,47.1,-122.8,49},
+ {-166.5,60.4,-165.6,59.9,-167.5,60.2,-166.5,60.4},
+ {-153.2,58,-152.1,57.6,-154.5,57,-153.2,58},
+ {-141,69.7,-141,60.3,-137.5,58.9,-135.5,59.8,-130,55.9,-130.5,54.8,-134.1,58.1,-139.9,59.5,-147.1,60.9,-151.7,59.2,-150.6,61.3,-158.4,56,-164.9,54.6,-157,58.9,-162,58.7,-165.3,60.5,-165.7,62.1,-160.8,64.8,-168.1,65.7,-161.7,66.1,-166.2,68.9,-156.6,71.4,-141,69.7},
+ {-171.7,63.8,-168.7,63.3,-171.7,63.8},
+ },
+ },
+ {
+ Name: "Uruguay",
+ ISO: "UY",
+ Rings: [][]float64{
+ {-57.6,-30.2,-53.8,-32,-53.8,-34.4,-58.4,-33.9,-57.6,-30.2},
+ },
+ },
+ {
+ Name: "Uzbekistan",
+ ISO: "UZ",
+ Rings: [][]float64{
+ {56,41.3,55.9,45,58.5,45.6,62,43.5,64.9,43.7,68.3,40.7,71,42.3,73.1,40.9,67.7,39.6,67.8,37.1,58.6,42.8,56,41.3},
+ },
+ },
+ {
+ Name: "Venezuela",
+ ISO: "VE",
+ Rings: [][]float64{
+ {-60.7,5.2,-64.8,4.1,-63.4,2.2,-66.3,0.7,-67.8,2.8,-67.3,6.1,-72,7,-72.9,10.5,-71.3,11.8,-71.3,9.1,-69.9,12.2,-68.2,10.6,-61.9,10.7,-59.8,8.4,-60.7,5.2},
+ },
+ },
+ {
+ Name: "Vietnam",
+ ISO: "VN",
+ Rings: [][]float64{
+ {104.3,10.5,107.5,12.3,107.6,15.2,102.2,22.5,105.3,23.4,108.1,21.6,105.7,19.1,108.9,15.3,109.2,11.7,105.2,8.6,104.3,10.5},
+ },
+ },
+ {
+ Name: "W. Sahara",
+ ISO: "EH",
+ Rings: [][]float64{
+ {-8.7,27.7,-8.7,25.9,-12,25.9,-12.9,21.3,-17.1,21,-14.8,21.5,-11.4,26.9,-8.7,27.7},
+ },
+ },
+ {
+ Name: "Yemen",
+ ISO: "YE",
+ Rings: [][]float64{
+ {52,19,52.2,15.6,43.5,12.6,43.4,17.6,47,16.9,52,19},
+ },
+ },
+ {
+ Name: "Zambia",
+ ISO: "ZM",
+ Rings: [][]float64{
+ {30.7,-8.3,33.2,-9.7,33.2,-14,27,-17.9,23.2,-17.5,21.9,-12.9,24,-12.9,23.9,-10.9,29.7,-13.3,28.4,-9.2,30.7,-8.3},
+ },
+ },
+ {
+ Name: "Zimbabwe",
+ ISO: "ZW",
+ Rings: [][]float64{
+ {31.2,-22.3,28,-21.5,25.3,-17.7,30.3,-15.5,32.8,-16.7,31.2,-22.3},
+ },
+ },
+}
diff --git a/ports/python/examples/world_probe.py b/ports/python/examples/world_probe.py
new file mode 100644
index 0000000..f5b3d88
--- /dev/null
+++ b/ports/python/examples/world_probe.py
@@ -0,0 +1,41 @@
+"""Prints what the country lookup answers for a fixed set of points.
+
+The same probe exists for every port, so "the ports agree about the world" is a
+diff rather than a hope.
+"""
+
+from __future__ import annotations
+
+from hqtui.graphics.world import WORLD_X, WORLD_Y, country_at, degrees_at
+from hqtui.widgets.world import WorldMapOptions, country_at_cell
+
+PLACES = [
+ ("Paris", 2.35, 48.86),
+ ("Tokyo", 139.7, 35.7),
+ ("Cairo", 31.2, 30.0),
+ ("Brasilia", -47.9, -15.8),
+ ("Canberra", 149.1, -35.3),
+ ("Denver", -105.0, 39.7),
+ ("Moscow", 37.6, 55.75),
+ ("Delhi", 77.2, 28.6),
+ ("Nairobi", 36.8, -1.3),
+ ("Pacific", -140.0, 0.0),
+ ("Atlantic", -30.0, 0.0),
+ ("SouthernOcean", 80.0, -40.0),
+ ("NorthPacific", -150.0, 40.0),
+]
+
+for name, lon, lat in PLACES:
+ found = country_at(lon, lat)
+ print(f"{name} {found.name if found else '-'}")
+
+# The cell path, which has to agree with what the canvas drew.
+for column, row in [(173, 28), (74, 2), (20, 25), (88, 7)]:
+ found = country_at_cell(column, row, 200, 50, WorldMapOptions())
+ print(f"cell:{column},{row} {found.name if found else '-'}")
+
+# And the projection itself, so a drift shows up as a number rather than as a
+# country that happens to still be right.
+for column, row in [(0, 0), (99, 25), (50, 13)]:
+ lon, lat = degrees_at(column, row, 100, 26, WORLD_X, WORLD_Y)
+ print(f"degrees:{column},{row} {lon:.4f} {lat:.4f}")
diff --git a/ports/python/hqtui/graphics/__init__.py b/ports/python/hqtui/graphics/__init__.py
index 9bf5a80..36801d7 100644
--- a/ports/python/hqtui/graphics/__init__.py
+++ b/ports/python/hqtui/graphics/__init__.py
@@ -15,6 +15,18 @@
vertical_glyph,
)
from .braille import BrailleCanvas
+from .world import (
+ WORLD_COUNTRIES,
+ WORLD_X,
+ WORLD_Y,
+ CountryOutline,
+ WorldShapeOptions,
+ country_at,
+ country_bounds,
+ degrees_at,
+ find_country,
+ world_shapes,
+)
from .canvas import (
Bounds,
CanvasOptions,
@@ -54,6 +66,16 @@
)
__all__ = [
+ "WORLD_COUNTRIES",
+ "WORLD_X",
+ "WORLD_Y",
+ "CountryOutline",
+ "WorldShapeOptions",
+ "country_at",
+ "country_bounds",
+ "degrees_at",
+ "find_country",
+ "world_shapes",
"Bounds",
"CanvasOptions",
"Projection",
diff --git a/ports/python/hqtui/graphics/world.py b/ports/python/hqtui/graphics/world.py
new file mode 100644
index 0000000..d4f3de0
--- /dev/null
+++ b/ports/python/hqtui/graphics/world.py
@@ -0,0 +1,183 @@
+"""The world, as shapes for the canvas, and the lookup that makes it clickable.
+
+The canvas already draws in the caller's own coordinates, and longitude and
+latitude are just another pair of axes — so a map is a list of polylines in
+degrees, and nothing here needs a projection of its own beyond deciding which
+window on the globe to show.
+
+The interesting half 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 testing it against the outlines. Doing it that way
+rather than with bounding boxes is what makes the answer right: Russia's
+bounding box covers most of the northern hemisphere, and Chile's covers
+Argentina.
+"""
+
+from __future__ import annotations
+
+import math
+from dataclasses import dataclass, field
+from typing import Sequence
+
+from ..color import Color
+from .canvas import Bounds, Shape
+from .world_data import CountryOutline, WORLD_COUNTRIES
+
+__all__ = [
+ "WORLD_COUNTRIES",
+ "WORLD_X",
+ "WORLD_Y",
+ "CountryOutline",
+ "WorldShapeOptions",
+ "country_at",
+ "country_bounds",
+ "degrees_at",
+ "find_country",
+ "world_shapes",
+]
+
+#: The whole globe, which is what a map shows unless told otherwise.
+WORLD_X = Bounds(-180.0, 180.0)
+WORLD_Y = Bounds(-90.0, 90.0)
+
+
+@dataclass(frozen=True, slots=True)
+class WorldShapeOptions:
+ #: Colour for countries with nothing special about them.
+ color: Color | None = None
+ #: Countries to pick out, by name or ISO code.
+ highlight: Sequence[str] = ()
+ highlight_color: Color | None = None
+
+
+def _matches(country: CountryOutline, keys: Sequence[str]) -> bool:
+ """Match on either the name or the ISO code, case-insensitively."""
+ for key in keys:
+ if not key:
+ continue
+ if country.name.lower() == key.lower():
+ return True
+ if country.iso and country.iso.lower() == key.lower():
+ return True
+ return False
+
+
+def world_shapes(options: WorldShapeOptions = WorldShapeOptions()) -> list[Shape]:
+ """The world as canvas shapes, one polyline per landmass.
+
+ Polylines rather than scattered points: the outlines are closed rings, so
+ joining them draws a coastline instead of a dotted suggestion of one, and it
+ reads at a fraction of the resolution dots would need.
+ """
+ shapes: list[Shape] = []
+ for country in WORLD_COUNTRIES:
+ picked = bool(options.highlight) and _matches(country, options.highlight)
+ color = options.highlight_color if picked else options.color
+ if picked and color is None:
+ color = options.color
+ for ring in country.rings:
+ points = [(ring[i], ring[i + 1]) for i in range(0, len(ring) - 1, 2)]
+ # Closed: the last point joins the first, or every country has a gap
+ # in its coastline where the ring started.
+ if points:
+ points.append(points[0])
+ shapes.append(Shape(kind="polyline", points=tuple(points), color=color))
+ return shapes
+
+
+def _inside_ring(ring: Sequence[float], lon: float, lat: float) -> bool:
+ """Whether a point is inside a ring, by ray casting.
+
+ The ring is a flat list of interleaved coordinates, so this walks it two at
+ a time rather than allocating a pair per vertex — it runs once per country
+ per click, and there are a couple of thousand vertices.
+ """
+ inside = False
+ n = len(ring) // 2
+ if n == 0:
+ return False
+ j = n - 1
+ for i in range(n):
+ xi, yi = ring[i * 2], ring[i * 2 + 1]
+ xj, yj = ring[j * 2], ring[j * 2 + 1]
+ if (yi > lat) != (yj > lat) and lon < (xj - xi) * (lat - yi) / (yj - yi) + xi:
+ inside = not inside
+ j = i
+ return inside
+
+
+def country_at(lon: float, lat: float) -> CountryOutline | None:
+ """The country containing a point, or None for open water.
+
+ Where outlines overlap — and at this resolution simplified borders do
+ overlap — the first match wins, which is stable because the data is sorted
+ by name.
+ """
+ if not math.isfinite(lon) or not math.isfinite(lat):
+ return None
+ for country in WORLD_COUNTRIES:
+ for ring in country.rings:
+ if _inside_ring(ring, lon, lat):
+ return country
+ return None
+
+
+def find_country(key: str) -> CountryOutline | None:
+ """Look a country up by name or ISO code."""
+ for country in WORLD_COUNTRIES:
+ if _matches(country, (key,)):
+ return country
+ return None
+
+
+def country_bounds(country: CountryOutline, margin: float = 0.08) -> tuple[Bounds, Bounds]:
+ """The window a country fills, with a little room around it.
+
+ For zooming a map to a country: the bounding box alone puts the coastline
+ flat against the edge of the panel, which reads as though the country has
+ been cut off rather than framed.
+ """
+ min_lon = min_lat = math.inf
+ max_lon = max_lat = -math.inf
+ for ring in country.rings:
+ for i in range(0, len(ring) - 1, 2):
+ min_lon = min(min_lon, ring[i])
+ max_lon = max(max_lon, ring[i])
+ min_lat = min(min_lat, ring[i + 1])
+ max_lat = max(max_lat, ring[i + 1])
+ if not math.isfinite(min_lon):
+ return WORLD_X, WORLD_Y
+ # A single-point country would give a zero-width window, which cannot be
+ # mapped onto anything.
+ pad_x = max((max_lon - min_lon) * margin, 1.0)
+ pad_y = max((max_lat - min_lat) * margin, 1.0)
+ return (
+ Bounds(min_lon - pad_x, max_lon + pad_x),
+ Bounds(min_lat - pad_y, max_lat + pad_y),
+ )
+
+
+def degrees_at(
+ column: int,
+ row: int,
+ width: int,
+ height: int,
+ x: Bounds = WORLD_X,
+ y: Bounds = WORLD_Y,
+) -> tuple[float, float] | None:
+ """The degrees under a terminal cell, given the window the map was drawn with.
+
+ The inverse of what the canvas does on the way in, taken at the centre of
+ the cell: a click lands on a whole cell, and the centre is the only point in
+ it that is not arbitrarily nearer one neighbour than the other.
+ """
+ if width <= 0 or height <= 0:
+ return None
+ # The canvas is 2x4 Braille pixels per cell, and it spans its bounds across
+ # ``pixels - 1``, so the inverse has to use the same denominators or a click
+ # drifts from what was drawn.
+ px = max(1, width * 2 - 1)
+ py = max(1, height * 4 - 1)
+ lon = x.min + ((column * 2 + 1) / px) * (x.max - x.min)
+ lat = y.min + (1 - (row * 4 + 2) / py) * (y.max - y.min)
+ return lon, lat
diff --git a/ports/python/hqtui/graphics/world_data.py b/ports/python/hqtui/graphics/world_data.py
new file mode 100644
index 0000000..21d2fca
--- /dev/null
+++ b/ports/python/hqtui/graphics/world_data.py
@@ -0,0 +1,1314 @@
+"""
+ Country outlines, flattened for a terminal.
+
+ Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's
+ 1:110m Admin 0 countries, which is public domain. Do not edit by hand.
+
+ Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --
+ rather than a list of pairs, because at 1982 points the nested form
+ costs a container per coordinate for no gain. A country has more than one
+ ring when it is more than one landmass.
+
+ 171 countries, 1982 points, simplified at 1 degrees.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+
+@dataclass(frozen=True, slots=True)
+class CountryOutline:
+ name: str
+ #: ISO 3166-1 alpha-2, where Natural Earth has one.
+ iso: str
+ #: Longitude and latitude, interleaved.
+ rings: tuple[tuple[float, ...], ...]
+
+
+WORLD_COUNTRIES: tuple[CountryOutline, ...] = (
+ CountryOutline(
+ "Afghanistan",
+ "AF",
+ (
+ (66.5,37.4,70.8,38.5,71.8,36.7,75.2,37.1,71.3,36.1,69.3,31.9,66.3,29.9,60.9,29.8,61.2,35.7,66.5,37.4),
+ ),
+ ),
+ CountryOutline(
+ "Albania",
+ "AL",
+ (
+ (21,40.8,19.4,40.3,19.7,42.7,21,40.8),
+ ),
+ ),
+ CountryOutline(
+ "Algeria",
+ "DZ",
+ (
+ (-8.7,27.4,-8.7,28.8,-1.3,32.3,-1.2,35.7,8.4,36.9,7.5,34.1,9.8,29.4,9.3,26.1,12,23.5,3.2,19.1,-8.7,27.4),
+ ),
+ ),
+ CountryOutline(
+ "Angola",
+ "AO",
+ (
+ (12.3,-6.1,16.3,-5.9,17.5,-8.1,21.7,-7.3,22.2,-11.1,24,-11.2,24,-12.9,21.9,-12.9,23.2,-17.5,11.7,-17.3,13.7,-11.3,12.3,-6.1),
+ ),
+ ),
+ CountryOutline(
+ "Antarctica",
+ "AQ",
+ (
+ (-48.7,-78,-43.9,-78.5,-43.3,-80,-54.2,-80.6,-48.7,-78),
+ (-66.3,-80.3,-59.6,-80,-66.3,-80.3),
+ (-73.9,-71.3,-70.3,-68.9,-68.3,-71.4,-75,-72.1,-73.9,-71.3),
+ (-102.3,-71.9,-96.2,-72.5,-102.3,-71.9),
+ (-122.6,-73.7,-118.7,-73.5,-122.6,-73.7),
+ (-127.3,-73.5,-124,-73.9,-127.3,-73.5),
+ (-163.7,-78.6,-159.2,-79.5,-163.7,-78.6),
+ (180,-84.7,180,-90,-180,-90,-179.1,-84.1,-143.1,-85,-153.6,-83.7,-152.9,-82,-156.8,-81.1,-146.4,-80.3,-155.3,-79.1,-158.4,-76.9,-151.3,-77.4,-146.1,-76.5,-146.2,-75.4,-135.2,-74.3,-100.1,-74.9,-103.7,-72.6,-74.9,-73.9,-67.4,-72.5,-67.7,-67.3,-57.8,-63.3,-65.7,-68,-61.8,-70.7,-60.8,-73.7,-70.6,-76.6,-77.2,-76.7,-73.7,-77.9,-78,-79.2,-58.2,-83.2,-28.5,-80.3,-35.6,-79.5,-35.8,-78.3,-17.5,-75.1,-15.4,-73.1,-6.9,-70.9,27.1,-70.5,33.9,-68.5,38.6,-69.8,54.5,-65.8,61.4,-68,68.9,-67.9,69.7,-69.2,67.9,-71.9,69.9,-72.3,73.9,-69.9,88,-66.2,95.8,-67.4,102.8,-65.6,106.2,-66.9,113.6,-65.9,119.8,-67.3,135.1,-65.3,137.5,-67,145.5,-66.9,171.2,-71.7,163.6,-76.2,167,-78.8,161.8,-79.2,159.8,-80.9,169.4,-83.8,180,-84.7),
+ ),
+ ),
+ CountryOutline(
+ "Argentina",
+ "AR",
+ (
+ (-68.6,-52.6,-65,-54.7,-68.6,-54.9,-68.6,-52.6),
+ (-57.6,-30.2,-58.5,-34.4,-56.8,-36.9,-62.3,-38.8,-62.7,-41,-65.1,-41.1,-63.5,-42.6,-67.3,-45.6,-65.6,-47.2,-69.1,-50.7,-68.1,-52.3,-71.9,-52,-73.4,-49.3,-71.2,-44.8,-72.1,-42.3,-68.4,-24.5,-66.3,-21.8,-62.8,-22,-57.8,-25.2,-58.6,-27.1,-55.7,-27.4,-54.1,-25.5,-53.6,-26.9,-57.6,-30.2),
+ ),
+ ),
+ CountryOutline(
+ "Armenia",
+ "AM",
+ (
+ (46.5,38.8,43.6,41.1,45.6,40.8,46.5,38.8),
+ ),
+ ),
+ CountryOutline(
+ "Australia",
+ "AU",
+ (
+ (147.7,-40.8,147.9,-43.2,146,-43.5,144.7,-40.7,147.7,-40.8),
+ (126.1,-32.2,118,-35.1,115,-34.2,113.7,-22.5,120.9,-19.7,125.7,-14.2,129.6,-15,132.4,-11.1,136.5,-11.9,135.5,-15,140.2,-17.7,142.5,-10.7,146.4,-19,150.7,-22.4,153.6,-28.1,150,-37.4,146.3,-39,140.6,-38,138.2,-34.4,136.8,-35.3,137.8,-32.9,136,-34.9,131.3,-31.5,126.1,-32.2),
+ ),
+ ),
+ CountryOutline(
+ "Austria",
+ "AT",
+ (
+ (17,48.1,14.6,46.4,9.5,47.1,12.9,47.5,13.6,48.9,17,48.1),
+ ),
+ ),
+ CountryOutline(
+ "Azerbaijan",
+ "AZ",
+ (
+ (46.4,41.9,50.4,40.3,48.9,38.3,45.6,39.9,45,41.2,46.4,41.9),
+ ),
+ ),
+ CountryOutline(
+ "Bahamas",
+ "BS",
+ (
+ (-78.2,25.2,-77.5,23.8,-78.2,25.2),
+ ),
+ ),
+ CountryOutline(
+ "Bangladesh",
+ "BD",
+ (
+ (92.7,22,92.4,20.7,91.4,22.8,89,22.1,88.6,26.4,92.4,25,91.2,23.5,92.7,22),
+ ),
+ ),
+ CountryOutline(
+ "Belarus",
+ "BY",
+ (
+ (28.2,56.2,30.9,55.6,32.7,53.4,31.8,52.1,23.5,51.6,23.5,53.9,28.2,56.2),
+ ),
+ ),
+ CountryOutline(
+ "Belgium",
+ "BE",
+ (
+ (6.2,50.8,5.7,49.5,2.5,51.1,6.2,50.8),
+ ),
+ ),
+ CountryOutline(
+ "Belize",
+ "BZ",
+ (
+ (-89.1,17.8,-88.1,18.3,-88.9,15.9,-89.1,17.8),
+ ),
+ ),
+ CountryOutline(
+ "Benin",
+ "BJ",
+ (
+ (2.7,6.3,0.8,10.5,2.8,12.2,2.7,6.3),
+ ),
+ ),
+ CountryOutline(
+ "Bhutan",
+ "BT",
+ (
+ (91.7,27.8,88.8,27.1,91.7,27.8),
+ ),
+ ),
+ CountryOutline(
+ "Bolivia",
+ "BO",
+ (
+ (-69.5,-11,-65.3,-9.8,-65.4,-11.6,-60.5,-13.8,-60.2,-16.3,-58.2,-16.3,-57.9,-20,-61.8,-19.6,-62.7,-22.2,-67.8,-22.9,-69.5,-11),
+ ),
+ ),
+ CountryOutline(
+ "Bosnia and Herz.",
+ "BA",
+ (
+ (18.6,42.7,16,45.2,19.4,44.9,18.6,42.7),
+ ),
+ ),
+ CountryOutline(
+ "Botswana",
+ "BW",
+ (
+ (29.4,-22.1,25.7,-25.5,21.6,-26.7,19.9,-24.8,20.9,-18.3,25.3,-17.7,29.4,-22.1),
+ ),
+ ),
+ CountryOutline(
+ "Brazil",
+ "BR",
+ (
+ (-53.4,-33.8,-53.8,-32,-57.6,-30.2,-53.6,-26.1,-55.8,-22.4,-57.9,-22.1,-58.2,-16.3,-60.2,-16.3,-60.5,-13.8,-65.4,-11.6,-65.3,-9.8,-70.5,-11,-70.5,-9.5,-72.2,-10.1,-74,-7.5,-72.9,-5.3,-69.9,-4.3,-69.8,1.7,-65.5,0.8,-63.4,2.2,-64.8,4.1,-60.7,5.2,-59,1.3,-52.9,2.1,-51.3,4.2,-50.4,-0.1,-44.6,-2.7,-40,-2.9,-35.6,-5.1,-34.7,-7.3,-38.7,-13.1,-40.9,-21.9,-47.6,-24.9,-48.9,-28.7,-53.4,-33.8),
+ ),
+ ),
+ CountryOutline(
+ "Bulgaria",
+ "BG",
+ (
+ (22.7,44.2,28.6,43.7,28,42,23,41.3,22.7,44.2),
+ ),
+ ),
+ CountryOutline(
+ "Burkina Faso",
+ "BF",
+ (
+ (-5.4,10.4,-4.3,13.2,-1.1,15,2.2,12.6,0.9,11,-2.9,11,-2.8,9.6,-5.4,10.4),
+ ),
+ ),
+ CountryOutline(
+ "Burundi",
+ "BI",
+ (
+ (30.5,-2.4,29.3,-4.5,29,-2.8,30.5,-2.4),
+ ),
+ ),
+ CountryOutline(
+ "Cambodia",
+ "KH",
+ (
+ (102.6,12.2,103,14.2,107.6,13.5,106.2,11,103.5,10.6,102.6,12.2),
+ ),
+ ),
+ CountryOutline(
+ "Cameroon",
+ "CM",
+ (
+ (14.5,12.9,14.5,4.7,15.9,1.7,9.6,2.3,8.8,5.5,11.7,7,14.5,12.9),
+ ),
+ ),
+ CountryOutline(
+ "Canada",
+ "CA",
+ (
+ (-122.8,49,-127.4,50.8,-130.5,54.3,-130,55.9,-135.5,59.8,-137.5,58.9,-141,60.3,-141,69.7,-136.5,68.9,-128.1,70.5,-113.5,67.7,-106.1,68.8,-101.5,67.6,-97.7,68.6,-96.1,67.3,-94.2,69.1,-96.5,70.1,-95.2,71.9,-87.4,67.2,-85.5,69.9,-82.6,69.7,-81.4,67.1,-85.8,66.6,-90.7,63.6,-94.7,58.9,-92.3,57.1,-82.3,55.1,-79.9,51.2,-78.6,52.6,-79.8,54.7,-76.5,56.5,-78.5,58.8,-77.3,59.9,-78.1,62.3,-73.8,62.4,-69.6,61.1,-67.6,58.2,-64.6,60.3,-61.8,56.3,-57.3,54.6,-55.7,52.1,-60,50.2,-66.4,50.2,-71.1,46.8,-65.1,49.2,-64.5,46.2,-60.5,47,-59.8,45.9,-65.4,43.5,-66.2,44.5,-64.4,45.3,-67.1,45.1,-69.2,47.4,-71.5,45,-82.4,41.7,-82.6,45.3,-88.4,48.3,-122.8,49),
+ (-84,62.5,-81.9,62.9,-84,62.5),
+ (-79.8,72.8,-80.8,73.7,-76.3,72.8,-79.8,72.8),
+ (-93.6,75,-96.8,74.9,-93.6,75),
+ (-93.8,77.5,-96.4,77.8,-93.8,77.5),
+ (-96.8,78.8,-95.6,78.4,-98.6,78.9,-96.8,78.8),
+ (-88.2,74.4,-97.1,76.8,-79.8,74.9,-88.2,74.4),
+ (-111.3,78.2,-109.9,78,-113.5,77.7,-111.3,78.2),
+ (-111,78.8,-109.7,78.6,-112.5,78.4,-111,78.8),
+ (-55.6,51.3,-56.8,49.8,-53.5,49.2,-53.1,46.7,-59.3,47.6,-55.6,51.3),
+ (-83.9,65.1,-80.1,63.7,-87.2,63.5,-85.9,65.7,-83.9,65.1),
+ (-78.8,72.4,-68.8,70.5,-67,69.2,-68.8,68.7,-61.9,66.9,-63.9,65,-68,66.3,-64.7,63.4,-68.8,63.7,-66.2,61.9,-68.9,62.3,-78.6,64.6,-74,65.5,-73.3,68.1,-79,70.2,-88.7,70.4,-90.2,72.2,-85.8,73.8,-85.8,72.5,-82.3,73.8,-78.8,72.4),
+ (-94.5,74.1,-90.5,73.9,-95.4,72.1,-96,73.4,-94.5,74.1),
+ (-122.9,76.1,-116.2,77.6,-122.9,76.1),
+ (-132.7,54,-131.2,52.2,-132.7,54),
+ (-105.5,79.3,-99.7,77.9,-105.5,79.3),
+ (-123.5,48.5,-128.4,50.8,-123.5,48.5),
+ (-121.5,74.4,-115.5,73.5,-123.1,70.9,-125.9,71.9,-123.9,73.7,-124.9,74.3,-121.5,74.4),
+ (-107.8,75.8,-105.7,75.5,-117.7,75.2,-115.4,76.5,-107.8,75.8),
+ (-106.5,73.1,-101.1,69.6,-113.3,68.5,-117.3,70,-112.4,70.4,-119.4,71.6,-115.2,73.3,-108.2,71.7,-108.4,73.1,-106.5,73.1),
+ (-100.4,72.7,-101.5,73.4,-97.4,73.8,-96.5,72.6,-98.4,71.3,-102.5,72.5,-100.4,72.7),
+ (-106.6,73.6,-104.5,73.4,-106.6,73.6),
+ (-98.5,76.7,-98.2,75,-102.5,75.6,-98.5,76.7),
+ (-96,80.6,-92.4,81.3,-85.8,79.3,-92.9,78.3,-96,80.6),
+ (-91.6,81.9,-61.8,82.6,-76.9,79.3,-75.4,78.5,-80.6,76.2,-89.5,76.5,-88.3,77.9,-85,77.5,-88,78.4,-85.1,79.3,-86.9,80.3,-81.8,80.5,-91.6,81.9),
+ (-75.2,67.4,-77.2,67.6,-75.2,67.4),
+ (-96.3,69.5,-99.8,69.4,-96.3,69.5),
+ (-64.5,49.9,-61.8,49.1,-64.5,49.9),
+ (-64,47,-62,46.4,-64,47),
+ ),
+ ),
+ CountryOutline(
+ "Central African Rep.",
+ "CF",
+ (
+ (27.4,5.2,22.4,4,19.5,5,16,2.3,14.5,5.5,15.3,7.4,22.9,11.1,27.4,5.2),
+ ),
+ ),
+ CountryOutline(
+ "Chad",
+ "TD",
+ (
+ (23.8,19.6,23.9,15.6,21.9,12.6,22.9,11.1,15.3,7.4,13.5,14.4,15.9,20.4,14.9,22.9,23.8,19.6),
+ ),
+ ),
+ CountryOutline(
+ "Chile",
+ "CL",
+ (
+ (-68.6,-52.6,-68.6,-54.9,-67,-54.9,-68.1,-55.6,-74.7,-52.8,-71.1,-54.1,-68.6,-52.6),
+ (-69.6,-17.6,-67,-23,-70.5,-31.4,-69.8,-34.2,-72.1,-42.3,-71.2,-44.8,-73.4,-49.3,-71.9,-52,-68.6,-52.3,-71.4,-53.9,-74.9,-52.3,-75.6,-48.7,-74.1,-46.9,-75.6,-46.6,-72.7,-42.4,-74.3,-43.2,-69.6,-17.6),
+ ),
+ ),
+ CountryOutline(
+ "China",
+ "CN",
+ (
+ (109.5,18.2,108.6,19.4,110.8,20.1,109.5,18.2),
+ (80.3,42.3,80,44.9,87.8,49.3,91,46.9,90.9,45.3,96.3,42.7,109.2,42.5,111.9,45.1,119.7,46.7,115.5,48.1,122.2,53.4,125.9,52.8,131,47.8,135,48.5,133.1,45.1,131,45,130.6,42.4,121.1,38.9,121.6,40.9,117.5,38.7,122.4,37.5,119.2,34.9,121.9,31.7,121.7,28.2,118.7,24.5,110.4,20.3,105.3,23.4,101.7,22.3,101.8,21.2,99.2,22.1,97.6,23.9,98.7,27.5,96.1,29.5,88.8,27.3,78.7,31.5,78.9,34.3,73.7,39.4,80.3,42.3),
+ ),
+ ),
+ CountryOutline(
+ "Colombia",
+ "CO",
+ (
+ (-66.9,1.3,-69.8,1.7,-69.9,-4.3,-70,-2.7,-77.4,0.4,-79,1.7,-77.1,3.8,-77.5,8.5,-71.4,12.4,-73.3,9.2,-72,7,-67.3,6.1,-66.9,1.3),
+ ),
+ ),
+ CountryOutline(
+ "Congo",
+ "CG",
+ (
+ (18.5,3.5,16,-3.5,11.9,-5,11.5,-2.8,14.4,-1.3,13.1,2.3,15.9,1.7,18.5,3.5),
+ ),
+ ),
+ CountryOutline(
+ "Costa Rica",
+ "CR",
+ (
+ (-82.5,9.6,-83,8.2,-85.9,10.9,-82.5,9.6),
+ ),
+ ),
+ CountryOutline(
+ "Côte d'Ivoire",
+ "CI",
+ (
+ (-8,10.2,-2.8,9.6,-2.9,5,-7.7,4.4,-8,10.2),
+ ),
+ ),
+ CountryOutline(
+ "Croatia",
+ "HR",
+ (
+ (16.6,46.5,19.4,45.2,15.8,44.8,18.5,42.5,13.7,45.1,16.6,46.5),
+ ),
+ ),
+ CountryOutline(
+ "Cuba",
+ "CU",
+ (
+ (-82.3,23.2,-74.2,20.3,-77.8,19.9,-81.8,22.6,-85,21.9,-82.3,23.2),
+ ),
+ ),
+ CountryOutline(
+ "Cyprus",
+ "CY",
+ (
+ (32.7,35.1,34,35,32.7,35.1),
+ ),
+ ),
+ CountryOutline(
+ "Czechia",
+ "CZ",
+ (
+ (15,51.1,18.9,49.5,12.5,49.5,15,51.1),
+ ),
+ ),
+ CountryOutline(
+ "Dem. Rep. Congo",
+ "CD",
+ (
+ (29.3,-4.5,30.7,-8.3,28.7,-8.5,28.4,-11.8,29.7,-13.3,22.2,-11.1,21.7,-7.3,17.5,-8.1,16.3,-5.9,12.2,-5.8,16,-3.5,19.5,5,29.7,4.6,31.2,2.2,29.3,-4.5),
+ ),
+ ),
+ CountryOutline(
+ "Denmark",
+ "DK",
+ (
+ (9.9,55,8.1,56.5,10.6,57.7,9.9,55),
+ (12.4,56.1,12.1,54.8,11,55.4,12.4,56.1),
+ ),
+ ),
+ CountryOutline(
+ "Djibouti",
+ "DJ",
+ (
+ (42.4,12.5,42.8,10.9,42.4,12.5),
+ ),
+ ),
+ CountryOutline(
+ "Dominican Rep.",
+ "DO",
+ (
+ (-71.7,18,-71.6,19.9,-68.3,18.6,-71.7,18),
+ ),
+ ),
+ CountryOutline(
+ "Ecuador",
+ "EC",
+ (
+ (-75.4,-0.2,-78.6,-4.5,-80.4,-4.4,-80.1,0.8,-75.4,-0.2),
+ ),
+ ),
+ CountryOutline(
+ "Egypt",
+ "EG",
+ (
+ (36.9,22,25,22,25.2,31.6,34.3,31.2,34.2,27.8,32.3,29.8,36.9,22),
+ ),
+ ),
+ CountryOutline(
+ "El Salvador",
+ "SV",
+ (
+ (-89.4,14.4,-87.9,13.1,-90.1,13.7,-89.4,14.4),
+ ),
+ ),
+ CountryOutline(
+ "Eq. Guinea",
+ "GQ",
+ (
+ (9.6,2.3,11.3,1.1,9.5,1,9.6,2.3),
+ ),
+ ),
+ CountryOutline(
+ "Eritrea",
+ "ER",
+ (
+ (36.4,14.4,38.4,18,43.1,12.7,36.4,14.4),
+ ),
+ ),
+ CountryOutline(
+ "Estonia",
+ "EE",
+ (
+ (28,59.5,27.3,57.5,23.3,59.2,28,59.5),
+ ),
+ ),
+ CountryOutline(
+ "eSwatini",
+ "SZ",
+ (
+ (32.1,-26.7,31,-25.7,32.1,-26.7),
+ ),
+ ),
+ CountryOutline(
+ "Ethiopia",
+ "ET",
+ (
+ (47.8,8,45,5,39.6,3.4,36.2,4.4,33,7.8,37.9,15,41.6,13.5,43.7,9.2,47.8,8),
+ ),
+ ),
+ CountryOutline(
+ "Falkland Is.",
+ "FK",
+ (
+ (-61.2,-51.8,-57.7,-51.5,-61.2,-51.8),
+ ),
+ ),
+ CountryOutline(
+ "Finland",
+ "FI",
+ (
+ (28.6,69.1,31.1,62.4,28.1,60.5,21.3,60.7,21.5,63.2,25.4,65.1,20.6,69.1,24.7,68.6,27.7,70.2,28.6,69.1),
+ ),
+ ),
+ CountryOutline(
+ "Fr. S. Antarctic Lands",
+ "TF",
+ (
+ (68.9,-48.6,70.6,-49.3,68.7,-49.8,68.9,-48.6),
+ ),
+ ),
+ CountryOutline(
+ "France",
+ "FR",
+ (
+ (-51.7,4.2,-52.9,2.1,-54.5,2.3,-54,5.8,-51.7,4.2),
+ (6.2,49.5,8.1,49,6,46.7,7.4,43.7,1.8,42.3,-1.9,43.4,-1.2,46,-4.6,48.7,-1.6,48.6,-1.9,49.8,2.5,51.1,6.2,49.5),
+ (8.7,42.6,9.2,41.4,8.7,42.6),
+ ),
+ ),
+ CountryOutline(
+ "Gabon",
+ "GA",
+ (
+ (11.3,2.3,14.3,1.2,14.4,-1.3,11.1,-4,8.8,-1.1,11.3,2.3),
+ ),
+ ),
+ CountryOutline(
+ "Gambia",
+ "GM",
+ (
+ (-16.7,13.6,-13.8,13.5,-16.7,13.6),
+ ),
+ ),
+ CountryOutline(
+ "Georgia",
+ "GE",
+ (
+ (40,43.4,46.6,41.2,41.6,41.5,40,43.4),
+ ),
+ ),
+ CountryOutline(
+ "Germany",
+ "DE",
+ (
+ (14.1,53.8,15,51.1,12.2,50.3,12.9,47.5,7.5,47.6,8.1,49,6,50.1,7.1,53.7,9.9,55,14.1,53.8),
+ ),
+ ),
+ CountryOutline(
+ "Ghana",
+ "GH",
+ (
+ (0,11,1.1,5.9,-2.9,5,-2.9,11,0,11),
+ ),
+ ),
+ CountryOutline(
+ "Greece",
+ "GR",
+ (
+ (26.3,35.3,23.5,35.3,26.3,35.3),
+ (23,41.3,26.6,41.6,22.6,40.3,24,37.7,22.5,36.4,20.2,39.6,23,41.3),
+ ),
+ ),
+ CountryOutline(
+ "Greenland",
+ "GL",
+ (
+ (-46.8,82.6,-27.1,83.5,-20.8,82.7,-31.9,82.2,-12.2,81.3,-20,80.2,-17.7,80.1,-19.7,78.8,-18.5,77,-21.7,76.6,-19.4,74.3,-24.8,72.3,-21.8,70.7,-25.5,71.4,-26.4,70.2,-22.3,70.1,-39.8,65.5,-43.4,60.1,-48.3,60.9,-51.6,63.6,-54,67.2,-50.9,69.9,-54.7,69.6,-54.4,70.8,-51.4,70.6,-55.8,71.7,-54.7,72.6,-58.6,75.5,-68.5,76.1,-71.4,77,-66.8,77.4,-73.3,78,-65.7,79.4,-68,80.1,-62.7,81.8,-44.5,81.7,-46.8,82.6),
+ ),
+ ),
+ CountryOutline(
+ "Guatemala",
+ "GT",
+ (
+ (-92.2,14.5,-90.5,16.1,-91,17.8,-89.1,17.8,-88.2,15.7,-89.4,14.4,-92.2,14.5),
+ ),
+ ),
+ CountryOutline(
+ "Guinea",
+ "GN",
+ (
+ (-13.7,12.6,-9.1,12.3,-8.3,7.7,-11.1,10,-13.2,8.9,-15.1,11,-13.7,12.6),
+ ),
+ ),
+ CountryOutline(
+ "Guinea-Bissau",
+ "GW",
+ (
+ (-16.7,12.4,-13.7,11.8,-15.1,11,-16.7,12.4),
+ ),
+ ),
+ CountryOutline(
+ "Guyana",
+ "GY",
+ (
+ (-56.5,1.9,-59.6,1.8,-61.4,6,-59.8,8.4,-57.1,6,-58,4.1,-56.5,1.9),
+ ),
+ ),
+ CountryOutline(
+ "Haiti",
+ "HT",
+ (
+ (-71.7,19.7,-71.7,18,-74.5,18.3,-71.7,19.7),
+ ),
+ ),
+ CountryOutline(
+ "Honduras",
+ "HN",
+ (
+ (-83.1,15,-87.3,13,-89.4,14.4,-87.9,15.9,-83.1,15),
+ ),
+ ),
+ CountryOutline(
+ "Hungary",
+ "HU",
+ (
+ (22.1,48.4,21,46.3,16.2,46.9,17,48.1,22.1,48.4),
+ ),
+ ),
+ CountryOutline(
+ "Iceland",
+ "IS",
+ (
+ (-14.5,66.5,-13.6,65.1,-18.7,63.5,-22.8,64,-21.8,64.4,-24,64.9,-22.2,65.4,-24.3,65.6,-14.5,66.5),
+ ),
+ ),
+ CountryOutline(
+ "India",
+ "IN",
+ (
+ (97.3,28.3,92.7,22,91.2,23.5,92.4,25,88.6,26.4,88.9,21.7,80.3,15.9,79.9,10.4,77.5,8,72.6,21.4,70.5,20.9,68.2,23.7,71,24.4,69.5,26.9,75.3,32.3,73.7,34.3,77.8,35.5,78.7,31.5,81.1,30.2,80.1,28.8,83.3,27.4,88.1,26.4,88.7,28.1,92,26.8,96.1,29.5,97.3,28.3),
+ ),
+ ),
+ CountryOutline(
+ "Indonesia",
+ "ID",
+ (
+ (141,-2.6,141,-9.1,137.6,-8.4,137.9,-5.4,133,-4.1,132,-2.8,133.7,-2.2,130.5,-0.9,134,-0.8,135.5,-3.4,137.4,-1.7,141,-2.6),
+ (125,-8.9,123.5,-10.2,125,-8.9),
+ (117.9,4.1,119,0.9,116.1,-4,110.2,-2.9,109.7,2,110.5,0.8,113.8,1.2,115.9,4.3,117.9,4.1),
+ (129.4,-2.8,130.8,-3.9,127.9,-3.4,129.4,-2.8),
+ (127.9,2.2,128.1,-0.9,127.9,2.2),
+ (122.9,0.9,125.2,1.4,120,-0.5,123.3,-0.6,121.5,-1.9,123.2,-5.3,121,-2.6,119.8,-5.7,118.8,-2.8,119.8,0.2,122.9,0.9),
+ (120.3,-10.3,119,-9.6,120.3,-10.3),
+ (121.3,-8.5,122.9,-8.1,119.9,-8.8,121.3,-8.5),
+ (118.3,-8.4,116.7,-9,118.3,-8.4),
+ (108.5,-6.4,115.7,-8.4,105.4,-6.9,108.5,-6.4),
+ (104.4,-1.1,106.1,-3.1,105.8,-5.9,102.6,-4.2,95.3,5.5,97.5,5.2,104.4,-1.1),
+ ),
+ ),
+ CountryOutline(
+ "Iran",
+ "IR",
+ (
+ (48.6,29.9,45.4,34,46.1,35.7,44.1,39.4,48.1,39.6,50.8,36.9,56.6,38.1,61.1,36.5,60.9,29.8,63.3,26.8,61.5,25.1,57.4,25.7,48.6,29.9),
+ ),
+ ),
+ CountryOutline(
+ "Iraq",
+ "IQ",
+ (
+ (39.2,32.2,41.3,36.4,44.8,37.2,48.6,29.9,44.7,29.2,39.2,32.2),
+ ),
+ ),
+ CountryOutline(
+ "Ireland",
+ "IE",
+ (
+ (-6.2,53.9,-6.8,52.3,-10,51.8,-7.6,55.1,-6.2,53.9),
+ ),
+ ),
+ CountryOutline(
+ "Israel",
+ "IL",
+ (
+ (35.7,32.7,34.9,29.5,34.3,31.2,35.7,32.7),
+ ),
+ ),
+ CountryOutline(
+ "Italy",
+ "IT",
+ (
+ (10.4,46.9,13.8,46.5,12.6,44.1,18.3,39.8,16.9,40.4,15.7,37.9,15.4,40,10.2,43.9,7.4,43.7,6.8,46,10.4,46.9),
+ (14.8,38.1,15.1,36.6,12.4,37.6,14.8,38.1),
+ (8.7,40.9,9.8,40.5,8.8,38.9,8.7,40.9),
+ ),
+ ),
+ CountryOutline(
+ "Jamaica",
+ "JM",
+ (
+ (-77.6,18.5,-76.2,17.9,-77.6,18.5),
+ ),
+ ),
+ CountryOutline(
+ "Japan",
+ "JP",
+ (
+ (141.9,39.2,140.3,35.1,135.8,33.5,135.1,34.6,131,33.9,132,33.1,130.2,31.4,129.4,33.3,139.4,38.2,140.3,41.2,141.9,39.2),
+ (144.6,44,145.5,43.3,140,41.6,142,45.6,144.6,44),
+ (132.4,33.5,134.8,33.8,132.4,33.5),
+ ),
+ ),
+ CountryOutline(
+ "Jordan",
+ "JO",
+ (
+ (35.5,32.4,38.8,33.4,39.2,32.2,37,31.5,38,30.5,36.1,29.2,34.9,29.5,35.5,32.4),
+ ),
+ ),
+ CountryOutline(
+ "Kazakhstan",
+ "KZ",
+ (
+ (87.4,49.2,80,44.9,80.3,42.3,74.2,43.3,68.6,40.7,64.9,43.7,62,43.5,58.5,45.6,55.9,45,56,41.3,52.5,41.8,50.3,44.6,53,45.3,53,46.9,49.1,46.4,46.5,48.4,50.8,51.7,61.3,50.8,60,52,61.4,54,69.1,55.4,73.4,53.5,76.9,54.5,80,50.9,87.4,49.2),
+ ),
+ ),
+ CountryOutline(
+ "Kenya",
+ "KE",
+ (
+ (39.2,-4.7,33.9,-0.9,35.3,5.5,38.1,3.6,41.9,3.9,41.6,-1.7,39.2,-4.7),
+ ),
+ ),
+ CountryOutline(
+ "Kosovo",
+ "XK",
+ (
+ (20.6,41.9,20.6,43.2,21.8,42.7,20.6,41.9),
+ ),
+ ),
+ CountryOutline(
+ "Kuwait",
+ "KW",
+ (
+ (48,30,48.4,28.6,46.6,29.1,48,30),
+ ),
+ ),
+ CountryOutline(
+ "Kyrgyzstan",
+ "KG",
+ (
+ (71,42.3,74.2,43.3,80.3,42.3,73.7,39.4,69.5,39.5,73.1,40.9,71,42.3),
+ ),
+ ),
+ CountryOutline(
+ "Laos",
+ "LA",
+ (
+ (107.4,14.2,105.2,14.3,104,18.2,101.1,17.5,100.1,20.4,101.7,22.3,104.4,20.8,103.9,19.3,107.4,14.2),
+ ),
+ ),
+ CountryOutline(
+ "Latvia",
+ "LV",
+ (
+ (27.3,57.5,28.2,56.2,26.5,55.6,21.1,56,22.5,57.8,27.3,57.5),
+ ),
+ ),
+ CountryOutline(
+ "Lebanon",
+ "LB",
+ (
+ (35.8,33.3,36.4,34.6,35.8,33.3),
+ ),
+ ),
+ CountryOutline(
+ "Lesotho",
+ "LS",
+ (
+ (29,-29,28.1,-30.5,27,-29.9,29,-29),
+ ),
+ ),
+ CountryOutline(
+ "Liberia",
+ "LR",
+ (
+ (-8.4,7.7,-7.7,4.4,-11.4,6.8,-10.2,8.4,-8.4,7.7),
+ ),
+ ),
+ CountryOutline(
+ "Libya",
+ "LY",
+ (
+ (25,22,23.8,19.6,10.3,24.4,10,31.4,11.5,33.1,19.1,30.3,20.9,32.7,24.9,31.9,25,22),
+ ),
+ ),
+ CountryOutline(
+ "Lithuania",
+ "LT",
+ (
+ (26.5,55.6,23.5,53.9,21.1,56,26.5,55.6),
+ ),
+ ),
+ CountryOutline(
+ "Madagascar",
+ "MG",
+ (
+ (49.5,-12.5,50.4,-15.7,47.1,-24.9,45.4,-25.6,43.3,-22.8,44,-17.4,49.5,-12.5),
+ ),
+ ),
+ CountryOutline(
+ "Malawi",
+ "MW",
+ (
+ (32.8,-9.2,35.7,-14.6,35,-16.8,32.7,-13.7,32.8,-9.2),
+ ),
+ ),
+ CountryOutline(
+ "Malaysia",
+ "MY",
+ (
+ (100.1,6.5,103,5.5,104.2,1.3,101.4,2.8,100.1,6.5),
+ (117.9,4.1,115.9,4.3,114.6,1.4,109.8,1.3,115.3,4.3,116.7,6.9,119.2,5.4,117.9,4.1),
+ ),
+ ),
+ CountryOutline(
+ "Mali",
+ "ML",
+ (
+ (-11.5,12.4,-11.7,15.4,-5.5,15.5,-6.5,25,4.3,19.2,3.6,15.6,-4,13.5,-5.4,10.4,-11.5,12.4),
+ ),
+ ),
+ CountryOutline(
+ "Mauritania",
+ "MR",
+ (
+ (-17.1,21,-12.9,21.3,-12,25.9,-8.7,25.9,-8.7,27.4,-4.9,25,-6.5,25,-5.5,15.5,-12.2,14.6,-14.6,16.6,-16.5,16.1,-17.1,21),
+ ),
+ ),
+ CountryOutline(
+ "Mexico",
+ "MX",
+ (
+ (-117.1,32.5,-106.5,31.8,-103.9,29.3,-101.7,29.8,-97.1,25.9,-97.9,22.4,-95.9,18.8,-91.4,18.9,-90.3,21,-87.1,21.5,-87.8,18.3,-91,17.8,-90.5,16.1,-92.2,14.5,-103.5,18.3,-113.1,31.2,-114.9,31.4,-109.9,22.8,-115.1,27.7,-114.2,28.6,-117.1,32.5),
+ ),
+ ),
+ CountryOutline(
+ "Moldova",
+ "MD",
+ (
+ (26.6,48.2,30,46.4,28.2,45.5,26.6,48.2),
+ ),
+ ),
+ CountryOutline(
+ "Mongolia",
+ "MN",
+ (
+ (87.8,49.3,92.2,50.8,97.3,49.7,98.9,52,108.5,49.3,116.7,49.9,115.7,47.7,119.8,47,105,41.6,96.3,42.7,90.9,45.3,91,46.9,87.8,49.3),
+ ),
+ ),
+ CountryOutline(
+ "Montenegro",
+ "ME",
+ (
+ (20.1,42.6,18.5,42.5,20.1,42.6),
+ ),
+ ),
+ CountryOutline(
+ "Morocco",
+ "MA",
+ (
+ (-2.2,35.2,-1.3,32.3,-8.7,28.8,-8.8,27.1,-11.4,26.9,-14.8,21.5,-17,21.4,-14.4,26.3,-9.6,29.9,-8.7,33.2,-5.9,35.8,-2.2,35.2),
+ ),
+ ),
+ CountryOutline(
+ "Mozambique",
+ "MZ",
+ (
+ (34.6,-11.5,40.3,-10.3,40.8,-14.7,34.8,-19.8,35.5,-24.1,32.1,-26.7,31.2,-22.3,32.8,-16.7,30.2,-14.8,33.2,-14,35,-16.8,34.6,-11.5),
+ ),
+ ),
+ CountryOutline(
+ "Myanmar",
+ "MM",
+ (
+ (100.1,20.4,97.4,18.4,99.6,11.9,98.6,9.9,97.2,16.9,94.2,16,92.3,21.5,97.3,28.3,98.7,27.5,97.6,23.9,101.2,21.8,100.1,20.4),
+ ),
+ ),
+ CountryOutline(
+ "N. Cyprus",
+ "-99",
+ (
+ (32.7,35.1,34.6,35.7,32.7,35.1),
+ ),
+ ),
+ CountryOutline(
+ "Namibia",
+ "NA",
+ (
+ (19.9,-24.8,19.9,-28.5,16.3,-28.6,11.7,-17.3,25.1,-17.6,20.9,-18.3,19.9,-24.8),
+ ),
+ ),
+ CountryOutline(
+ "Nepal",
+ "NP",
+ (
+ (88.1,27.9,87.2,26.4,80.1,28.8,81.5,30.4,88.1,27.9),
+ ),
+ ),
+ CountryOutline(
+ "Netherlands",
+ "NL",
+ (
+ (6.9,53.5,6.2,50.8,3.3,51.3,6.9,53.5),
+ ),
+ ),
+ CountryOutline(
+ "New Caledonia",
+ "NC",
+ (
+ (165.8,-21.1,167.1,-22.2,164,-20.1,165.8,-21.1),
+ ),
+ ),
+ CountryOutline(
+ "New Zealand",
+ "NZ",
+ (
+ (176.9,-40.1,174.7,-41.3,174.7,-37.4,172.6,-34.5,176,-37.6,178.5,-37.7,176.9,-40.1),
+ (169.7,-43.6,172.8,-40.5,174.2,-41.3,170.6,-45.9,166.7,-46.2,169.7,-43.6),
+ ),
+ ),
+ CountryOutline(
+ "Nicaragua",
+ "NI",
+ (
+ (-83.7,10.9,-87.7,12.9,-83.1,15,-83.7,10.9),
+ ),
+ ),
+ CountryOutline(
+ "Niger",
+ "NE",
+ (
+ (14.9,22.9,15.9,20.4,13.5,14.4,14.2,12.5,5.4,13.9,3.6,11.7,1,12.9,0.4,14.9,3.6,15.6,4.3,19.2,12,23.5,14.9,22.9),
+ ),
+ ),
+ CountryOutline(
+ "Nigeria",
+ "NG",
+ (
+ (2.7,6.3,4.4,13.7,13.1,13.6,14.6,12.1,11.7,7,8.5,4.8,5.9,4.3,2.7,6.3),
+ ),
+ ),
+ CountryOutline(
+ "North Korea",
+ "KP",
+ (
+ (130.6,42.4,127.5,39.8,128.2,38.4,124.7,38.1,125.1,40.6,130.6,42.4),
+ ),
+ ),
+ CountryOutline(
+ "North Macedonia",
+ "MK",
+ (
+ (22.4,42.3,23,41.3,20.6,41.1,22.4,42.3),
+ ),
+ ),
+ CountryOutline(
+ "Norway",
+ "NO",
+ (
+ (15.1,79.7,21.5,79,15.9,76.8,10.4,79.7,15.1,79.7),
+ (31.1,69.6,18,68.6,12.6,64.1,11,58.9,5.7,58.6,5,62,19.2,69.8,28.2,71.2,31.1,69.6),
+ (27.4,80.1,17.4,80.3,27.4,80.1),
+ (24.7,77.9,20.7,77.7,24.7,77.9),
+ ),
+ ),
+ CountryOutline(
+ "Oman",
+ "OM",
+ (
+ (55.2,22.7,56.4,24.9,59.8,22.3,57.7,18.9,53.1,16.7,52,19,55,20,55.2,22.7),
+ ),
+ ),
+ CountryOutline(
+ "Pakistan",
+ "PK",
+ (
+ (77.8,35.5,73.7,34.3,75.3,32.3,69.5,26.9,71,24.4,61.5,25.1,63.3,26.8,60.9,29.8,66.3,29.9,71.8,36.5,75.2,37.1,77.8,35.5),
+ ),
+ ),
+ CountryOutline(
+ "Panama",
+ "PA",
+ (
+ (-77.4,8.7,-77.9,7.2,-79.1,9,-80.9,7.2,-82.9,9.5,-77.4,8.7),
+ ),
+ ),
+ CountryOutline(
+ "Papua New Guinea",
+ "PG",
+ (
+ (141,-2.6,147.6,-6.1,147.2,-7.4,150.7,-10.6,144.7,-7.6,141,-9.1,141,-2.6),
+ (152.6,-3.7,152.8,-4.8,150.7,-2.7,152.6,-3.7),
+ (151.3,-5.8,148.3,-5.7,152.1,-4.1,151.3,-5.8),
+ (154.8,-5.3,155.9,-6.8,154.8,-5.3),
+ ),
+ ),
+ CountryOutline(
+ "Paraguay",
+ "PY",
+ (
+ (-58.2,-20.2,-57.9,-22.1,-54.3,-24,-55.7,-27.4,-58.6,-27.1,-57.8,-25.2,-62.7,-22.2,-61.8,-19.6,-58.2,-20.2),
+ ),
+ ),
+ CountryOutline(
+ "Peru",
+ "PE",
+ (
+ (-69.9,-4.3,-72.9,-5.3,-74,-7.5,-68.7,-12.6,-70.4,-18.3,-76,-14.6,-81.4,-4.7,-80.3,-3.4,-78.6,-4.5,-75.1,-0.1,-73.1,-2.3,-70,-2.7,-69.9,-4.3),
+ ),
+ ),
+ CountryOutline(
+ "Philippines",
+ "PH",
+ (
+ (122.6,10,124.1,11.2,123,9,122.6,10),
+ (126.4,8.4,125.4,5.6,123.6,7.8,121.9,7.2,125.4,9.8,126.4,8.4),
+ (118.5,9.3,117.2,8.4,119.5,11.4,118.5,9.3),
+ (122.3,18.2,121.7,14.3,124.1,12.5,119.9,15.4,120.7,18.5,122.3,18.2),
+ (125.5,12.2,124.8,10.1,124.3,12.6,125.5,12.2),
+ ),
+ ),
+ CountryOutline(
+ "Poland",
+ "PL",
+ (
+ (23.5,53.9,24,50.7,22.8,49,16.2,50.4,14.1,53,17.6,54.9,23.5,53.9),
+ ),
+ ),
+ CountryOutline(
+ "Portugal",
+ "PT",
+ (
+ (-9,41.9,-6.4,41.4,-7.9,36.8,-9.5,38.7,-9,41.9),
+ ),
+ ),
+ CountryOutline(
+ "Puerto Rico",
+ "PR",
+ (
+ (-66.3,18.5,-67.2,17.9,-66.3,18.5),
+ ),
+ ),
+ CountryOutline(
+ "Qatar",
+ "QA",
+ (
+ (50.8,24.8,51.3,26.1,50.8,24.8),
+ ),
+ ),
+ CountryOutline(
+ "Romania",
+ "RO",
+ (
+ (28.2,45.5,29.6,45.3,28.6,43.7,22.9,43.8,20.2,46.1,26.6,48.2,28.2,45.5),
+ ),
+ ),
+ CountryOutline(
+ "Russia",
+ "RU",
+ (
+ (49.1,46.4,46.7,44.6,47.8,41.2,36.7,45.2,40.1,49.6,31.8,52.1,32.7,53.4,30.9,55.6,27.3,57.5,29.1,60,28.1,60.5,31.5,62.9,30,63.6,28.6,69.1,32.1,69.9,41.1,67.5,38.4,66,33.2,66.6,37,63.8,37.2,65.1,43.9,66.1,43.5,68.6,46.3,68.3,46.3,66.7,53.7,68.9,59.9,68.3,60.6,69.9,68.5,68.1,66.7,71,69.9,73,72.8,72.2,71.8,71.4,73.7,68.4,71.3,66.3,72.4,66.2,75.1,67.8,73.1,71.4,74.7,72.8,76.4,71.2,81.5,71.8,80.5,73.6,104.4,77.7,114.1,75.8,109.4,74.2,127,73.6,131.3,70.8,139.9,71.5,139.1,72.4,140.5,72.8,159,70.9,160.9,69.4,180,69,180,65,177.4,64.6,179.2,62.3,170.3,59.9,163.5,59.9,162,58.2,163.2,57.6,162.1,54.9,156.8,51,155.9,56.8,164.5,62.6,160.1,60.5,156.7,61.4,154.2,59.8,155,59.1,142.2,59,135.1,54.7,141.3,53.1,140.1,48.4,134.9,43.4,130.8,42.2,131,45,133.1,45.1,135,48.5,131,47.8,123.6,53.5,120.2,52.8,117.9,49.5,108.5,49.3,98.9,52,97.3,49.7,92.2,50.8,87.4,49.2,80,50.9,76.9,54.5,73.4,53.5,69.1,55.4,61.4,54,60,52,61.3,50.8,50.8,51.7,47.5,50.5,46.5,48.4,49.1,46.4),
+ (93.8,81,100.2,79.8,97.8,78.8,91.2,80.3,93.8,81),
+ (102.8,79.3,105.4,78.7,99.4,77.9,102.8,79.3),
+ (138.8,76.1,145.1,75.6,137,75.3,138.8,76.1),
+ (148.2,75.3,150.7,75.1,146.1,75.2,148.2,75.3),
+ (139.9,73.4,143.6,73.2,139.9,73.4),
+ (44.8,80.6,51.5,80.7,44.8,80.6),
+ (22.7,54.3,19.7,54.4,22.7,54.3),
+ (53.5,73.7,61.2,76.3,68.9,76.5,58.5,74.3,55.4,72.4,57.5,70.7,51.6,71.5,53.5,73.7),
+ (142.9,53.7,144.7,49,143.2,49.3,143.5,46.1,142.1,46,141.7,53.3,142.9,53.7),
+ (-174.9,67.2,-169.9,66,-173,64.3,-178.7,66.1,-180,65,-180,69,-174.9,67.2),
+ (-178.7,70.9,-180,71.5,-177.6,71.3,-178.7,70.9),
+ (33.4,46,36.5,45.5,33.9,44.4,32.5,45.3,33.4,46),
+ ),
+ ),
+ CountryOutline(
+ "Rwanda",
+ "RW",
+ (
+ (30.4,-1.1,29,-2.8,30.4,-1.1),
+ ),
+ ),
+ CountryOutline(
+ "S. Sudan",
+ "SS",
+ (
+ (30.8,3.5,23.9,8.6,25.8,10.4,31.4,9.8,33.2,12.2,33,7.8,35.3,5.5,30.8,3.5),
+ ),
+ ),
+ CountryOutline(
+ "Saudi Arabia",
+ "SA",
+ (
+ (35,29.4,39.2,32.2,47.5,29,52,23,55.2,22.7,55,20,47,16.9,43.4,17.6,42.8,16.3,35,29.4),
+ ),
+ ),
+ CountryOutline(
+ "Senegal",
+ "SN",
+ (
+ (-16.7,13.6,-17.6,14.7,-14.6,16.6,-11.5,12.4,-16.7,12.4,-13.8,13.5,-16.7,13.6),
+ ),
+ ),
+ CountryOutline(
+ "Serbia",
+ "RS",
+ (
+ (18.8,45.9,22.7,44.6,22.5,42.5,19.2,43.5,18.8,45.9),
+ ),
+ ),
+ CountryOutline(
+ "Sierra Leone",
+ "SL",
+ (
+ (-13.2,8.9,-11.1,10,-10.2,8.4,-11.4,6.8,-13.2,8.9),
+ ),
+ ),
+ CountryOutline(
+ "Slovakia",
+ "SK",
+ (
+ (22.6,49.1,16.9,48.5,22.6,49.1),
+ ),
+ ),
+ CountryOutline(
+ "Slovenia",
+ "SI",
+ (
+ (13.8,46.5,16.6,46.5,15.3,45.5,13.8,46.5),
+ ),
+ ),
+ CountryOutline(
+ "Solomon Is.",
+ "SB",
+ (
+ (159.6,-8,158.2,-7.4,159.6,-8),
+ ),
+ ),
+ CountryOutline(
+ "Somalia",
+ "SO",
+ (
+ (41.6,-1.7,41,2.8,45,5,48.9,9.5,48.9,11.4,51.1,12,48.6,5.3,41.6,-1.7),
+ ),
+ ),
+ CountryOutline(
+ "Somaliland",
+ "-99",
+ (
+ (48.9,11.4,47.8,8,42.6,10.6,48.9,11.4),
+ ),
+ ),
+ CountryOutline(
+ "South Africa",
+ "ZA",
+ (
+ (16.3,-28.6,19.9,-28.5,19.9,-24.8,21.6,-26.7,25.7,-25.5,29.4,-22.1,31.2,-22.3,31.9,-24.4,30.7,-26.7,32.8,-26.7,28.2,-32.8,20.1,-34.8,18.4,-34.1,16.3,-28.6),
+ ),
+ ),
+ CountryOutline(
+ "South Korea",
+ "KR",
+ (
+ (126.2,37.7,128.3,38.6,129.1,35.1,126.5,34.4,126.2,37.7),
+ ),
+ ),
+ CountryOutline(
+ "Spain",
+ "ES",
+ (
+ (-7.5,37.1,-6.4,41.4,-9.4,43,3,42.5,-2.1,36.7,-7.5,37.1),
+ ),
+ ),
+ CountryOutline(
+ "Sri Lanka",
+ "LK",
+ (
+ (81.8,7.5,80.3,6,80.1,9.8,81.8,7.5),
+ ),
+ ),
+ CountryOutline(
+ "Sudan",
+ "SD",
+ (
+ (24.6,8.2,21.9,12.6,25,22,36.9,22,38.4,18,34,8.7,32.7,12.2,31.4,9.8,25.1,10.3,24.6,8.2),
+ ),
+ ),
+ CountryOutline(
+ "Suriname",
+ "SR",
+ (
+ (-54.5,2.3,-56.5,1.9,-57.6,3.3,-57.1,6,-54,5.8,-54.5,2.3),
+ ),
+ ),
+ CountryOutline(
+ "Sweden",
+ "SE",
+ (
+ (11,58.9,12.6,61.3,11.9,63.1,16.8,68,20.6,69.1,23.5,67.9,23.9,66,17.8,62.7,17.1,61.3,18.8,60.1,15.9,56.1,12.9,55.4,11,58.9),
+ ),
+ ),
+ CountryOutline(
+ "Switzerland",
+ "CH",
+ (
+ (9.6,47.5,10.4,46.5,6,46.3,9.6,47.5),
+ ),
+ ),
+ CountryOutline(
+ "Syria",
+ "SY",
+ (
+ (35.7,32.7,36.7,36.8,42.3,37.2,41,34.4,35.7,32.7),
+ ),
+ ),
+ CountryOutline(
+ "Taiwan",
+ "TW",
+ (
+ (121.8,24.4,120.7,22,120.1,23.6,121.8,24.4),
+ ),
+ ),
+ CountryOutline(
+ "Tajikistan",
+ "TJ",
+ (
+ (67.8,37.1,67.7,39.6,70.7,41,69.5,39.5,73.7,39.4,75,37.4,71.8,36.7,70.8,38.5,67.8,37.1),
+ ),
+ ),
+ CountryOutline(
+ "Tanzania",
+ "TZ",
+ (
+ (33.9,-0.9,39.2,-4.7,39.5,-10.9,34.6,-11.5,29.6,-6.5,30.4,-1.1,33.9,-0.9),
+ ),
+ ),
+ CountryOutline(
+ "Thailand",
+ "TH",
+ (
+ (105.2,14.3,103,14.2,102.6,12.2,100.1,13.4,99.2,9.2,102.1,6.2,101.2,5.7,98.2,8.4,99.6,11.9,97.4,18.4,100.1,20.4,101.1,17.5,104.7,17.4,105.2,14.3),
+ ),
+ ),
+ CountryOutline(
+ "Timor-Leste",
+ "TL",
+ (
+ (125,-8.9,127.3,-8.4,125,-8.9),
+ ),
+ ),
+ CountryOutline(
+ "Togo",
+ "TG",
+ (
+ (0.9,11,1.1,5.9,0.9,11),
+ ),
+ ),
+ CountryOutline(
+ "Tunisia",
+ "TN",
+ (
+ (9.5,30.3,7.5,34.1,9.5,37.3,11,37.1,10.1,34.3,11.5,33.1,9.5,30.3),
+ ),
+ ),
+ CountryOutline(
+ "Turkey",
+ "TR",
+ (
+ (44.8,37.2,29.7,36.1,27.6,36.7,26.2,39.5,33.5,42,42.6,41.6,44.8,39.7,44.8,37.2),
+ (26.1,41.8,29,41.3,26.4,40.2,26.1,41.8),
+ ),
+ ),
+ CountryOutline(
+ "Turkmenistan",
+ "TM",
+ (
+ (52.5,41.8,57.1,41.3,58.6,42.8,66.5,37.4,62.2,35.3,57.3,38,53.9,37.2,52.7,40,54.7,41,52.5,41.8),
+ ),
+ ),
+ CountryOutline(
+ "Uganda",
+ "UG",
+ (
+ (33.9,-0.9,29.6,-1.3,31.2,3.8,34.5,3.6,33.9,-0.9),
+ ),
+ ),
+ CountryOutline(
+ "Ukraine",
+ "UA",
+ (
+ (31.8,52.1,40.1,49.6,39.7,47.9,35,45.7,31.7,46.7,28.7,45.3,30,46.4,28.7,48.1,22.1,48.4,23.5,51.6,31.8,52.1),
+ ),
+ ),
+ CountryOutline(
+ "United Arab Emirates",
+ "AE",
+ (
+ (51.6,24.2,56.3,25.7,55,22.5,51.6,24.2),
+ ),
+ ),
+ CountryOutline(
+ "United Kingdom",
+ "GB",
+ (
+ (-6.2,53.9,-7.6,55.1,-6.2,53.9),
+ (-3.1,53.4,-6.1,56.8,-5,58.6,-2,57.7,-3.1,56,1.7,52.7,1.4,51.3,-5.8,50.2,-3.4,51.4,-5.3,52,-4.6,53.5,-3.1,53.4),
+ ),
+ ),
+ CountryOutline(
+ "United States of America",
+ "US",
+ (
+ (-122.8,49,-88.4,48.3,-82.6,45.3,-82.7,41.7,-71.5,45,-69.2,47.4,-67,44.8,-70.1,43.7,-70,41.6,-75.5,39.5,-75.9,37.2,-76.3,39.2,-77,38.2,-75.7,35.6,-81.3,31.4,-80.4,25.2,-83.7,29.9,-86.4,30.4,-94.7,29.5,-97.5,25.8,-101,29.4,-103.9,29.3,-106.5,31.8,-117.1,32.5,-120.6,34.6,-124.4,40.3,-124.7,48.2,-122.6,47.1,-122.8,49),
+ (-166.5,60.4,-165.6,59.9,-167.5,60.2,-166.5,60.4),
+ (-153.2,58,-152.1,57.6,-154.5,57,-153.2,58),
+ (-141,69.7,-141,60.3,-137.5,58.9,-135.5,59.8,-130,55.9,-130.5,54.8,-134.1,58.1,-139.9,59.5,-147.1,60.9,-151.7,59.2,-150.6,61.3,-158.4,56,-164.9,54.6,-157,58.9,-162,58.7,-165.3,60.5,-165.7,62.1,-160.8,64.8,-168.1,65.7,-161.7,66.1,-166.2,68.9,-156.6,71.4,-141,69.7),
+ (-171.7,63.8,-168.7,63.3,-171.7,63.8),
+ ),
+ ),
+ CountryOutline(
+ "Uruguay",
+ "UY",
+ (
+ (-57.6,-30.2,-53.8,-32,-53.8,-34.4,-58.4,-33.9,-57.6,-30.2),
+ ),
+ ),
+ CountryOutline(
+ "Uzbekistan",
+ "UZ",
+ (
+ (56,41.3,55.9,45,58.5,45.6,62,43.5,64.9,43.7,68.3,40.7,71,42.3,73.1,40.9,67.7,39.6,67.8,37.1,58.6,42.8,56,41.3),
+ ),
+ ),
+ CountryOutline(
+ "Venezuela",
+ "VE",
+ (
+ (-60.7,5.2,-64.8,4.1,-63.4,2.2,-66.3,0.7,-67.8,2.8,-67.3,6.1,-72,7,-72.9,10.5,-71.3,11.8,-71.3,9.1,-69.9,12.2,-68.2,10.6,-61.9,10.7,-59.8,8.4,-60.7,5.2),
+ ),
+ ),
+ CountryOutline(
+ "Vietnam",
+ "VN",
+ (
+ (104.3,10.5,107.5,12.3,107.6,15.2,102.2,22.5,105.3,23.4,108.1,21.6,105.7,19.1,108.9,15.3,109.2,11.7,105.2,8.6,104.3,10.5),
+ ),
+ ),
+ CountryOutline(
+ "W. Sahara",
+ "EH",
+ (
+ (-8.7,27.7,-8.7,25.9,-12,25.9,-12.9,21.3,-17.1,21,-14.8,21.5,-11.4,26.9,-8.7,27.7),
+ ),
+ ),
+ CountryOutline(
+ "Yemen",
+ "YE",
+ (
+ (52,19,52.2,15.6,43.5,12.6,43.4,17.6,47,16.9,52,19),
+ ),
+ ),
+ CountryOutline(
+ "Zambia",
+ "ZM",
+ (
+ (30.7,-8.3,33.2,-9.7,33.2,-14,27,-17.9,23.2,-17.5,21.9,-12.9,24,-12.9,23.9,-10.9,29.7,-13.3,28.4,-9.2,30.7,-8.3),
+ ),
+ ),
+ CountryOutline(
+ "Zimbabwe",
+ "ZW",
+ (
+ (31.2,-22.3,28,-21.5,25.3,-17.7,30.3,-15.5,32.8,-16.7,31.2,-22.3),
+ ),
+ ),
+)
diff --git a/ports/python/hqtui/ui.py b/ports/python/hqtui/ui.py
index c4782f1..7bda9a5 100644
--- a/ports/python/hqtui/ui.py
+++ b/ports/python/hqtui/ui.py
@@ -529,6 +529,19 @@ def shapes(self, options: g.CanvasOptions, layout: Layout | None = None):
lambda s: g.draw_canvas(s, options),
)
+ def world_map(self, options: w.WorldMapOptions, layout: Layout | None = None):
+ """A world map, and the country under whatever gets clicked.
+
+ The click is answered by turning the cell back into degrees and testing
+ it against the outlines, so the answer is the country actually under the
+ cursor. Bounding boxes would be cheaper and wrong: Russia's covers most
+ of the northern hemisphere and Chile's covers Argentina.
+ """
+ return self._add(
+ self._constraint(layout or Layout(), "fill"),
+ lambda s: w.draw_world_map(s, options),
+ )
+
def sparkline(self, options: w.SparklineWidgetOptions, layout: Layout | None = None):
return self._add(self._leaf(layout or Layout(), 1), lambda s: w.draw_sparkline(s, options))
diff --git a/ports/python/hqtui/widgets/__init__.py b/ports/python/hqtui/widgets/__init__.py
index 1ea8e1f..a82dd1d 100644
--- a/ports/python/hqtui/widgets/__init__.py
+++ b/ports/python/hqtui/widgets/__init__.py
@@ -77,6 +77,7 @@
)
from .chart import ChartOptions, draw_chart
from .shadow import ShadowOptions, dim_rect, draw_shadow
+from .world import WorldMapOptions, country_at_cell, draw_world_map
from .surface import ClearOptions, FillOptions, draw_clear, draw_fill
from .scrollbar import (
ScrollbarOptions,
@@ -122,6 +123,7 @@
"draw_calendar", "is_leap_year", "CALENDAR_WIDTH",
"ChartOptions", "draw_chart", "ClearOptions", "FillOptions", "draw_clear", "draw_fill",
"ShadowOptions", "dim_rect", "draw_shadow",
+ "WorldMapOptions", "country_at_cell", "draw_world_map",
"ScrollbarOptions", "ScrollbarOrientation", "is_vertical", "offset_for_position",
"thumb", "draw_scrollbar", "draw_scrollbar_widget", "draw_select", "draw_sparkline", "draw_status_bar",
"draw_table", "draw_tabs", "draw_text", "draw_text_input", "draw_tooltip",
diff --git a/ports/python/hqtui/widgets/world.py b/ports/python/hqtui/widgets/world.py
new file mode 100644
index 0000000..cff21d6
--- /dev/null
+++ b/ports/python/hqtui/widgets/world.py
@@ -0,0 +1,90 @@
+"""A world map you can click.
+
+The drawing is the canvas doing what it already does — polylines in the caller's
+own coordinates, which for a map are degrees. What this adds is the other
+direction: turning a click back into a country.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Sequence
+
+from ..color import Color
+from ..graphics.canvas import Bounds, CanvasOptions, draw_canvas
+from ..graphics.world import (
+ WORLD_X,
+ WORLD_Y,
+ CountryOutline,
+ WorldShapeOptions,
+ country_at,
+ degrees_at,
+ world_shapes,
+)
+from ..surface import Surface
+
+__all__ = ["WorldMapOptions", "country_at_cell", "draw_world_map"]
+
+
+@dataclass(frozen=True, slots=True)
+class WorldMapOptions:
+ #: The window on the globe. None means all of it.
+ x: Bounds | None = None
+ y: Bounds | None = None
+ #: Coastline colour.
+ color: Color | None = None
+ #: Countries to pick out, by name or ISO code.
+ highlight: Sequence[str] = ()
+ highlight_color: Color | None = None
+ background: Color | None = None
+ grid: bool = False
+
+ @property
+ def window(self) -> tuple[Bounds, Bounds]:
+ return (self.x or WORLD_X, self.y or WORLD_Y)
+
+
+def draw_world_map(surface: Surface, options: WorldMapOptions = WorldMapOptions()) -> None:
+ if surface.empty:
+ return
+ theme = surface.theme
+ x, y = options.window
+ draw_canvas(
+ surface,
+ CanvasOptions(
+ shapes=world_shapes(
+ WorldShapeOptions(
+ color=options.color if options.color is not None else theme.border,
+ highlight=options.highlight,
+ highlight_color=(
+ options.highlight_color
+ if options.highlight_color is not None
+ else theme.accent
+ ),
+ )
+ ),
+ x=x,
+ y=y,
+ background=options.background,
+ grid=options.grid,
+ ),
+ )
+
+
+def country_at_cell(
+ column: int,
+ row: int,
+ width: int,
+ height: int,
+ options: WorldMapOptions = WorldMapOptions(),
+) -> CountryOutline | None:
+ """The country under a cell of a map drawn with these bounds.
+
+ Exposed so a caller can answer a hover as well as a click, and so the
+ arithmetic that has to agree with the drawing lives in one place.
+ """
+ x, y = options.window
+ at = degrees_at(column, row, width, height, x, y)
+ if at is None:
+ return None
+ return country_at(at[0], at[1])
diff --git a/ports/python/tests/test_conformance_widgets.py b/ports/python/tests/test_conformance_widgets.py
index acebbe5..69ae2e4 100644
--- a/ports/python/tests/test_conformance_widgets.py
+++ b/ports/python/tests/test_conformance_widgets.py
@@ -138,6 +138,14 @@ def draw_scene(case, name: str, s: Surface) -> None:
elif name == "shadow-solid":
w.draw_fill(s, w.FillOptions(symbol="x"))
w.draw_shadow(s, Rect(2, 1, 6, 2), w.ShadowOptions(color=rgb(0x10, 0x14, 0x18)))
+ elif name == "world":
+ w.draw_world_map(s, w.WorldMapOptions())
+ elif name == "world-zoom":
+ w.draw_world_map(s, w.WorldMapOptions(
+ x=gc.Bounds(112, 156), y=gc.Bounds(24, 50)
+ ))
+ elif name == "world-highlight":
+ w.draw_world_map(s, w.WorldMapOptions(highlight=("Brazil", "JP")))
elif name == "badge":
w.draw_badge(s, w.BadgeOptions(text="LIVE"))
elif name == "badge-outline":
diff --git a/ports/rust/examples/world-probe.rs b/ports/rust/examples/world-probe.rs
new file mode 100644
index 0000000..5befef9
--- /dev/null
+++ b/ports/rust/examples/world-probe.rs
@@ -0,0 +1,42 @@
+//! Prints what the country lookup answers for a fixed set of points.
+//!
+//! The same probe exists for every port, so "the ports agree about the world"
+//! is a diff rather than a hope.
+use hqtui::graphics::{country_at, degrees_at, WORLD_X, WORLD_Y};
+use hqtui::widgets::{country_at_cell, WorldMapOptions};
+
+fn main() {
+ let places: [(&str, f64, f64); 13] = [
+ ("Paris", 2.35, 48.86),
+ ("Tokyo", 139.7, 35.7),
+ ("Cairo", 31.2, 30.0),
+ ("Brasilia", -47.9, -15.8),
+ ("Canberra", 149.1, -35.3),
+ ("Denver", -105.0, 39.7),
+ ("Moscow", 37.6, 55.75),
+ ("Delhi", 77.2, 28.6),
+ ("Nairobi", 36.8, -1.3),
+ ("Pacific", -140.0, 0.0),
+ ("Atlantic", -30.0, 0.0),
+ ("SouthernOcean", 80.0, -40.0),
+ ("NorthPacific", -150.0, 40.0),
+ ];
+ for (name, lon, lat) in places {
+ let found = country_at(lon, lat).map(|c| c.name).unwrap_or("-");
+ println!("{name} {found}");
+ }
+
+ // The cell path, which has to agree with what the canvas drew.
+ let options = WorldMapOptions::default();
+ for (column, row) in [(173usize, 28usize), (74, 2), (20, 25), (88, 7)] {
+ let found = country_at_cell(column, row, 200, 50, &options).map(|c| c.name).unwrap_or("-");
+ println!("cell:{column},{row} {found}");
+ }
+
+ // And the projection itself, so a drift shows up as a number rather than as
+ // a country that happens to still be right.
+ for (column, row) in [(0usize, 0usize), (99, 25), (50, 13)] {
+ let (lon, lat) = degrees_at(column, row, 100, 26, WORLD_X, WORLD_Y).unwrap();
+ println!("degrees:{column},{row} {lon:.4} {lat:.4}");
+ }
+}
diff --git a/ports/rust/src/graphics/mod.rs b/ports/rust/src/graphics/mod.rs
index cce0075..26ef2f7 100644
--- a/ports/rust/src/graphics/mod.rs
+++ b/ports/rust/src/graphics/mod.rs
@@ -3,6 +3,8 @@
pub mod blocks;
pub mod canvas;
+pub mod world;
+pub mod world_data;
pub mod chart;
pub mod braille;
pub mod plot;
@@ -12,6 +14,11 @@ pub use blocks::{
HORIZONTAL_EIGHTHS, QUADRANTS, SHADES, VERTICAL_EIGHTHS,
};
pub use braille::BrailleCanvas;
+pub use world::{
+ country_at, country_bounds, degrees_at, find_country, world_shapes, WorldShapeOptions,
+ WORLD_X, WORLD_Y,
+};
+pub use world_data::{CountryOutline, WORLD_COUNTRIES};
pub use canvas::{
draw_canvas, projection, Bounds, CanvasOptions, Projection, Shape,
};
diff --git a/ports/rust/src/graphics/world.rs b/ports/rust/src/graphics/world.rs
new file mode 100644
index 0000000..7ed1897
--- /dev/null
+++ b/ports/rust/src/graphics/world.rs
@@ -0,0 +1,169 @@
+//! The world, as shapes for the canvas, and the lookup that makes it clickable.
+//!
+//! The canvas already draws in the caller's own coordinates, and longitude and
+//! latitude are just another pair of axes -- so a map is a list of polylines in
+//! degrees, and nothing here needs a projection of its own beyond deciding
+//! which window on the globe to show.
+//!
+//! The interesting half 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 testing it against the outlines.
+//! Doing it that way rather than with bounding boxes is what makes the answer
+//! right: Russia's bounding box covers most of the northern hemisphere, and
+//! Chile's covers Argentina.
+
+use crate::color::Color;
+use crate::graphics::canvas::{Bounds, Shape};
+use crate::graphics::world_data::{CountryOutline, WORLD_COUNTRIES};
+
+/// The whole globe, which is what a map shows unless told otherwise.
+pub const WORLD_X: Bounds = Bounds { min: -180.0, max: 180.0 };
+pub const WORLD_Y: Bounds = Bounds { min: -90.0, max: 90.0 };
+
+#[derive(Clone, Debug, Default)]
+pub struct WorldShapeOptions {
+ /// Colour for countries with nothing special about them.
+ pub color: Option,
+ /// Countries to pick out, by name or ISO code.
+ pub highlight: Vec,
+ pub highlight_color: Option,
+}
+
+/// Match on either the name or the ISO code, case-insensitively.
+fn matches(country: &CountryOutline, keys: &[String]) -> bool {
+ keys.iter().any(|key| {
+ !key.is_empty()
+ && (country.name.eq_ignore_ascii_case(key)
+ || (!country.iso.is_empty() && country.iso.eq_ignore_ascii_case(key)))
+ })
+}
+
+/// The world as canvas shapes, one polyline per landmass.
+///
+/// Polylines rather than scattered points: the outlines are closed rings, so
+/// joining them draws a coastline instead of a dotted suggestion of one, and it
+/// reads at a fraction of the resolution dots would need.
+pub fn world_shapes(options: &WorldShapeOptions) -> Vec {
+ let mut shapes = Vec::new();
+ for country in WORLD_COUNTRIES {
+ let picked = !options.highlight.is_empty() && matches(country, &options.highlight);
+ let color = if picked { options.highlight_color.or(options.color) } else { options.color };
+ for ring in country.rings {
+ let mut points: Vec<(f64, f64)> = Vec::with_capacity(ring.len() / 2 + 1);
+ let mut i = 0;
+ while i + 1 < ring.len() {
+ points.push((ring[i], ring[i + 1]));
+ i += 2;
+ }
+ // Closed: the last point joins the first, or every country has a
+ // gap in its coastline where the ring started.
+ if let Some(first) = points.first().copied() {
+ points.push(first);
+ }
+ shapes.push(Shape::Polyline { points, color });
+ }
+ }
+ shapes
+}
+
+/// Whether a point is inside a ring, by ray casting.
+///
+/// The ring is a flat list of interleaved coordinates, so this walks it two at
+/// a time rather than allocating a pair per vertex -- it runs once per country
+/// per click, and there are a couple of thousand vertices.
+fn inside_ring(ring: &[f64], lon: f64, lat: f64) -> bool {
+ let mut inside = false;
+ let n = ring.len() / 2;
+ if n == 0 {
+ return false;
+ }
+ let mut j = n - 1;
+ for i in 0..n {
+ let (xi, yi) = (ring[i * 2], ring[i * 2 + 1]);
+ let (xj, yj) = (ring[j * 2], ring[j * 2 + 1]);
+ if (yi > lat) != (yj > lat) && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi {
+ inside = !inside;
+ }
+ j = i;
+ }
+ inside
+}
+
+/// The country containing a point, or `None` for open water.
+///
+/// Where outlines overlap -- and at this resolution simplified borders do
+/// overlap -- the first match wins, which is stable because the data is sorted
+/// by name.
+pub fn country_at(lon: f64, lat: f64) -> Option<&'static CountryOutline> {
+ if !lon.is_finite() || !lat.is_finite() {
+ return None;
+ }
+ WORLD_COUNTRIES
+ .iter()
+ .find(|country| country.rings.iter().any(|ring| inside_ring(ring, lon, lat)))
+}
+
+/// Look a country up by name or ISO code.
+pub fn find_country(key: &str) -> Option<&'static CountryOutline> {
+ let keys = [key.to_string()];
+ WORLD_COUNTRIES.iter().find(|country| matches(country, &keys))
+}
+
+/// The window a country fills, with a little room around it.
+///
+/// For zooming a map to a country: the bounding box alone puts the coastline
+/// flat against the edge of the panel, which reads as though the country has
+/// been cut off rather than framed.
+pub fn country_bounds(country: &CountryOutline, margin: f64) -> (Bounds, Bounds) {
+ let mut min_lon = f64::INFINITY;
+ let mut max_lon = f64::NEG_INFINITY;
+ let mut min_lat = f64::INFINITY;
+ let mut max_lat = f64::NEG_INFINITY;
+ for ring in country.rings {
+ let mut i = 0;
+ while i + 1 < ring.len() {
+ min_lon = min_lon.min(ring[i]);
+ max_lon = max_lon.max(ring[i]);
+ min_lat = min_lat.min(ring[i + 1]);
+ max_lat = max_lat.max(ring[i + 1]);
+ i += 2;
+ }
+ }
+ if !min_lon.is_finite() {
+ return (WORLD_X, WORLD_Y);
+ }
+ // A single-point country would give a zero-width window, which cannot be
+ // mapped onto anything.
+ let pad_x = ((max_lon - min_lon) * margin).max(1.0);
+ let pad_y = ((max_lat - min_lat) * margin).max(1.0);
+ (
+ Bounds { min: min_lon - pad_x, max: max_lon + pad_x },
+ Bounds { min: min_lat - pad_y, max: max_lat + pad_y },
+ )
+}
+
+/// The degrees under a terminal cell, given the window the map was drawn with.
+///
+/// The inverse of what the canvas does on the way in, taken at the centre of
+/// the cell: a click lands on a whole cell, and the centre is the only point in
+/// it that is not arbitrarily nearer one neighbour than the other.
+pub fn degrees_at(
+ column: usize,
+ row: usize,
+ width: usize,
+ height: usize,
+ x: Bounds,
+ y: Bounds,
+) -> Option<(f64, f64)> {
+ if width == 0 || height == 0 {
+ return None;
+ }
+ // The canvas is 2x4 Braille pixels per cell, and it spans its bounds across
+ // `pixels - 1`, so the inverse has to use the same denominators or a click
+ // drifts from what was drawn.
+ let px = (width * 2).saturating_sub(1).max(1) as f64;
+ let py = (height * 4).saturating_sub(1).max(1) as f64;
+ let lon = x.min + ((column * 2 + 1) as f64 / px) * (x.max - x.min);
+ let lat = y.min + (1.0 - (row * 4 + 2) as f64 / py) * (y.max - y.min);
+ Some((lon, lat))
+}
diff --git a/ports/rust/src/graphics/world_data.rs b/ports/rust/src/graphics/world_data.rs
new file mode 100644
index 0000000..7d25d36
--- /dev/null
+++ b/ports/rust/src/graphics/world_data.rs
@@ -0,0 +1,1306 @@
+//! Country outlines, flattened for a terminal.
+//!
+//! Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's
+//! 1:110m Admin 0 countries, which is public domain. Do not edit by hand.
+//!
+//! Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --
+//! rather than a list of pairs, because at 1982 points the nested form
+//! costs a container per coordinate for no gain. A country has more than one
+//! ring when it is more than one landmass.
+//!
+//! 171 countries, 1982 points, simplified at 1 degrees.
+
+pub struct CountryOutline {
+ pub name: &'static str,
+ /// ISO 3166-1 alpha-2, where Natural Earth has one.
+ pub iso: &'static str,
+ /// Longitude and latitude, interleaved.
+ pub rings: &'static [&'static [f64]],
+}
+
+pub static WORLD_COUNTRIES: &[CountryOutline] = &[
+ CountryOutline {
+ name: "Afghanistan",
+ iso: "AF",
+ rings: &[
+ &[66.5,37.4,70.8,38.5,71.8,36.7,75.2,37.1,71.3,36.1,69.3,31.9,66.3,29.9,60.9,29.8,61.2,35.7,66.5,37.4],
+ ],
+ },
+ CountryOutline {
+ name: "Albania",
+ iso: "AL",
+ rings: &[
+ &[21.0,40.8,19.4,40.3,19.7,42.7,21.0,40.8],
+ ],
+ },
+ CountryOutline {
+ name: "Algeria",
+ iso: "DZ",
+ rings: &[
+ &[-8.7,27.4,-8.7,28.8,-1.3,32.3,-1.2,35.7,8.4,36.9,7.5,34.1,9.8,29.4,9.3,26.1,12.0,23.5,3.2,19.1,-8.7,27.4],
+ ],
+ },
+ CountryOutline {
+ name: "Angola",
+ iso: "AO",
+ rings: &[
+ &[12.3,-6.1,16.3,-5.9,17.5,-8.1,21.7,-7.3,22.2,-11.1,24.0,-11.2,24.0,-12.9,21.9,-12.9,23.2,-17.5,11.7,-17.3,13.7,-11.3,12.3,-6.1],
+ ],
+ },
+ CountryOutline {
+ name: "Antarctica",
+ iso: "AQ",
+ rings: &[
+ &[-48.7,-78.0,-43.9,-78.5,-43.3,-80.0,-54.2,-80.6,-48.7,-78.0],
+ &[-66.3,-80.3,-59.6,-80.0,-66.3,-80.3],
+ &[-73.9,-71.3,-70.3,-68.9,-68.3,-71.4,-75.0,-72.1,-73.9,-71.3],
+ &[-102.3,-71.9,-96.2,-72.5,-102.3,-71.9],
+ &[-122.6,-73.7,-118.7,-73.5,-122.6,-73.7],
+ &[-127.3,-73.5,-124.0,-73.9,-127.3,-73.5],
+ &[-163.7,-78.6,-159.2,-79.5,-163.7,-78.6],
+ &[180.0,-84.7,180.0,-90.0,-180.0,-90.0,-179.1,-84.1,-143.1,-85.0,-153.6,-83.7,-152.9,-82.0,-156.8,-81.1,-146.4,-80.3,-155.3,-79.1,-158.4,-76.9,-151.3,-77.4,-146.1,-76.5,-146.2,-75.4,-135.2,-74.3,-100.1,-74.9,-103.7,-72.6,-74.9,-73.9,-67.4,-72.5,-67.7,-67.3,-57.8,-63.3,-65.7,-68.0,-61.8,-70.7,-60.8,-73.7,-70.6,-76.6,-77.2,-76.7,-73.7,-77.9,-78.0,-79.2,-58.2,-83.2,-28.5,-80.3,-35.6,-79.5,-35.8,-78.3,-17.5,-75.1,-15.4,-73.1,-6.9,-70.9,27.1,-70.5,33.9,-68.5,38.6,-69.8,54.5,-65.8,61.4,-68.0,68.9,-67.9,69.7,-69.2,67.9,-71.9,69.9,-72.3,73.9,-69.9,88.0,-66.2,95.8,-67.4,102.8,-65.6,106.2,-66.9,113.6,-65.9,119.8,-67.3,135.1,-65.3,137.5,-67.0,145.5,-66.9,171.2,-71.7,163.6,-76.2,167.0,-78.8,161.8,-79.2,159.8,-80.9,169.4,-83.8,180.0,-84.7],
+ ],
+ },
+ CountryOutline {
+ name: "Argentina",
+ iso: "AR",
+ rings: &[
+ &[-68.6,-52.6,-65.0,-54.7,-68.6,-54.9,-68.6,-52.6],
+ &[-57.6,-30.2,-58.5,-34.4,-56.8,-36.9,-62.3,-38.8,-62.7,-41.0,-65.1,-41.1,-63.5,-42.6,-67.3,-45.6,-65.6,-47.2,-69.1,-50.7,-68.1,-52.3,-71.9,-52.0,-73.4,-49.3,-71.2,-44.8,-72.1,-42.3,-68.4,-24.5,-66.3,-21.8,-62.8,-22.0,-57.8,-25.2,-58.6,-27.1,-55.7,-27.4,-54.1,-25.5,-53.6,-26.9,-57.6,-30.2],
+ ],
+ },
+ CountryOutline {
+ name: "Armenia",
+ iso: "AM",
+ rings: &[
+ &[46.5,38.8,43.6,41.1,45.6,40.8,46.5,38.8],
+ ],
+ },
+ CountryOutline {
+ name: "Australia",
+ iso: "AU",
+ rings: &[
+ &[147.7,-40.8,147.9,-43.2,146.0,-43.5,144.7,-40.7,147.7,-40.8],
+ &[126.1,-32.2,118.0,-35.1,115.0,-34.2,113.7,-22.5,120.9,-19.7,125.7,-14.2,129.6,-15.0,132.4,-11.1,136.5,-11.9,135.5,-15.0,140.2,-17.7,142.5,-10.7,146.4,-19.0,150.7,-22.4,153.6,-28.1,150.0,-37.4,146.3,-39.0,140.6,-38.0,138.2,-34.4,136.8,-35.3,137.8,-32.9,136.0,-34.9,131.3,-31.5,126.1,-32.2],
+ ],
+ },
+ CountryOutline {
+ name: "Austria",
+ iso: "AT",
+ rings: &[
+ &[17.0,48.1,14.6,46.4,9.5,47.1,12.9,47.5,13.6,48.9,17.0,48.1],
+ ],
+ },
+ CountryOutline {
+ name: "Azerbaijan",
+ iso: "AZ",
+ rings: &[
+ &[46.4,41.9,50.4,40.3,48.9,38.3,45.6,39.9,45.0,41.2,46.4,41.9],
+ ],
+ },
+ CountryOutline {
+ name: "Bahamas",
+ iso: "BS",
+ rings: &[
+ &[-78.2,25.2,-77.5,23.8,-78.2,25.2],
+ ],
+ },
+ CountryOutline {
+ name: "Bangladesh",
+ iso: "BD",
+ rings: &[
+ &[92.7,22.0,92.4,20.7,91.4,22.8,89.0,22.1,88.6,26.4,92.4,25.0,91.2,23.5,92.7,22.0],
+ ],
+ },
+ CountryOutline {
+ name: "Belarus",
+ iso: "BY",
+ rings: &[
+ &[28.2,56.2,30.9,55.6,32.7,53.4,31.8,52.1,23.5,51.6,23.5,53.9,28.2,56.2],
+ ],
+ },
+ CountryOutline {
+ name: "Belgium",
+ iso: "BE",
+ rings: &[
+ &[6.2,50.8,5.7,49.5,2.5,51.1,6.2,50.8],
+ ],
+ },
+ CountryOutline {
+ name: "Belize",
+ iso: "BZ",
+ rings: &[
+ &[-89.1,17.8,-88.1,18.3,-88.9,15.9,-89.1,17.8],
+ ],
+ },
+ CountryOutline {
+ name: "Benin",
+ iso: "BJ",
+ rings: &[
+ &[2.7,6.3,0.8,10.5,2.8,12.2,2.7,6.3],
+ ],
+ },
+ CountryOutline {
+ name: "Bhutan",
+ iso: "BT",
+ rings: &[
+ &[91.7,27.8,88.8,27.1,91.7,27.8],
+ ],
+ },
+ CountryOutline {
+ name: "Bolivia",
+ iso: "BO",
+ rings: &[
+ &[-69.5,-11.0,-65.3,-9.8,-65.4,-11.6,-60.5,-13.8,-60.2,-16.3,-58.2,-16.3,-57.9,-20.0,-61.8,-19.6,-62.7,-22.2,-67.8,-22.9,-69.5,-11.0],
+ ],
+ },
+ CountryOutline {
+ name: "Bosnia and Herz.",
+ iso: "BA",
+ rings: &[
+ &[18.6,42.7,16.0,45.2,19.4,44.9,18.6,42.7],
+ ],
+ },
+ CountryOutline {
+ name: "Botswana",
+ iso: "BW",
+ rings: &[
+ &[29.4,-22.1,25.7,-25.5,21.6,-26.7,19.9,-24.8,20.9,-18.3,25.3,-17.7,29.4,-22.1],
+ ],
+ },
+ CountryOutline {
+ name: "Brazil",
+ iso: "BR",
+ rings: &[
+ &[-53.4,-33.8,-53.8,-32.0,-57.6,-30.2,-53.6,-26.1,-55.8,-22.4,-57.9,-22.1,-58.2,-16.3,-60.2,-16.3,-60.5,-13.8,-65.4,-11.6,-65.3,-9.8,-70.5,-11.0,-70.5,-9.5,-72.2,-10.1,-74.0,-7.5,-72.9,-5.3,-69.9,-4.3,-69.8,1.7,-65.5,0.8,-63.4,2.2,-64.8,4.1,-60.7,5.2,-59.0,1.3,-52.9,2.1,-51.3,4.2,-50.4,-0.1,-44.6,-2.7,-40.0,-2.9,-35.6,-5.1,-34.7,-7.3,-38.7,-13.1,-40.9,-21.9,-47.6,-24.9,-48.9,-28.7,-53.4,-33.8],
+ ],
+ },
+ CountryOutline {
+ name: "Bulgaria",
+ iso: "BG",
+ rings: &[
+ &[22.7,44.2,28.6,43.7,28.0,42.0,23.0,41.3,22.7,44.2],
+ ],
+ },
+ CountryOutline {
+ name: "Burkina Faso",
+ iso: "BF",
+ rings: &[
+ &[-5.4,10.4,-4.3,13.2,-1.1,15.0,2.2,12.6,0.9,11.0,-2.9,11.0,-2.8,9.6,-5.4,10.4],
+ ],
+ },
+ CountryOutline {
+ name: "Burundi",
+ iso: "BI",
+ rings: &[
+ &[30.5,-2.4,29.3,-4.5,29.0,-2.8,30.5,-2.4],
+ ],
+ },
+ CountryOutline {
+ name: "Cambodia",
+ iso: "KH",
+ rings: &[
+ &[102.6,12.2,103.0,14.2,107.6,13.5,106.2,11.0,103.5,10.6,102.6,12.2],
+ ],
+ },
+ CountryOutline {
+ name: "Cameroon",
+ iso: "CM",
+ rings: &[
+ &[14.5,12.9,14.5,4.7,15.9,1.7,9.6,2.3,8.8,5.5,11.7,7.0,14.5,12.9],
+ ],
+ },
+ CountryOutline {
+ name: "Canada",
+ iso: "CA",
+ rings: &[
+ &[-122.8,49.0,-127.4,50.8,-130.5,54.3,-130.0,55.9,-135.5,59.8,-137.5,58.9,-141.0,60.3,-141.0,69.7,-136.5,68.9,-128.1,70.5,-113.5,67.7,-106.1,68.8,-101.5,67.6,-97.7,68.6,-96.1,67.3,-94.2,69.1,-96.5,70.1,-95.2,71.9,-87.4,67.2,-85.5,69.9,-82.6,69.7,-81.4,67.1,-85.8,66.6,-90.7,63.6,-94.7,58.9,-92.3,57.1,-82.3,55.1,-79.9,51.2,-78.6,52.6,-79.8,54.7,-76.5,56.5,-78.5,58.8,-77.3,59.9,-78.1,62.3,-73.8,62.4,-69.6,61.1,-67.6,58.2,-64.6,60.3,-61.8,56.3,-57.3,54.6,-55.7,52.1,-60.0,50.2,-66.4,50.2,-71.1,46.8,-65.1,49.2,-64.5,46.2,-60.5,47.0,-59.8,45.9,-65.4,43.5,-66.2,44.5,-64.4,45.3,-67.1,45.1,-69.2,47.4,-71.5,45.0,-82.4,41.7,-82.6,45.3,-88.4,48.3,-122.8,49.0],
+ &[-84.0,62.5,-81.9,62.9,-84.0,62.5],
+ &[-79.8,72.8,-80.8,73.7,-76.3,72.8,-79.8,72.8],
+ &[-93.6,75.0,-96.8,74.9,-93.6,75.0],
+ &[-93.8,77.5,-96.4,77.8,-93.8,77.5],
+ &[-96.8,78.8,-95.6,78.4,-98.6,78.9,-96.8,78.8],
+ &[-88.2,74.4,-97.1,76.8,-79.8,74.9,-88.2,74.4],
+ &[-111.3,78.2,-109.9,78.0,-113.5,77.7,-111.3,78.2],
+ &[-111.0,78.8,-109.7,78.6,-112.5,78.4,-111.0,78.8],
+ &[-55.6,51.3,-56.8,49.8,-53.5,49.2,-53.1,46.7,-59.3,47.6,-55.6,51.3],
+ &[-83.9,65.1,-80.1,63.7,-87.2,63.5,-85.9,65.7,-83.9,65.1],
+ &[-78.8,72.4,-68.8,70.5,-67.0,69.2,-68.8,68.7,-61.9,66.9,-63.9,65.0,-68.0,66.3,-64.7,63.4,-68.8,63.7,-66.2,61.9,-68.9,62.3,-78.6,64.6,-74.0,65.5,-73.3,68.1,-79.0,70.2,-88.7,70.4,-90.2,72.2,-85.8,73.8,-85.8,72.5,-82.3,73.8,-78.8,72.4],
+ &[-94.5,74.1,-90.5,73.9,-95.4,72.1,-96.0,73.4,-94.5,74.1],
+ &[-122.9,76.1,-116.2,77.6,-122.9,76.1],
+ &[-132.7,54.0,-131.2,52.2,-132.7,54.0],
+ &[-105.5,79.3,-99.7,77.9,-105.5,79.3],
+ &[-123.5,48.5,-128.4,50.8,-123.5,48.5],
+ &[-121.5,74.4,-115.5,73.5,-123.1,70.9,-125.9,71.9,-123.9,73.7,-124.9,74.3,-121.5,74.4],
+ &[-107.8,75.8,-105.7,75.5,-117.7,75.2,-115.4,76.5,-107.8,75.8],
+ &[-106.5,73.1,-101.1,69.6,-113.3,68.5,-117.3,70.0,-112.4,70.4,-119.4,71.6,-115.2,73.3,-108.2,71.7,-108.4,73.1,-106.5,73.1],
+ &[-100.4,72.7,-101.5,73.4,-97.4,73.8,-96.5,72.6,-98.4,71.3,-102.5,72.5,-100.4,72.7],
+ &[-106.6,73.6,-104.5,73.4,-106.6,73.6],
+ &[-98.5,76.7,-98.2,75.0,-102.5,75.6,-98.5,76.7],
+ &[-96.0,80.6,-92.4,81.3,-85.8,79.3,-92.9,78.3,-96.0,80.6],
+ &[-91.6,81.9,-61.8,82.6,-76.9,79.3,-75.4,78.5,-80.6,76.2,-89.5,76.5,-88.3,77.9,-85.0,77.5,-88.0,78.4,-85.1,79.3,-86.9,80.3,-81.8,80.5,-91.6,81.9],
+ &[-75.2,67.4,-77.2,67.6,-75.2,67.4],
+ &[-96.3,69.5,-99.8,69.4,-96.3,69.5],
+ &[-64.5,49.9,-61.8,49.1,-64.5,49.9],
+ &[-64.0,47.0,-62.0,46.4,-64.0,47.0],
+ ],
+ },
+ CountryOutline {
+ name: "Central African Rep.",
+ iso: "CF",
+ rings: &[
+ &[27.4,5.2,22.4,4.0,19.5,5.0,16.0,2.3,14.5,5.5,15.3,7.4,22.9,11.1,27.4,5.2],
+ ],
+ },
+ CountryOutline {
+ name: "Chad",
+ iso: "TD",
+ rings: &[
+ &[23.8,19.6,23.9,15.6,21.9,12.6,22.9,11.1,15.3,7.4,13.5,14.4,15.9,20.4,14.9,22.9,23.8,19.6],
+ ],
+ },
+ CountryOutline {
+ name: "Chile",
+ iso: "CL",
+ rings: &[
+ &[-68.6,-52.6,-68.6,-54.9,-67.0,-54.9,-68.1,-55.6,-74.7,-52.8,-71.1,-54.1,-68.6,-52.6],
+ &[-69.6,-17.6,-67.0,-23.0,-70.5,-31.4,-69.8,-34.2,-72.1,-42.3,-71.2,-44.8,-73.4,-49.3,-71.9,-52.0,-68.6,-52.3,-71.4,-53.9,-74.9,-52.3,-75.6,-48.7,-74.1,-46.9,-75.6,-46.6,-72.7,-42.4,-74.3,-43.2,-69.6,-17.6],
+ ],
+ },
+ CountryOutline {
+ name: "China",
+ iso: "CN",
+ rings: &[
+ &[109.5,18.2,108.6,19.4,110.8,20.1,109.5,18.2],
+ &[80.3,42.3,80.0,44.9,87.8,49.3,91.0,46.9,90.9,45.3,96.3,42.7,109.2,42.5,111.9,45.1,119.7,46.7,115.5,48.1,122.2,53.4,125.9,52.8,131.0,47.8,135.0,48.5,133.1,45.1,131.0,45.0,130.6,42.4,121.1,38.9,121.6,40.9,117.5,38.7,122.4,37.5,119.2,34.9,121.9,31.7,121.7,28.2,118.7,24.5,110.4,20.3,105.3,23.4,101.7,22.3,101.8,21.2,99.2,22.1,97.6,23.9,98.7,27.5,96.1,29.5,88.8,27.3,78.7,31.5,78.9,34.3,73.7,39.4,80.3,42.3],
+ ],
+ },
+ CountryOutline {
+ name: "Colombia",
+ iso: "CO",
+ rings: &[
+ &[-66.9,1.3,-69.8,1.7,-69.9,-4.3,-70.0,-2.7,-77.4,0.4,-79.0,1.7,-77.1,3.8,-77.5,8.5,-71.4,12.4,-73.3,9.2,-72.0,7.0,-67.3,6.1,-66.9,1.3],
+ ],
+ },
+ CountryOutline {
+ name: "Congo",
+ iso: "CG",
+ rings: &[
+ &[18.5,3.5,16.0,-3.5,11.9,-5.0,11.5,-2.8,14.4,-1.3,13.1,2.3,15.9,1.7,18.5,3.5],
+ ],
+ },
+ CountryOutline {
+ name: "Costa Rica",
+ iso: "CR",
+ rings: &[
+ &[-82.5,9.6,-83.0,8.2,-85.9,10.9,-82.5,9.6],
+ ],
+ },
+ CountryOutline {
+ name: "Côte d'Ivoire",
+ iso: "CI",
+ rings: &[
+ &[-8.0,10.2,-2.8,9.6,-2.9,5.0,-7.7,4.4,-8.0,10.2],
+ ],
+ },
+ CountryOutline {
+ name: "Croatia",
+ iso: "HR",
+ rings: &[
+ &[16.6,46.5,19.4,45.2,15.8,44.8,18.5,42.5,13.7,45.1,16.6,46.5],
+ ],
+ },
+ CountryOutline {
+ name: "Cuba",
+ iso: "CU",
+ rings: &[
+ &[-82.3,23.2,-74.2,20.3,-77.8,19.9,-81.8,22.6,-85.0,21.9,-82.3,23.2],
+ ],
+ },
+ CountryOutline {
+ name: "Cyprus",
+ iso: "CY",
+ rings: &[
+ &[32.7,35.1,34.0,35.0,32.7,35.1],
+ ],
+ },
+ CountryOutline {
+ name: "Czechia",
+ iso: "CZ",
+ rings: &[
+ &[15.0,51.1,18.9,49.5,12.5,49.5,15.0,51.1],
+ ],
+ },
+ CountryOutline {
+ name: "Dem. Rep. Congo",
+ iso: "CD",
+ rings: &[
+ &[29.3,-4.5,30.7,-8.3,28.7,-8.5,28.4,-11.8,29.7,-13.3,22.2,-11.1,21.7,-7.3,17.5,-8.1,16.3,-5.9,12.2,-5.8,16.0,-3.5,19.5,5.0,29.7,4.6,31.2,2.2,29.3,-4.5],
+ ],
+ },
+ CountryOutline {
+ name: "Denmark",
+ iso: "DK",
+ rings: &[
+ &[9.9,55.0,8.1,56.5,10.6,57.7,9.9,55.0],
+ &[12.4,56.1,12.1,54.8,11.0,55.4,12.4,56.1],
+ ],
+ },
+ CountryOutline {
+ name: "Djibouti",
+ iso: "DJ",
+ rings: &[
+ &[42.4,12.5,42.8,10.9,42.4,12.5],
+ ],
+ },
+ CountryOutline {
+ name: "Dominican Rep.",
+ iso: "DO",
+ rings: &[
+ &[-71.7,18.0,-71.6,19.9,-68.3,18.6,-71.7,18.0],
+ ],
+ },
+ CountryOutline {
+ name: "Ecuador",
+ iso: "EC",
+ rings: &[
+ &[-75.4,-0.2,-78.6,-4.5,-80.4,-4.4,-80.1,0.8,-75.4,-0.2],
+ ],
+ },
+ CountryOutline {
+ name: "Egypt",
+ iso: "EG",
+ rings: &[
+ &[36.9,22.0,25.0,22.0,25.2,31.6,34.3,31.2,34.2,27.8,32.3,29.8,36.9,22.0],
+ ],
+ },
+ CountryOutline {
+ name: "El Salvador",
+ iso: "SV",
+ rings: &[
+ &[-89.4,14.4,-87.9,13.1,-90.1,13.7,-89.4,14.4],
+ ],
+ },
+ CountryOutline {
+ name: "Eq. Guinea",
+ iso: "GQ",
+ rings: &[
+ &[9.6,2.3,11.3,1.1,9.5,1.0,9.6,2.3],
+ ],
+ },
+ CountryOutline {
+ name: "Eritrea",
+ iso: "ER",
+ rings: &[
+ &[36.4,14.4,38.4,18.0,43.1,12.7,36.4,14.4],
+ ],
+ },
+ CountryOutline {
+ name: "Estonia",
+ iso: "EE",
+ rings: &[
+ &[28.0,59.5,27.3,57.5,23.3,59.2,28.0,59.5],
+ ],
+ },
+ CountryOutline {
+ name: "eSwatini",
+ iso: "SZ",
+ rings: &[
+ &[32.1,-26.7,31.0,-25.7,32.1,-26.7],
+ ],
+ },
+ CountryOutline {
+ name: "Ethiopia",
+ iso: "ET",
+ rings: &[
+ &[47.8,8.0,45.0,5.0,39.6,3.4,36.2,4.4,33.0,7.8,37.9,15.0,41.6,13.5,43.7,9.2,47.8,8.0],
+ ],
+ },
+ CountryOutline {
+ name: "Falkland Is.",
+ iso: "FK",
+ rings: &[
+ &[-61.2,-51.8,-57.7,-51.5,-61.2,-51.8],
+ ],
+ },
+ CountryOutline {
+ name: "Finland",
+ iso: "FI",
+ rings: &[
+ &[28.6,69.1,31.1,62.4,28.1,60.5,21.3,60.7,21.5,63.2,25.4,65.1,20.6,69.1,24.7,68.6,27.7,70.2,28.6,69.1],
+ ],
+ },
+ CountryOutline {
+ name: "Fr. S. Antarctic Lands",
+ iso: "TF",
+ rings: &[
+ &[68.9,-48.6,70.6,-49.3,68.7,-49.8,68.9,-48.6],
+ ],
+ },
+ CountryOutline {
+ name: "France",
+ iso: "FR",
+ rings: &[
+ &[-51.7,4.2,-52.9,2.1,-54.5,2.3,-54.0,5.8,-51.7,4.2],
+ &[6.2,49.5,8.1,49.0,6.0,46.7,7.4,43.7,1.8,42.3,-1.9,43.4,-1.2,46.0,-4.6,48.7,-1.6,48.6,-1.9,49.8,2.5,51.1,6.2,49.5],
+ &[8.7,42.6,9.2,41.4,8.7,42.6],
+ ],
+ },
+ CountryOutline {
+ name: "Gabon",
+ iso: "GA",
+ rings: &[
+ &[11.3,2.3,14.3,1.2,14.4,-1.3,11.1,-4.0,8.8,-1.1,11.3,2.3],
+ ],
+ },
+ CountryOutline {
+ name: "Gambia",
+ iso: "GM",
+ rings: &[
+ &[-16.7,13.6,-13.8,13.5,-16.7,13.6],
+ ],
+ },
+ CountryOutline {
+ name: "Georgia",
+ iso: "GE",
+ rings: &[
+ &[40.0,43.4,46.6,41.2,41.6,41.5,40.0,43.4],
+ ],
+ },
+ CountryOutline {
+ name: "Germany",
+ iso: "DE",
+ rings: &[
+ &[14.1,53.8,15.0,51.1,12.2,50.3,12.9,47.5,7.5,47.6,8.1,49.0,6.0,50.1,7.1,53.7,9.9,55.0,14.1,53.8],
+ ],
+ },
+ CountryOutline {
+ name: "Ghana",
+ iso: "GH",
+ rings: &[
+ &[0.0,11.0,1.1,5.9,-2.9,5.0,-2.9,11.0,0.0,11.0],
+ ],
+ },
+ CountryOutline {
+ name: "Greece",
+ iso: "GR",
+ rings: &[
+ &[26.3,35.3,23.5,35.3,26.3,35.3],
+ &[23.0,41.3,26.6,41.6,22.6,40.3,24.0,37.7,22.5,36.4,20.2,39.6,23.0,41.3],
+ ],
+ },
+ CountryOutline {
+ name: "Greenland",
+ iso: "GL",
+ rings: &[
+ &[-46.8,82.6,-27.1,83.5,-20.8,82.7,-31.9,82.2,-12.2,81.3,-20.0,80.2,-17.7,80.1,-19.7,78.8,-18.5,77.0,-21.7,76.6,-19.4,74.3,-24.8,72.3,-21.8,70.7,-25.5,71.4,-26.4,70.2,-22.3,70.1,-39.8,65.5,-43.4,60.1,-48.3,60.9,-51.6,63.6,-54.0,67.2,-50.9,69.9,-54.7,69.6,-54.4,70.8,-51.4,70.6,-55.8,71.7,-54.7,72.6,-58.6,75.5,-68.5,76.1,-71.4,77.0,-66.8,77.4,-73.3,78.0,-65.7,79.4,-68.0,80.1,-62.7,81.8,-44.5,81.7,-46.8,82.6],
+ ],
+ },
+ CountryOutline {
+ name: "Guatemala",
+ iso: "GT",
+ rings: &[
+ &[-92.2,14.5,-90.5,16.1,-91.0,17.8,-89.1,17.8,-88.2,15.7,-89.4,14.4,-92.2,14.5],
+ ],
+ },
+ CountryOutline {
+ name: "Guinea",
+ iso: "GN",
+ rings: &[
+ &[-13.7,12.6,-9.1,12.3,-8.3,7.7,-11.1,10.0,-13.2,8.9,-15.1,11.0,-13.7,12.6],
+ ],
+ },
+ CountryOutline {
+ name: "Guinea-Bissau",
+ iso: "GW",
+ rings: &[
+ &[-16.7,12.4,-13.7,11.8,-15.1,11.0,-16.7,12.4],
+ ],
+ },
+ CountryOutline {
+ name: "Guyana",
+ iso: "GY",
+ rings: &[
+ &[-56.5,1.9,-59.6,1.8,-61.4,6.0,-59.8,8.4,-57.1,6.0,-58.0,4.1,-56.5,1.9],
+ ],
+ },
+ CountryOutline {
+ name: "Haiti",
+ iso: "HT",
+ rings: &[
+ &[-71.7,19.7,-71.7,18.0,-74.5,18.3,-71.7,19.7],
+ ],
+ },
+ CountryOutline {
+ name: "Honduras",
+ iso: "HN",
+ rings: &[
+ &[-83.1,15.0,-87.3,13.0,-89.4,14.4,-87.9,15.9,-83.1,15.0],
+ ],
+ },
+ CountryOutline {
+ name: "Hungary",
+ iso: "HU",
+ rings: &[
+ &[22.1,48.4,21.0,46.3,16.2,46.9,17.0,48.1,22.1,48.4],
+ ],
+ },
+ CountryOutline {
+ name: "Iceland",
+ iso: "IS",
+ rings: &[
+ &[-14.5,66.5,-13.6,65.1,-18.7,63.5,-22.8,64.0,-21.8,64.4,-24.0,64.9,-22.2,65.4,-24.3,65.6,-14.5,66.5],
+ ],
+ },
+ CountryOutline {
+ name: "India",
+ iso: "IN",
+ rings: &[
+ &[97.3,28.3,92.7,22.0,91.2,23.5,92.4,25.0,88.6,26.4,88.9,21.7,80.3,15.9,79.9,10.4,77.5,8.0,72.6,21.4,70.5,20.9,68.2,23.7,71.0,24.4,69.5,26.9,75.3,32.3,73.7,34.3,77.8,35.5,78.7,31.5,81.1,30.2,80.1,28.8,83.3,27.4,88.1,26.4,88.7,28.1,92.0,26.8,96.1,29.5,97.3,28.3],
+ ],
+ },
+ CountryOutline {
+ name: "Indonesia",
+ iso: "ID",
+ rings: &[
+ &[141.0,-2.6,141.0,-9.1,137.6,-8.4,137.9,-5.4,133.0,-4.1,132.0,-2.8,133.7,-2.2,130.5,-0.9,134.0,-0.8,135.5,-3.4,137.4,-1.7,141.0,-2.6],
+ &[125.0,-8.9,123.5,-10.2,125.0,-8.9],
+ &[117.9,4.1,119.0,0.9,116.1,-4.0,110.2,-2.9,109.7,2.0,110.5,0.8,113.8,1.2,115.9,4.3,117.9,4.1],
+ &[129.4,-2.8,130.8,-3.9,127.9,-3.4,129.4,-2.8],
+ &[127.9,2.2,128.1,-0.9,127.9,2.2],
+ &[122.9,0.9,125.2,1.4,120.0,-0.5,123.3,-0.6,121.5,-1.9,123.2,-5.3,121.0,-2.6,119.8,-5.7,118.8,-2.8,119.8,0.2,122.9,0.9],
+ &[120.3,-10.3,119.0,-9.6,120.3,-10.3],
+ &[121.3,-8.5,122.9,-8.1,119.9,-8.8,121.3,-8.5],
+ &[118.3,-8.4,116.7,-9.0,118.3,-8.4],
+ &[108.5,-6.4,115.7,-8.4,105.4,-6.9,108.5,-6.4],
+ &[104.4,-1.1,106.1,-3.1,105.8,-5.9,102.6,-4.2,95.3,5.5,97.5,5.2,104.4,-1.1],
+ ],
+ },
+ CountryOutline {
+ name: "Iran",
+ iso: "IR",
+ rings: &[
+ &[48.6,29.9,45.4,34.0,46.1,35.7,44.1,39.4,48.1,39.6,50.8,36.9,56.6,38.1,61.1,36.5,60.9,29.8,63.3,26.8,61.5,25.1,57.4,25.7,48.6,29.9],
+ ],
+ },
+ CountryOutline {
+ name: "Iraq",
+ iso: "IQ",
+ rings: &[
+ &[39.2,32.2,41.3,36.4,44.8,37.2,48.6,29.9,44.7,29.2,39.2,32.2],
+ ],
+ },
+ CountryOutline {
+ name: "Ireland",
+ iso: "IE",
+ rings: &[
+ &[-6.2,53.9,-6.8,52.3,-10.0,51.8,-7.6,55.1,-6.2,53.9],
+ ],
+ },
+ CountryOutline {
+ name: "Israel",
+ iso: "IL",
+ rings: &[
+ &[35.7,32.7,34.9,29.5,34.3,31.2,35.7,32.7],
+ ],
+ },
+ CountryOutline {
+ name: "Italy",
+ iso: "IT",
+ rings: &[
+ &[10.4,46.9,13.8,46.5,12.6,44.1,18.3,39.8,16.9,40.4,15.7,37.9,15.4,40.0,10.2,43.9,7.4,43.7,6.8,46.0,10.4,46.9],
+ &[14.8,38.1,15.1,36.6,12.4,37.6,14.8,38.1],
+ &[8.7,40.9,9.8,40.5,8.8,38.9,8.7,40.9],
+ ],
+ },
+ CountryOutline {
+ name: "Jamaica",
+ iso: "JM",
+ rings: &[
+ &[-77.6,18.5,-76.2,17.9,-77.6,18.5],
+ ],
+ },
+ CountryOutline {
+ name: "Japan",
+ iso: "JP",
+ rings: &[
+ &[141.9,39.2,140.3,35.1,135.8,33.5,135.1,34.6,131.0,33.9,132.0,33.1,130.2,31.4,129.4,33.3,139.4,38.2,140.3,41.2,141.9,39.2],
+ &[144.6,44.0,145.5,43.3,140.0,41.6,142.0,45.6,144.6,44.0],
+ &[132.4,33.5,134.8,33.8,132.4,33.5],
+ ],
+ },
+ CountryOutline {
+ name: "Jordan",
+ iso: "JO",
+ rings: &[
+ &[35.5,32.4,38.8,33.4,39.2,32.2,37.0,31.5,38.0,30.5,36.1,29.2,34.9,29.5,35.5,32.4],
+ ],
+ },
+ CountryOutline {
+ name: "Kazakhstan",
+ iso: "KZ",
+ rings: &[
+ &[87.4,49.2,80.0,44.9,80.3,42.3,74.2,43.3,68.6,40.7,64.9,43.7,62.0,43.5,58.5,45.6,55.9,45.0,56.0,41.3,52.5,41.8,50.3,44.6,53.0,45.3,53.0,46.9,49.1,46.4,46.5,48.4,50.8,51.7,61.3,50.8,60.0,52.0,61.4,54.0,69.1,55.4,73.4,53.5,76.9,54.5,80.0,50.9,87.4,49.2],
+ ],
+ },
+ CountryOutline {
+ name: "Kenya",
+ iso: "KE",
+ rings: &[
+ &[39.2,-4.7,33.9,-0.9,35.3,5.5,38.1,3.6,41.9,3.9,41.6,-1.7,39.2,-4.7],
+ ],
+ },
+ CountryOutline {
+ name: "Kosovo",
+ iso: "XK",
+ rings: &[
+ &[20.6,41.9,20.6,43.2,21.8,42.7,20.6,41.9],
+ ],
+ },
+ CountryOutline {
+ name: "Kuwait",
+ iso: "KW",
+ rings: &[
+ &[48.0,30.0,48.4,28.6,46.6,29.1,48.0,30.0],
+ ],
+ },
+ CountryOutline {
+ name: "Kyrgyzstan",
+ iso: "KG",
+ rings: &[
+ &[71.0,42.3,74.2,43.3,80.3,42.3,73.7,39.4,69.5,39.5,73.1,40.9,71.0,42.3],
+ ],
+ },
+ CountryOutline {
+ name: "Laos",
+ iso: "LA",
+ rings: &[
+ &[107.4,14.2,105.2,14.3,104.0,18.2,101.1,17.5,100.1,20.4,101.7,22.3,104.4,20.8,103.9,19.3,107.4,14.2],
+ ],
+ },
+ CountryOutline {
+ name: "Latvia",
+ iso: "LV",
+ rings: &[
+ &[27.3,57.5,28.2,56.2,26.5,55.6,21.1,56.0,22.5,57.8,27.3,57.5],
+ ],
+ },
+ CountryOutline {
+ name: "Lebanon",
+ iso: "LB",
+ rings: &[
+ &[35.8,33.3,36.4,34.6,35.8,33.3],
+ ],
+ },
+ CountryOutline {
+ name: "Lesotho",
+ iso: "LS",
+ rings: &[
+ &[29.0,-29.0,28.1,-30.5,27.0,-29.9,29.0,-29.0],
+ ],
+ },
+ CountryOutline {
+ name: "Liberia",
+ iso: "LR",
+ rings: &[
+ &[-8.4,7.7,-7.7,4.4,-11.4,6.8,-10.2,8.4,-8.4,7.7],
+ ],
+ },
+ CountryOutline {
+ name: "Libya",
+ iso: "LY",
+ rings: &[
+ &[25.0,22.0,23.8,19.6,10.3,24.4,10.0,31.4,11.5,33.1,19.1,30.3,20.9,32.7,24.9,31.9,25.0,22.0],
+ ],
+ },
+ CountryOutline {
+ name: "Lithuania",
+ iso: "LT",
+ rings: &[
+ &[26.5,55.6,23.5,53.9,21.1,56.0,26.5,55.6],
+ ],
+ },
+ CountryOutline {
+ name: "Madagascar",
+ iso: "MG",
+ rings: &[
+ &[49.5,-12.5,50.4,-15.7,47.1,-24.9,45.4,-25.6,43.3,-22.8,44.0,-17.4,49.5,-12.5],
+ ],
+ },
+ CountryOutline {
+ name: "Malawi",
+ iso: "MW",
+ rings: &[
+ &[32.8,-9.2,35.7,-14.6,35.0,-16.8,32.7,-13.7,32.8,-9.2],
+ ],
+ },
+ CountryOutline {
+ name: "Malaysia",
+ iso: "MY",
+ rings: &[
+ &[100.1,6.5,103.0,5.5,104.2,1.3,101.4,2.8,100.1,6.5],
+ &[117.9,4.1,115.9,4.3,114.6,1.4,109.8,1.3,115.3,4.3,116.7,6.9,119.2,5.4,117.9,4.1],
+ ],
+ },
+ CountryOutline {
+ name: "Mali",
+ iso: "ML",
+ rings: &[
+ &[-11.5,12.4,-11.7,15.4,-5.5,15.5,-6.5,25.0,4.3,19.2,3.6,15.6,-4.0,13.5,-5.4,10.4,-11.5,12.4],
+ ],
+ },
+ CountryOutline {
+ name: "Mauritania",
+ iso: "MR",
+ rings: &[
+ &[-17.1,21.0,-12.9,21.3,-12.0,25.9,-8.7,25.9,-8.7,27.4,-4.9,25.0,-6.5,25.0,-5.5,15.5,-12.2,14.6,-14.6,16.6,-16.5,16.1,-17.1,21.0],
+ ],
+ },
+ CountryOutline {
+ name: "Mexico",
+ iso: "MX",
+ rings: &[
+ &[-117.1,32.5,-106.5,31.8,-103.9,29.3,-101.7,29.8,-97.1,25.9,-97.9,22.4,-95.9,18.8,-91.4,18.9,-90.3,21.0,-87.1,21.5,-87.8,18.3,-91.0,17.8,-90.5,16.1,-92.2,14.5,-103.5,18.3,-113.1,31.2,-114.9,31.4,-109.9,22.8,-115.1,27.7,-114.2,28.6,-117.1,32.5],
+ ],
+ },
+ CountryOutline {
+ name: "Moldova",
+ iso: "MD",
+ rings: &[
+ &[26.6,48.2,30.0,46.4,28.2,45.5,26.6,48.2],
+ ],
+ },
+ CountryOutline {
+ name: "Mongolia",
+ iso: "MN",
+ rings: &[
+ &[87.8,49.3,92.2,50.8,97.3,49.7,98.9,52.0,108.5,49.3,116.7,49.9,115.7,47.7,119.8,47.0,105.0,41.6,96.3,42.7,90.9,45.3,91.0,46.9,87.8,49.3],
+ ],
+ },
+ CountryOutline {
+ name: "Montenegro",
+ iso: "ME",
+ rings: &[
+ &[20.1,42.6,18.5,42.5,20.1,42.6],
+ ],
+ },
+ CountryOutline {
+ name: "Morocco",
+ iso: "MA",
+ rings: &[
+ &[-2.2,35.2,-1.3,32.3,-8.7,28.8,-8.8,27.1,-11.4,26.9,-14.8,21.5,-17.0,21.4,-14.4,26.3,-9.6,29.9,-8.7,33.2,-5.9,35.8,-2.2,35.2],
+ ],
+ },
+ CountryOutline {
+ name: "Mozambique",
+ iso: "MZ",
+ rings: &[
+ &[34.6,-11.5,40.3,-10.3,40.8,-14.7,34.8,-19.8,35.5,-24.1,32.1,-26.7,31.2,-22.3,32.8,-16.7,30.2,-14.8,33.2,-14.0,35.0,-16.8,34.6,-11.5],
+ ],
+ },
+ CountryOutline {
+ name: "Myanmar",
+ iso: "MM",
+ rings: &[
+ &[100.1,20.4,97.4,18.4,99.6,11.9,98.6,9.9,97.2,16.9,94.2,16.0,92.3,21.5,97.3,28.3,98.7,27.5,97.6,23.9,101.2,21.8,100.1,20.4],
+ ],
+ },
+ CountryOutline {
+ name: "N. Cyprus",
+ iso: "-99",
+ rings: &[
+ &[32.7,35.1,34.6,35.7,32.7,35.1],
+ ],
+ },
+ CountryOutline {
+ name: "Namibia",
+ iso: "NA",
+ rings: &[
+ &[19.9,-24.8,19.9,-28.5,16.3,-28.6,11.7,-17.3,25.1,-17.6,20.9,-18.3,19.9,-24.8],
+ ],
+ },
+ CountryOutline {
+ name: "Nepal",
+ iso: "NP",
+ rings: &[
+ &[88.1,27.9,87.2,26.4,80.1,28.8,81.5,30.4,88.1,27.9],
+ ],
+ },
+ CountryOutline {
+ name: "Netherlands",
+ iso: "NL",
+ rings: &[
+ &[6.9,53.5,6.2,50.8,3.3,51.3,6.9,53.5],
+ ],
+ },
+ CountryOutline {
+ name: "New Caledonia",
+ iso: "NC",
+ rings: &[
+ &[165.8,-21.1,167.1,-22.2,164.0,-20.1,165.8,-21.1],
+ ],
+ },
+ CountryOutline {
+ name: "New Zealand",
+ iso: "NZ",
+ rings: &[
+ &[176.9,-40.1,174.7,-41.3,174.7,-37.4,172.6,-34.5,176.0,-37.6,178.5,-37.7,176.9,-40.1],
+ &[169.7,-43.6,172.8,-40.5,174.2,-41.3,170.6,-45.9,166.7,-46.2,169.7,-43.6],
+ ],
+ },
+ CountryOutline {
+ name: "Nicaragua",
+ iso: "NI",
+ rings: &[
+ &[-83.7,10.9,-87.7,12.9,-83.1,15.0,-83.7,10.9],
+ ],
+ },
+ CountryOutline {
+ name: "Niger",
+ iso: "NE",
+ rings: &[
+ &[14.9,22.9,15.9,20.4,13.5,14.4,14.2,12.5,5.4,13.9,3.6,11.7,1.0,12.9,0.4,14.9,3.6,15.6,4.3,19.2,12.0,23.5,14.9,22.9],
+ ],
+ },
+ CountryOutline {
+ name: "Nigeria",
+ iso: "NG",
+ rings: &[
+ &[2.7,6.3,4.4,13.7,13.1,13.6,14.6,12.1,11.7,7.0,8.5,4.8,5.9,4.3,2.7,6.3],
+ ],
+ },
+ CountryOutline {
+ name: "North Korea",
+ iso: "KP",
+ rings: &[
+ &[130.6,42.4,127.5,39.8,128.2,38.4,124.7,38.1,125.1,40.6,130.6,42.4],
+ ],
+ },
+ CountryOutline {
+ name: "North Macedonia",
+ iso: "MK",
+ rings: &[
+ &[22.4,42.3,23.0,41.3,20.6,41.1,22.4,42.3],
+ ],
+ },
+ CountryOutline {
+ name: "Norway",
+ iso: "NO",
+ rings: &[
+ &[15.1,79.7,21.5,79.0,15.9,76.8,10.4,79.7,15.1,79.7],
+ &[31.1,69.6,18.0,68.6,12.6,64.1,11.0,58.9,5.7,58.6,5.0,62.0,19.2,69.8,28.2,71.2,31.1,69.6],
+ &[27.4,80.1,17.4,80.3,27.4,80.1],
+ &[24.7,77.9,20.7,77.7,24.7,77.9],
+ ],
+ },
+ CountryOutline {
+ name: "Oman",
+ iso: "OM",
+ rings: &[
+ &[55.2,22.7,56.4,24.9,59.8,22.3,57.7,18.9,53.1,16.7,52.0,19.0,55.0,20.0,55.2,22.7],
+ ],
+ },
+ CountryOutline {
+ name: "Pakistan",
+ iso: "PK",
+ rings: &[
+ &[77.8,35.5,73.7,34.3,75.3,32.3,69.5,26.9,71.0,24.4,61.5,25.1,63.3,26.8,60.9,29.8,66.3,29.9,71.8,36.5,75.2,37.1,77.8,35.5],
+ ],
+ },
+ CountryOutline {
+ name: "Panama",
+ iso: "PA",
+ rings: &[
+ &[-77.4,8.7,-77.9,7.2,-79.1,9.0,-80.9,7.2,-82.9,9.5,-77.4,8.7],
+ ],
+ },
+ CountryOutline {
+ name: "Papua New Guinea",
+ iso: "PG",
+ rings: &[
+ &[141.0,-2.6,147.6,-6.1,147.2,-7.4,150.7,-10.6,144.7,-7.6,141.0,-9.1,141.0,-2.6],
+ &[152.6,-3.7,152.8,-4.8,150.7,-2.7,152.6,-3.7],
+ &[151.3,-5.8,148.3,-5.7,152.1,-4.1,151.3,-5.8],
+ &[154.8,-5.3,155.9,-6.8,154.8,-5.3],
+ ],
+ },
+ CountryOutline {
+ name: "Paraguay",
+ iso: "PY",
+ rings: &[
+ &[-58.2,-20.2,-57.9,-22.1,-54.3,-24.0,-55.7,-27.4,-58.6,-27.1,-57.8,-25.2,-62.7,-22.2,-61.8,-19.6,-58.2,-20.2],
+ ],
+ },
+ CountryOutline {
+ name: "Peru",
+ iso: "PE",
+ rings: &[
+ &[-69.9,-4.3,-72.9,-5.3,-74.0,-7.5,-68.7,-12.6,-70.4,-18.3,-76.0,-14.6,-81.4,-4.7,-80.3,-3.4,-78.6,-4.5,-75.1,-0.1,-73.1,-2.3,-70.0,-2.7,-69.9,-4.3],
+ ],
+ },
+ CountryOutline {
+ name: "Philippines",
+ iso: "PH",
+ rings: &[
+ &[122.6,10.0,124.1,11.2,123.0,9.0,122.6,10.0],
+ &[126.4,8.4,125.4,5.6,123.6,7.8,121.9,7.2,125.4,9.8,126.4,8.4],
+ &[118.5,9.3,117.2,8.4,119.5,11.4,118.5,9.3],
+ &[122.3,18.2,121.7,14.3,124.1,12.5,119.9,15.4,120.7,18.5,122.3,18.2],
+ &[125.5,12.2,124.8,10.1,124.3,12.6,125.5,12.2],
+ ],
+ },
+ CountryOutline {
+ name: "Poland",
+ iso: "PL",
+ rings: &[
+ &[23.5,53.9,24.0,50.7,22.8,49.0,16.2,50.4,14.1,53.0,17.6,54.9,23.5,53.9],
+ ],
+ },
+ CountryOutline {
+ name: "Portugal",
+ iso: "PT",
+ rings: &[
+ &[-9.0,41.9,-6.4,41.4,-7.9,36.8,-9.5,38.7,-9.0,41.9],
+ ],
+ },
+ CountryOutline {
+ name: "Puerto Rico",
+ iso: "PR",
+ rings: &[
+ &[-66.3,18.5,-67.2,17.9,-66.3,18.5],
+ ],
+ },
+ CountryOutline {
+ name: "Qatar",
+ iso: "QA",
+ rings: &[
+ &[50.8,24.8,51.3,26.1,50.8,24.8],
+ ],
+ },
+ CountryOutline {
+ name: "Romania",
+ iso: "RO",
+ rings: &[
+ &[28.2,45.5,29.6,45.3,28.6,43.7,22.9,43.8,20.2,46.1,26.6,48.2,28.2,45.5],
+ ],
+ },
+ CountryOutline {
+ name: "Russia",
+ iso: "RU",
+ rings: &[
+ &[49.1,46.4,46.7,44.6,47.8,41.2,36.7,45.2,40.1,49.6,31.8,52.1,32.7,53.4,30.9,55.6,27.3,57.5,29.1,60.0,28.1,60.5,31.5,62.9,30.0,63.6,28.6,69.1,32.1,69.9,41.1,67.5,38.4,66.0,33.2,66.6,37.0,63.8,37.2,65.1,43.9,66.1,43.5,68.6,46.3,68.3,46.3,66.7,53.7,68.9,59.9,68.3,60.6,69.9,68.5,68.1,66.7,71.0,69.9,73.0,72.8,72.2,71.8,71.4,73.7,68.4,71.3,66.3,72.4,66.2,75.1,67.8,73.1,71.4,74.7,72.8,76.4,71.2,81.5,71.8,80.5,73.6,104.4,77.7,114.1,75.8,109.4,74.2,127.0,73.6,131.3,70.8,139.9,71.5,139.1,72.4,140.5,72.8,159.0,70.9,160.9,69.4,180.0,69.0,180.0,65.0,177.4,64.6,179.2,62.3,170.3,59.9,163.5,59.9,162.0,58.2,163.2,57.6,162.1,54.9,156.8,51.0,155.9,56.8,164.5,62.6,160.1,60.5,156.7,61.4,154.2,59.8,155.0,59.1,142.2,59.0,135.1,54.7,141.3,53.1,140.1,48.4,134.9,43.4,130.8,42.2,131.0,45.0,133.1,45.1,135.0,48.5,131.0,47.8,123.6,53.5,120.2,52.8,117.9,49.5,108.5,49.3,98.9,52.0,97.3,49.7,92.2,50.8,87.4,49.2,80.0,50.9,76.9,54.5,73.4,53.5,69.1,55.4,61.4,54.0,60.0,52.0,61.3,50.8,50.8,51.7,47.5,50.5,46.5,48.4,49.1,46.4],
+ &[93.8,81.0,100.2,79.8,97.8,78.8,91.2,80.3,93.8,81.0],
+ &[102.8,79.3,105.4,78.7,99.4,77.9,102.8,79.3],
+ &[138.8,76.1,145.1,75.6,137.0,75.3,138.8,76.1],
+ &[148.2,75.3,150.7,75.1,146.1,75.2,148.2,75.3],
+ &[139.9,73.4,143.6,73.2,139.9,73.4],
+ &[44.8,80.6,51.5,80.7,44.8,80.6],
+ &[22.7,54.3,19.7,54.4,22.7,54.3],
+ &[53.5,73.7,61.2,76.3,68.9,76.5,58.5,74.3,55.4,72.4,57.5,70.7,51.6,71.5,53.5,73.7],
+ &[142.9,53.7,144.7,49.0,143.2,49.3,143.5,46.1,142.1,46.0,141.7,53.3,142.9,53.7],
+ &[-174.9,67.2,-169.9,66.0,-173.0,64.3,-178.7,66.1,-180.0,65.0,-180.0,69.0,-174.9,67.2],
+ &[-178.7,70.9,-180.0,71.5,-177.6,71.3,-178.7,70.9],
+ &[33.4,46.0,36.5,45.5,33.9,44.4,32.5,45.3,33.4,46.0],
+ ],
+ },
+ CountryOutline {
+ name: "Rwanda",
+ iso: "RW",
+ rings: &[
+ &[30.4,-1.1,29.0,-2.8,30.4,-1.1],
+ ],
+ },
+ CountryOutline {
+ name: "S. Sudan",
+ iso: "SS",
+ rings: &[
+ &[30.8,3.5,23.9,8.6,25.8,10.4,31.4,9.8,33.2,12.2,33.0,7.8,35.3,5.5,30.8,3.5],
+ ],
+ },
+ CountryOutline {
+ name: "Saudi Arabia",
+ iso: "SA",
+ rings: &[
+ &[35.0,29.4,39.2,32.2,47.5,29.0,52.0,23.0,55.2,22.7,55.0,20.0,47.0,16.9,43.4,17.6,42.8,16.3,35.0,29.4],
+ ],
+ },
+ CountryOutline {
+ name: "Senegal",
+ iso: "SN",
+ rings: &[
+ &[-16.7,13.6,-17.6,14.7,-14.6,16.6,-11.5,12.4,-16.7,12.4,-13.8,13.5,-16.7,13.6],
+ ],
+ },
+ CountryOutline {
+ name: "Serbia",
+ iso: "RS",
+ rings: &[
+ &[18.8,45.9,22.7,44.6,22.5,42.5,19.2,43.5,18.8,45.9],
+ ],
+ },
+ CountryOutline {
+ name: "Sierra Leone",
+ iso: "SL",
+ rings: &[
+ &[-13.2,8.9,-11.1,10.0,-10.2,8.4,-11.4,6.8,-13.2,8.9],
+ ],
+ },
+ CountryOutline {
+ name: "Slovakia",
+ iso: "SK",
+ rings: &[
+ &[22.6,49.1,16.9,48.5,22.6,49.1],
+ ],
+ },
+ CountryOutline {
+ name: "Slovenia",
+ iso: "SI",
+ rings: &[
+ &[13.8,46.5,16.6,46.5,15.3,45.5,13.8,46.5],
+ ],
+ },
+ CountryOutline {
+ name: "Solomon Is.",
+ iso: "SB",
+ rings: &[
+ &[159.6,-8.0,158.2,-7.4,159.6,-8.0],
+ ],
+ },
+ CountryOutline {
+ name: "Somalia",
+ iso: "SO",
+ rings: &[
+ &[41.6,-1.7,41.0,2.8,45.0,5.0,48.9,9.5,48.9,11.4,51.1,12.0,48.6,5.3,41.6,-1.7],
+ ],
+ },
+ CountryOutline {
+ name: "Somaliland",
+ iso: "-99",
+ rings: &[
+ &[48.9,11.4,47.8,8.0,42.6,10.6,48.9,11.4],
+ ],
+ },
+ CountryOutline {
+ name: "South Africa",
+ iso: "ZA",
+ rings: &[
+ &[16.3,-28.6,19.9,-28.5,19.9,-24.8,21.6,-26.7,25.7,-25.5,29.4,-22.1,31.2,-22.3,31.9,-24.4,30.7,-26.7,32.8,-26.7,28.2,-32.8,20.1,-34.8,18.4,-34.1,16.3,-28.6],
+ ],
+ },
+ CountryOutline {
+ name: "South Korea",
+ iso: "KR",
+ rings: &[
+ &[126.2,37.7,128.3,38.6,129.1,35.1,126.5,34.4,126.2,37.7],
+ ],
+ },
+ CountryOutline {
+ name: "Spain",
+ iso: "ES",
+ rings: &[
+ &[-7.5,37.1,-6.4,41.4,-9.4,43.0,3.0,42.5,-2.1,36.7,-7.5,37.1],
+ ],
+ },
+ CountryOutline {
+ name: "Sri Lanka",
+ iso: "LK",
+ rings: &[
+ &[81.8,7.5,80.3,6.0,80.1,9.8,81.8,7.5],
+ ],
+ },
+ CountryOutline {
+ name: "Sudan",
+ iso: "SD",
+ rings: &[
+ &[24.6,8.2,21.9,12.6,25.0,22.0,36.9,22.0,38.4,18.0,34.0,8.7,32.7,12.2,31.4,9.8,25.1,10.3,24.6,8.2],
+ ],
+ },
+ CountryOutline {
+ name: "Suriname",
+ iso: "SR",
+ rings: &[
+ &[-54.5,2.3,-56.5,1.9,-57.6,3.3,-57.1,6.0,-54.0,5.8,-54.5,2.3],
+ ],
+ },
+ CountryOutline {
+ name: "Sweden",
+ iso: "SE",
+ rings: &[
+ &[11.0,58.9,12.6,61.3,11.9,63.1,16.8,68.0,20.6,69.1,23.5,67.9,23.9,66.0,17.8,62.7,17.1,61.3,18.8,60.1,15.9,56.1,12.9,55.4,11.0,58.9],
+ ],
+ },
+ CountryOutline {
+ name: "Switzerland",
+ iso: "CH",
+ rings: &[
+ &[9.6,47.5,10.4,46.5,6.0,46.3,9.6,47.5],
+ ],
+ },
+ CountryOutline {
+ name: "Syria",
+ iso: "SY",
+ rings: &[
+ &[35.7,32.7,36.7,36.8,42.3,37.2,41.0,34.4,35.7,32.7],
+ ],
+ },
+ CountryOutline {
+ name: "Taiwan",
+ iso: "TW",
+ rings: &[
+ &[121.8,24.4,120.7,22.0,120.1,23.6,121.8,24.4],
+ ],
+ },
+ CountryOutline {
+ name: "Tajikistan",
+ iso: "TJ",
+ rings: &[
+ &[67.8,37.1,67.7,39.6,70.7,41.0,69.5,39.5,73.7,39.4,75.0,37.4,71.8,36.7,70.8,38.5,67.8,37.1],
+ ],
+ },
+ CountryOutline {
+ name: "Tanzania",
+ iso: "TZ",
+ rings: &[
+ &[33.9,-0.9,39.2,-4.7,39.5,-10.9,34.6,-11.5,29.6,-6.5,30.4,-1.1,33.9,-0.9],
+ ],
+ },
+ CountryOutline {
+ name: "Thailand",
+ iso: "TH",
+ rings: &[
+ &[105.2,14.3,103.0,14.2,102.6,12.2,100.1,13.4,99.2,9.2,102.1,6.2,101.2,5.7,98.2,8.4,99.6,11.9,97.4,18.4,100.1,20.4,101.1,17.5,104.7,17.4,105.2,14.3],
+ ],
+ },
+ CountryOutline {
+ name: "Timor-Leste",
+ iso: "TL",
+ rings: &[
+ &[125.0,-8.9,127.3,-8.4,125.0,-8.9],
+ ],
+ },
+ CountryOutline {
+ name: "Togo",
+ iso: "TG",
+ rings: &[
+ &[0.9,11.0,1.1,5.9,0.9,11.0],
+ ],
+ },
+ CountryOutline {
+ name: "Tunisia",
+ iso: "TN",
+ rings: &[
+ &[9.5,30.3,7.5,34.1,9.5,37.3,11.0,37.1,10.1,34.3,11.5,33.1,9.5,30.3],
+ ],
+ },
+ CountryOutline {
+ name: "Turkey",
+ iso: "TR",
+ rings: &[
+ &[44.8,37.2,29.7,36.1,27.6,36.7,26.2,39.5,33.5,42.0,42.6,41.6,44.8,39.7,44.8,37.2],
+ &[26.1,41.8,29.0,41.3,26.4,40.2,26.1,41.8],
+ ],
+ },
+ CountryOutline {
+ name: "Turkmenistan",
+ iso: "TM",
+ rings: &[
+ &[52.5,41.8,57.1,41.3,58.6,42.8,66.5,37.4,62.2,35.3,57.3,38.0,53.9,37.2,52.7,40.0,54.7,41.0,52.5,41.8],
+ ],
+ },
+ CountryOutline {
+ name: "Uganda",
+ iso: "UG",
+ rings: &[
+ &[33.9,-0.9,29.6,-1.3,31.2,3.8,34.5,3.6,33.9,-0.9],
+ ],
+ },
+ CountryOutline {
+ name: "Ukraine",
+ iso: "UA",
+ rings: &[
+ &[31.8,52.1,40.1,49.6,39.7,47.9,35.0,45.7,31.7,46.7,28.7,45.3,30.0,46.4,28.7,48.1,22.1,48.4,23.5,51.6,31.8,52.1],
+ ],
+ },
+ CountryOutline {
+ name: "United Arab Emirates",
+ iso: "AE",
+ rings: &[
+ &[51.6,24.2,56.3,25.7,55.0,22.5,51.6,24.2],
+ ],
+ },
+ CountryOutline {
+ name: "United Kingdom",
+ iso: "GB",
+ rings: &[
+ &[-6.2,53.9,-7.6,55.1,-6.2,53.9],
+ &[-3.1,53.4,-6.1,56.8,-5.0,58.6,-2.0,57.7,-3.1,56.0,1.7,52.7,1.4,51.3,-5.8,50.2,-3.4,51.4,-5.3,52.0,-4.6,53.5,-3.1,53.4],
+ ],
+ },
+ CountryOutline {
+ name: "United States of America",
+ iso: "US",
+ rings: &[
+ &[-122.8,49.0,-88.4,48.3,-82.6,45.3,-82.7,41.7,-71.5,45.0,-69.2,47.4,-67.0,44.8,-70.1,43.7,-70.0,41.6,-75.5,39.5,-75.9,37.2,-76.3,39.2,-77.0,38.2,-75.7,35.6,-81.3,31.4,-80.4,25.2,-83.7,29.9,-86.4,30.4,-94.7,29.5,-97.5,25.8,-101.0,29.4,-103.9,29.3,-106.5,31.8,-117.1,32.5,-120.6,34.6,-124.4,40.3,-124.7,48.2,-122.6,47.1,-122.8,49.0],
+ &[-166.5,60.4,-165.6,59.9,-167.5,60.2,-166.5,60.4],
+ &[-153.2,58.0,-152.1,57.6,-154.5,57.0,-153.2,58.0],
+ &[-141.0,69.7,-141.0,60.3,-137.5,58.9,-135.5,59.8,-130.0,55.9,-130.5,54.8,-134.1,58.1,-139.9,59.5,-147.1,60.9,-151.7,59.2,-150.6,61.3,-158.4,56.0,-164.9,54.6,-157.0,58.9,-162.0,58.7,-165.3,60.5,-165.7,62.1,-160.8,64.8,-168.1,65.7,-161.7,66.1,-166.2,68.9,-156.6,71.4,-141.0,69.7],
+ &[-171.7,63.8,-168.7,63.3,-171.7,63.8],
+ ],
+ },
+ CountryOutline {
+ name: "Uruguay",
+ iso: "UY",
+ rings: &[
+ &[-57.6,-30.2,-53.8,-32.0,-53.8,-34.4,-58.4,-33.9,-57.6,-30.2],
+ ],
+ },
+ CountryOutline {
+ name: "Uzbekistan",
+ iso: "UZ",
+ rings: &[
+ &[56.0,41.3,55.9,45.0,58.5,45.6,62.0,43.5,64.9,43.7,68.3,40.7,71.0,42.3,73.1,40.9,67.7,39.6,67.8,37.1,58.6,42.8,56.0,41.3],
+ ],
+ },
+ CountryOutline {
+ name: "Venezuela",
+ iso: "VE",
+ rings: &[
+ &[-60.7,5.2,-64.8,4.1,-63.4,2.2,-66.3,0.7,-67.8,2.8,-67.3,6.1,-72.0,7.0,-72.9,10.5,-71.3,11.8,-71.3,9.1,-69.9,12.2,-68.2,10.6,-61.9,10.7,-59.8,8.4,-60.7,5.2],
+ ],
+ },
+ CountryOutline {
+ name: "Vietnam",
+ iso: "VN",
+ rings: &[
+ &[104.3,10.5,107.5,12.3,107.6,15.2,102.2,22.5,105.3,23.4,108.1,21.6,105.7,19.1,108.9,15.3,109.2,11.7,105.2,8.6,104.3,10.5],
+ ],
+ },
+ CountryOutline {
+ name: "W. Sahara",
+ iso: "EH",
+ rings: &[
+ &[-8.7,27.7,-8.7,25.9,-12.0,25.9,-12.9,21.3,-17.1,21.0,-14.8,21.5,-11.4,26.9,-8.7,27.7],
+ ],
+ },
+ CountryOutline {
+ name: "Yemen",
+ iso: "YE",
+ rings: &[
+ &[52.0,19.0,52.2,15.6,43.5,12.6,43.4,17.6,47.0,16.9,52.0,19.0],
+ ],
+ },
+ CountryOutline {
+ name: "Zambia",
+ iso: "ZM",
+ rings: &[
+ &[30.7,-8.3,33.2,-9.7,33.2,-14.0,27.0,-17.9,23.2,-17.5,21.9,-12.9,24.0,-12.9,23.9,-10.9,29.7,-13.3,28.4,-9.2,30.7,-8.3],
+ ],
+ },
+ CountryOutline {
+ name: "Zimbabwe",
+ iso: "ZW",
+ rings: &[
+ &[31.2,-22.3,28.0,-21.5,25.3,-17.7,30.3,-15.5,32.8,-16.7,31.2,-22.3],
+ ],
+ },
+];
diff --git a/ports/rust/src/ui.rs b/ports/rust/src/ui.rs
index 2d834ef..1374095 100644
--- a/ports/rust/src/ui.rs
+++ b/ports/rust/src/ui.rs
@@ -795,6 +795,24 @@ impl<'a> Container<'a> {
self.add(constraint, move |s| crate::graphics::draw_canvas(&s, &options))
}
+ /// A world map, and the country under whatever gets clicked.
+ ///
+ /// The click is answered by turning the cell back into degrees and testing
+ /// it against the outlines, so the answer is the country actually under the
+ /// cursor. Bounding boxes would be cheaper and wrong: Russia's covers most
+ /// of the northern hemisphere and Chile's covers Argentina.
+ pub fn world_map(&mut self, options: w::WorldMapOptions, id: &str) -> &mut Self {
+ let constraint = self.filling();
+ let ctx = self.ctx.clone();
+ let id = id.to_string();
+ self.add(constraint, move |s| {
+ w::draw_world_map(&s, &options);
+ if !id.is_empty() {
+ ctx.hit(&id, s.hit_rect(), 0);
+ }
+ })
+ }
+
pub fn sparkline(&mut self, options: w::SparklineWidgetOptions) -> &mut Self {
let constraint = self.leaf(1);
self.add(constraint, move |s| w::draw_sparkline(&s, &options))
diff --git a/ports/rust/src/widgets/mod.rs b/ports/rust/src/widgets/mod.rs
index 0ae7f78..013c4ef 100644
--- a/ports/rust/src/widgets/mod.rs
+++ b/ports/rust/src/widgets/mod.rs
@@ -9,6 +9,7 @@ pub mod meters;
pub mod scrollbar;
pub mod shadow;
pub mod surface;
+pub mod world;
pub mod table;
pub mod text;
@@ -35,6 +36,7 @@ pub use scrollbar::{
};
pub use shadow::{dim_rect, draw_shadow, ShadowOptions};
pub use surface::{draw_clear, draw_fill, ClearOptions, FillOptions};
+pub use world::{country_at_cell, draw_world_map, WorldMapOptions};
pub use table::{
draw_list, draw_log, draw_table, draw_tree, resolve_offset, TableColumn,
ListItem, ListOptions, LogEntry, LogOptions, TableOptions, TableRow, TreeNode, TreeOptions,
diff --git a/ports/rust/src/widgets/world.rs b/ports/rust/src/widgets/world.rs
new file mode 100644
index 0000000..b9b4469
--- /dev/null
+++ b/ports/rust/src/widgets/world.rs
@@ -0,0 +1,71 @@
+//! A world map you can click.
+//!
+//! The drawing is the canvas doing what it already does -- polylines in the
+//! caller's own coordinates, which for a map are degrees. What this adds is the
+//! other direction: turning a click back into a country.
+
+use crate::color::Color;
+use crate::graphics::canvas::{draw_canvas, Bounds, CanvasOptions};
+use crate::graphics::world::{
+ country_at, degrees_at, world_shapes, WorldShapeOptions, WORLD_X, WORLD_Y,
+};
+use crate::graphics::world_data::CountryOutline;
+use crate::surface::Surface;
+
+#[derive(Clone, Debug, Default)]
+pub struct WorldMapOptions {
+ /// The window on the globe. Defaults to all of it.
+ pub x: Option,
+ pub y: Option,
+ /// Coastline colour.
+ pub color: Option,
+ /// Countries to pick out, by name or ISO code.
+ pub highlight: Vec,
+ pub highlight_color: Option,
+ pub background: Option,
+ pub grid: bool,
+}
+
+pub fn draw_world_map(surface: &Surface, options: &WorldMapOptions) {
+ if surface.is_empty() {
+ return;
+ }
+ let theme = surface.theme.clone();
+ draw_canvas(
+ surface,
+ &CanvasOptions {
+ shapes: world_shapes(&WorldShapeOptions {
+ color: Some(options.color.unwrap_or(theme.border)),
+ highlight: options.highlight.clone(),
+ highlight_color: Some(options.highlight_color.unwrap_or(theme.accent)),
+ }),
+ x: Some(options.x.unwrap_or(WORLD_X)),
+ y: Some(options.y.unwrap_or(WORLD_Y)),
+ background: options.background,
+ grid: options.grid,
+ ..Default::default()
+ },
+ );
+}
+
+/// The country under a cell of a map drawn with these bounds.
+///
+/// Exposed so a caller can answer a hover as well as a click, and so the
+/// arithmetic that has to agree with the drawing lives in one place.
+pub fn country_at_cell(
+ column: usize,
+ row: usize,
+ width: usize,
+ height: usize,
+ options: &WorldMapOptions,
+) -> Option<&'static CountryOutline> {
+ let (lon, lat) = degrees_at(
+ column,
+ row,
+ width,
+ height,
+ options.x.unwrap_or(WORLD_X),
+ options.y.unwrap_or(WORLD_Y),
+ )?;
+ country_at(lon, lat)
+}
diff --git a/ports/rust/tests/conformance_widgets.rs b/ports/rust/tests/conformance_widgets.rs
index 6a28c92..40b9f9d 100644
--- a/ports/rust/tests/conformance_widgets.rs
+++ b/ports/rust/tests/conformance_widgets.rs
@@ -176,6 +176,22 @@ fn draw_scene(name: &str, s: &Surface) {
&ShadowOptions { color: Some(Color::rgb(0x10, 0x14, 0x18)), ..Default::default() },
);
}
+ "world" => draw_world_map(s, &WorldMapOptions::default()),
+ "world-zoom" => draw_world_map(
+ s,
+ &WorldMapOptions {
+ x: Some(Bounds { min: 112.0, max: 156.0 }),
+ y: Some(Bounds { min: 24.0, max: 50.0 }),
+ ..Default::default()
+ },
+ ),
+ "world-highlight" => draw_world_map(
+ s,
+ &WorldMapOptions {
+ highlight: vec!["Brazil".into(), "JP".into()],
+ ..Default::default()
+ },
+ ),
"badge" => {
draw_badge(s, &BadgeOptions::new("LIVE"));
}
diff --git a/ports/zig/build.zig b/ports/zig/build.zig
index 687e773..6785e8a 100644
--- a/ports/zig/build.zig
+++ b/ports/zig/build.zig
@@ -46,7 +46,7 @@ pub fn build(b: *std.Build) void {
// Examples are separate executables, so `zig build run-screenshot` works
// without a TTY while `run-dashboard` takes one over.
- for ([_][]const u8{ "hello", "dashboard", "screenshot", "widgets", "collapse", "justify_parity" }) |name| {
+ for ([_][]const u8{ "hello", "dashboard", "screenshot", "widgets", "collapse", "justify_parity", "world-probe" }) |name| {
const command_name = if (std.mem.eql(u8, name, "dashboard")) "dashboard-mini" else name;
const exe = b.addExecutable(.{
.name = command_name,
diff --git a/ports/zig/examples/world-probe.zig b/ports/zig/examples/world-probe.zig
new file mode 100644
index 0000000..26043e3
--- /dev/null
+++ b/ports/zig/examples/world-probe.zig
@@ -0,0 +1,66 @@
+//! Prints what the country lookup answers for a fixed set of points.
+//!
+//! The same probe exists for every port, so "the ports agree about the world" is
+//! a diff rather than a hope.
+const std = @import("std");
+const hqtui = @import("hqtui");
+
+const world = hqtui.graphics.world;
+const widgets = hqtui.widgets;
+
+pub fn main(init: std.process.Init) !void {
+ const stdout = std.Io.File.stdout();
+ // A line at a time straight out: this prints a few dozen lines once, so a
+ // buffer of its own would be ceremony.
+ var line: [256]u8 = undefined;
+
+ const places = [_]struct { name: []const u8, lon: f64, lat: f64 }{
+ .{ .name = "Paris", .lon = 2.35, .lat = 48.86 },
+ .{ .name = "Tokyo", .lon = 139.7, .lat = 35.7 },
+ .{ .name = "Cairo", .lon = 31.2, .lat = 30.0 },
+ .{ .name = "Brasilia", .lon = -47.9, .lat = -15.8 },
+ .{ .name = "Canberra", .lon = 149.1, .lat = -35.3 },
+ .{ .name = "Denver", .lon = -105.0, .lat = 39.7 },
+ .{ .name = "Moscow", .lon = 37.6, .lat = 55.75 },
+ .{ .name = "Delhi", .lon = 77.2, .lat = 28.6 },
+ .{ .name = "Nairobi", .lon = 36.8, .lat = -1.3 },
+ .{ .name = "Pacific", .lon = -140.0, .lat = 0.0 },
+ .{ .name = "Atlantic", .lon = -30.0, .lat = 0.0 },
+ .{ .name = "SouthernOcean", .lon = 80.0, .lat = -40.0 },
+ .{ .name = "NorthPacific", .lon = -150.0, .lat = 40.0 },
+ };
+ for (places) |place| {
+ const found = world.countryAt(place.lon, place.lat);
+ const text = try std.fmt.bufPrint(&line, "{s} {s}\n", .{
+ place.name,
+ if (found) |c| c.name else "-",
+ });
+ try stdout.writeStreamingAll(init.io, text);
+ }
+
+ // The cell path, which has to agree with what the canvas drew.
+ const cells = [_][2]usize{ .{ 173, 28 }, .{ 74, 2 }, .{ 20, 25 }, .{ 88, 7 } };
+ for (cells) |cell| {
+ const found = widgets.countryAtCell(cell[0], cell[1], 200, 50, .{});
+ const text = try std.fmt.bufPrint(&line, "cell:{d},{d} {s}\n", .{
+ cell[0],
+ cell[1],
+ if (found) |c| c.name else "-",
+ });
+ try stdout.writeStreamingAll(init.io, text);
+ }
+
+ // And the projection itself, so a drift shows up as a number rather than as
+ // a country that happens to still be right.
+ const probes = [_][2]usize{ .{ 0, 0 }, .{ 99, 25 }, .{ 50, 13 } };
+ for (probes) |cell| {
+ const at = world.degreesAt(cell[0], cell[1], 100, 26, world.WORLD_X, world.WORLD_Y).?;
+ const text = try std.fmt.bufPrint(&line, "degrees:{d},{d} {d:.4} {d:.4}\n", .{
+ cell[0],
+ cell[1],
+ at.lon,
+ at.lat,
+ });
+ try stdout.writeStreamingAll(init.io, text);
+ }
+}
diff --git a/ports/zig/src/conformance_widgets.zig b/ports/zig/src/conformance_widgets.zig
index 35017c8..3d04a3e 100644
--- a/ports/zig/src/conformance_widgets.zig
+++ b/ports/zig/src/conformance_widgets.zig
@@ -137,6 +137,15 @@ fn drawScene(allocator: std.mem.Allocator, name: []const u8, s: Surface) !void {
w.drawShadow(s, .{ .x = 2, .y = 1, .width = 6, .height = 2 }, .{
.color = hqtui_color.Color.rgb(0x10, 0x14, 0x18),
});
+ } else if (eq(u8, name, "world")) {
+ try w.drawWorldMap(allocator, s, .{});
+ } else if (eq(u8, name, "world-zoom")) {
+ try w.drawWorldMap(allocator, s, .{
+ .x = .{ .min = 112, .max = 156 },
+ .y = .{ .min = 24, .max = 50 },
+ });
+ } else if (eq(u8, name, "world-highlight")) {
+ try w.drawWorldMap(allocator, s, .{ .highlight = &.{ "Brazil", "JP" } });
} else if (eq(u8, name, "badge")) {
_ = w.drawBadge(s, .{ .text = "LIVE" });
} else if (eq(u8, name, "badge-outline")) {
diff --git a/ports/zig/src/graphics.zig b/ports/zig/src/graphics.zig
index 42904c8..83377c8 100644
--- a/ports/zig/src/graphics.zig
+++ b/ports/zig/src/graphics.zig
@@ -4,6 +4,8 @@
pub const blocks = @import("graphics/blocks.zig");
pub const braille = @import("graphics/braille.zig");
pub const canvas_mod = @import("graphics/canvas.zig");
+pub const world = @import("graphics/world.zig");
+pub const world_data = @import("graphics/world_data.zig");
pub const chart_mod = @import("graphics/chart.zig");
pub const plot_mod = @import("graphics/plot.zig");
@@ -35,6 +37,13 @@ pub const gauge = plot_mod.gauge;
pub const histogram = plot_mod.histogram;
pub const plot = plot_mod.plot;
pub const Bounds = canvas_mod.Bounds;
+pub const CountryOutline = world_data.CountryOutline;
+pub const WORLD_COUNTRIES = world_data.WORLD_COUNTRIES;
+pub const countryAt = world.countryAt;
+pub const findCountry = world.findCountry;
+pub const countryBounds = world.countryBounds;
+pub const degreesAt = world.degreesAt;
+pub const worldShapes = world.worldShapes;
pub const CanvasOptions = canvas_mod.CanvasOptions;
pub const Shape = canvas_mod.Shape;
pub const ShapeKind = canvas_mod.ShapeKind;
diff --git a/ports/zig/src/graphics/world.zig b/ports/zig/src/graphics/world.zig
new file mode 100644
index 0000000..adc7a10
--- /dev/null
+++ b/ports/zig/src/graphics/world.zig
@@ -0,0 +1,202 @@
+//! The world, as shapes for the canvas, and the lookup that makes it clickable.
+//!
+//! The canvas already draws in the caller's own coordinates, and longitude and
+//! latitude are just another pair of axes -- so a map is a list of polylines in
+//! degrees, and nothing here needs a projection of its own beyond deciding
+//! which window on the globe to show.
+//!
+//! The interesting half 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 testing it against the outlines.
+//! Doing it that way rather than with bounding boxes is what makes the answer
+//! right: Russia's bounding box covers most of the northern hemisphere, and
+//! Chile's covers Argentina.
+
+const std = @import("std");
+
+const braille_mod = @import("braille.zig");
+const canvas_mod = @import("canvas.zig");
+const color_mod = @import("../color.zig");
+const world_data = @import("world_data.zig");
+
+const Bounds = canvas_mod.Bounds;
+const Color = color_mod.Color;
+const Point = braille_mod.Point;
+const Shape = canvas_mod.Shape;
+
+pub const CountryOutline = world_data.CountryOutline;
+pub const WORLD_COUNTRIES = world_data.WORLD_COUNTRIES;
+
+/// The whole globe, which is what a map shows unless told otherwise.
+pub const WORLD_X = Bounds{ .min = -180, .max = 180 };
+pub const WORLD_Y = Bounds{ .min = -90, .max = 90 };
+
+pub const WorldShapeOptions = struct {
+ /// Colour for countries with nothing special about them.
+ color: ?Color = null,
+ /// Countries to pick out, by name or ISO code.
+ highlight: []const []const u8 = &.{},
+ highlight_color: ?Color = null,
+};
+
+/// Match on either the name or the ISO code, case-insensitively.
+fn matches(country: CountryOutline, keys: []const []const u8) bool {
+ for (keys) |key| {
+ if (key.len == 0) continue;
+ if (std.ascii.eqlIgnoreCase(country.name, key)) return true;
+ if (country.iso.len > 0 and std.ascii.eqlIgnoreCase(country.iso, key)) return true;
+ }
+ return false;
+}
+
+/// The world as canvas shapes, one polyline per landmass.
+///
+/// Polylines rather than scattered points: the outlines are closed rings, so
+/// joining them draws a coastline instead of a dotted suggestion of one, and it
+/// reads at a fraction of the resolution dots would need.
+///
+/// The caller owns the returned shapes and the points inside them, which is why
+/// this takes an allocator: a ring becomes a list of pairs, and there is nowhere
+/// else to put a couple of thousand of them.
+pub fn worldShapes(
+ allocator: std.mem.Allocator,
+ options: WorldShapeOptions,
+) ![]Shape {
+ var shapes = try allocator.alloc(Shape, countRings());
+ var at: usize = 0;
+ errdefer freeShapes(allocator, shapes[0..at]);
+
+ for (WORLD_COUNTRIES) |country| {
+ const picked = options.highlight.len > 0 and matches(country, options.highlight);
+ const color = if (picked) (options.highlight_color orelse options.color) else options.color;
+ for (country.rings) |ring| {
+ const count = ring.len / 2;
+ // Closed: the last point joins the first, or every country has a
+ // gap in its coastline where the ring started.
+ const points = try allocator.alloc(Point, if (count > 0) count + 1 else 0);
+ for (0..count) |i| {
+ points[i] = .{ .x = ring[i * 2], .y = ring[i * 2 + 1] };
+ }
+ if (count > 0) points[count] = points[0];
+ shapes[at] = .{ .kind = .polyline, .points = points, .color = color };
+ at += 1;
+ }
+ }
+ return shapes;
+}
+
+/// Release what `worldShapes` allocated.
+pub fn freeShapes(allocator: std.mem.Allocator, shapes: []Shape) void {
+ for (shapes) |shape| allocator.free(shape.points);
+ allocator.free(shapes);
+}
+
+fn countRings() usize {
+ var total: usize = 0;
+ for (WORLD_COUNTRIES) |country| total += country.rings.len;
+ return total;
+}
+
+/// Whether a point is inside a ring, by ray casting.
+///
+/// The ring is a flat list of interleaved coordinates, so this walks it two at
+/// a time rather than allocating a pair per vertex -- it runs once per country
+/// per click, and there are a couple of thousand vertices.
+fn insideRing(ring: []const f64, lon: f64, lat: f64) bool {
+ var inside = false;
+ const n = ring.len / 2;
+ if (n == 0) return false;
+ var j = n - 1;
+ for (0..n) |i| {
+ const xi = ring[i * 2];
+ const yi = ring[i * 2 + 1];
+ const xj = ring[j * 2];
+ const yj = ring[j * 2 + 1];
+ if ((yi > lat) != (yj > lat) and lon < (xj - xi) * (lat - yi) / (yj - yi) + xi) {
+ inside = !inside;
+ }
+ j = i;
+ }
+ return inside;
+}
+
+/// The country containing a point, or null for open water.
+///
+/// Where outlines overlap -- and at this resolution simplified borders do
+/// overlap -- the first match wins, which is stable because the data is sorted
+/// by name.
+pub fn countryAt(lon: f64, lat: f64) ?CountryOutline {
+ if (!std.math.isFinite(lon) or !std.math.isFinite(lat)) return null;
+ for (WORLD_COUNTRIES) |country| {
+ for (country.rings) |ring| {
+ if (insideRing(ring, lon, lat)) return country;
+ }
+ }
+ return null;
+}
+
+/// Look a country up by name or ISO code.
+pub fn findCountry(key: []const u8) ?CountryOutline {
+ const keys = [_][]const u8{key};
+ for (WORLD_COUNTRIES) |country| {
+ if (matches(country, &keys)) return country;
+ }
+ return null;
+}
+
+/// The window a country fills, with a little room around it.
+///
+/// For zooming a map to a country: the bounding box alone puts the coastline
+/// flat against the edge of the panel, which reads as though the country has
+/// been cut off rather than framed.
+pub fn countryBounds(country: CountryOutline, margin: f64) struct { x: Bounds, y: Bounds } {
+ var min_lon: f64 = std.math.inf(f64);
+ var max_lon: f64 = -std.math.inf(f64);
+ var min_lat: f64 = std.math.inf(f64);
+ var max_lat: f64 = -std.math.inf(f64);
+ for (country.rings) |ring| {
+ var i: usize = 0;
+ while (i + 1 < ring.len) : (i += 2) {
+ min_lon = @min(min_lon, ring[i]);
+ max_lon = @max(max_lon, ring[i]);
+ min_lat = @min(min_lat, ring[i + 1]);
+ max_lat = @max(max_lat, ring[i + 1]);
+ }
+ }
+ if (!std.math.isFinite(min_lon)) return .{ .x = WORLD_X, .y = WORLD_Y };
+ // A single-point country would give a zero-width window, which cannot be
+ // mapped onto anything.
+ const pad_x = @max((max_lon - min_lon) * margin, 1.0);
+ const pad_y = @max((max_lat - min_lat) * margin, 1.0);
+ return .{
+ .x = .{ .min = min_lon - pad_x, .max = max_lon + pad_x },
+ .y = .{ .min = min_lat - pad_y, .max = max_lat + pad_y },
+ };
+}
+
+/// The degrees under a terminal cell, given the window the map was drawn with.
+///
+/// The inverse of what the canvas does on the way in, taken at the centre of
+/// the cell: a click lands on a whole cell, and the centre is the only point in
+/// it that is not arbitrarily nearer one neighbour than the other.
+pub fn degreesAt(
+ column: usize,
+ row: usize,
+ width: usize,
+ height: usize,
+ x: Bounds,
+ y: Bounds,
+) ?struct { lon: f64, lat: f64 } {
+ if (width == 0 or height == 0) return null;
+ // The canvas is 2x4 Braille pixels per cell, and it spans its bounds across
+ // `pixels - 1`, so the inverse has to use the same denominators or a click
+ // drifts from what was drawn.
+ const px: f64 = @floatFromInt(@max(1, width * 2 -| 1));
+ const py: f64 = @floatFromInt(@max(1, height * 4 -| 1));
+ const cx: f64 = @floatFromInt(column * 2 + 1);
+ const cy: f64 = @floatFromInt(row * 4 + 2);
+ return .{
+ .lon = x.min + (cx / px) * (x.max - x.min),
+ .lat = y.min + (1 - cy / py) * (y.max - y.min),
+ };
+}
diff --git a/ports/zig/src/graphics/world_data.zig b/ports/zig/src/graphics/world_data.zig
new file mode 100644
index 0000000..2b3b984
--- /dev/null
+++ b/ports/zig/src/graphics/world_data.zig
@@ -0,0 +1,1306 @@
+//! Country outlines, flattened for a terminal.
+//!
+//! Generated by packages/hqtui/scripts/generate-world.ts from Natural Earth's
+//! 1:110m Admin 0 countries, which is public domain. Do not edit by hand.
+//!
+//! Each ring is longitude and latitude interleaved -- lon, lat, lon, lat --
+//! rather than a list of pairs, because at 1982 points the nested form
+//! costs a container per coordinate for no gain. A country has more than one
+//! ring when it is more than one landmass.
+//!
+//! 171 countries, 1982 points, simplified at 1 degrees.
+
+pub const CountryOutline = struct {
+ name: []const u8,
+ /// ISO 3166-1 alpha-2, where Natural Earth has one.
+ iso: []const u8,
+ /// Longitude and latitude, interleaved.
+ rings: []const []const f64,
+};
+
+pub const WORLD_COUNTRIES = [_]CountryOutline{
+ .{
+ .name = "Afghanistan",
+ .iso = "AF",
+ .rings = &.{
+ &.{66.5,37.4,70.8,38.5,71.8,36.7,75.2,37.1,71.3,36.1,69.3,31.9,66.3,29.9,60.9,29.8,61.2,35.7,66.5,37.4},
+ },
+ },
+ .{
+ .name = "Albania",
+ .iso = "AL",
+ .rings = &.{
+ &.{21.0,40.8,19.4,40.3,19.7,42.7,21.0,40.8},
+ },
+ },
+ .{
+ .name = "Algeria",
+ .iso = "DZ",
+ .rings = &.{
+ &.{-8.7,27.4,-8.7,28.8,-1.3,32.3,-1.2,35.7,8.4,36.9,7.5,34.1,9.8,29.4,9.3,26.1,12.0,23.5,3.2,19.1,-8.7,27.4},
+ },
+ },
+ .{
+ .name = "Angola",
+ .iso = "AO",
+ .rings = &.{
+ &.{12.3,-6.1,16.3,-5.9,17.5,-8.1,21.7,-7.3,22.2,-11.1,24.0,-11.2,24.0,-12.9,21.9,-12.9,23.2,-17.5,11.7,-17.3,13.7,-11.3,12.3,-6.1},
+ },
+ },
+ .{
+ .name = "Antarctica",
+ .iso = "AQ",
+ .rings = &.{
+ &.{-48.7,-78.0,-43.9,-78.5,-43.3,-80.0,-54.2,-80.6,-48.7,-78.0},
+ &.{-66.3,-80.3,-59.6,-80.0,-66.3,-80.3},
+ &.{-73.9,-71.3,-70.3,-68.9,-68.3,-71.4,-75.0,-72.1,-73.9,-71.3},
+ &.{-102.3,-71.9,-96.2,-72.5,-102.3,-71.9},
+ &.{-122.6,-73.7,-118.7,-73.5,-122.6,-73.7},
+ &.{-127.3,-73.5,-124.0,-73.9,-127.3,-73.5},
+ &.{-163.7,-78.6,-159.2,-79.5,-163.7,-78.6},
+ &.{180.0,-84.7,180.0,-90.0,-180.0,-90.0,-179.1,-84.1,-143.1,-85.0,-153.6,-83.7,-152.9,-82.0,-156.8,-81.1,-146.4,-80.3,-155.3,-79.1,-158.4,-76.9,-151.3,-77.4,-146.1,-76.5,-146.2,-75.4,-135.2,-74.3,-100.1,-74.9,-103.7,-72.6,-74.9,-73.9,-67.4,-72.5,-67.7,-67.3,-57.8,-63.3,-65.7,-68.0,-61.8,-70.7,-60.8,-73.7,-70.6,-76.6,-77.2,-76.7,-73.7,-77.9,-78.0,-79.2,-58.2,-83.2,-28.5,-80.3,-35.6,-79.5,-35.8,-78.3,-17.5,-75.1,-15.4,-73.1,-6.9,-70.9,27.1,-70.5,33.9,-68.5,38.6,-69.8,54.5,-65.8,61.4,-68.0,68.9,-67.9,69.7,-69.2,67.9,-71.9,69.9,-72.3,73.9,-69.9,88.0,-66.2,95.8,-67.4,102.8,-65.6,106.2,-66.9,113.6,-65.9,119.8,-67.3,135.1,-65.3,137.5,-67.0,145.5,-66.9,171.2,-71.7,163.6,-76.2,167.0,-78.8,161.8,-79.2,159.8,-80.9,169.4,-83.8,180.0,-84.7},
+ },
+ },
+ .{
+ .name = "Argentina",
+ .iso = "AR",
+ .rings = &.{
+ &.{-68.6,-52.6,-65.0,-54.7,-68.6,-54.9,-68.6,-52.6},
+ &.{-57.6,-30.2,-58.5,-34.4,-56.8,-36.9,-62.3,-38.8,-62.7,-41.0,-65.1,-41.1,-63.5,-42.6,-67.3,-45.6,-65.6,-47.2,-69.1,-50.7,-68.1,-52.3,-71.9,-52.0,-73.4,-49.3,-71.2,-44.8,-72.1,-42.3,-68.4,-24.5,-66.3,-21.8,-62.8,-22.0,-57.8,-25.2,-58.6,-27.1,-55.7,-27.4,-54.1,-25.5,-53.6,-26.9,-57.6,-30.2},
+ },
+ },
+ .{
+ .name = "Armenia",
+ .iso = "AM",
+ .rings = &.{
+ &.{46.5,38.8,43.6,41.1,45.6,40.8,46.5,38.8},
+ },
+ },
+ .{
+ .name = "Australia",
+ .iso = "AU",
+ .rings = &.{
+ &.{147.7,-40.8,147.9,-43.2,146.0,-43.5,144.7,-40.7,147.7,-40.8},
+ &.{126.1,-32.2,118.0,-35.1,115.0,-34.2,113.7,-22.5,120.9,-19.7,125.7,-14.2,129.6,-15.0,132.4,-11.1,136.5,-11.9,135.5,-15.0,140.2,-17.7,142.5,-10.7,146.4,-19.0,150.7,-22.4,153.6,-28.1,150.0,-37.4,146.3,-39.0,140.6,-38.0,138.2,-34.4,136.8,-35.3,137.8,-32.9,136.0,-34.9,131.3,-31.5,126.1,-32.2},
+ },
+ },
+ .{
+ .name = "Austria",
+ .iso = "AT",
+ .rings = &.{
+ &.{17.0,48.1,14.6,46.4,9.5,47.1,12.9,47.5,13.6,48.9,17.0,48.1},
+ },
+ },
+ .{
+ .name = "Azerbaijan",
+ .iso = "AZ",
+ .rings = &.{
+ &.{46.4,41.9,50.4,40.3,48.9,38.3,45.6,39.9,45.0,41.2,46.4,41.9},
+ },
+ },
+ .{
+ .name = "Bahamas",
+ .iso = "BS",
+ .rings = &.{
+ &.{-78.2,25.2,-77.5,23.8,-78.2,25.2},
+ },
+ },
+ .{
+ .name = "Bangladesh",
+ .iso = "BD",
+ .rings = &.{
+ &.{92.7,22.0,92.4,20.7,91.4,22.8,89.0,22.1,88.6,26.4,92.4,25.0,91.2,23.5,92.7,22.0},
+ },
+ },
+ .{
+ .name = "Belarus",
+ .iso = "BY",
+ .rings = &.{
+ &.{28.2,56.2,30.9,55.6,32.7,53.4,31.8,52.1,23.5,51.6,23.5,53.9,28.2,56.2},
+ },
+ },
+ .{
+ .name = "Belgium",
+ .iso = "BE",
+ .rings = &.{
+ &.{6.2,50.8,5.7,49.5,2.5,51.1,6.2,50.8},
+ },
+ },
+ .{
+ .name = "Belize",
+ .iso = "BZ",
+ .rings = &.{
+ &.{-89.1,17.8,-88.1,18.3,-88.9,15.9,-89.1,17.8},
+ },
+ },
+ .{
+ .name = "Benin",
+ .iso = "BJ",
+ .rings = &.{
+ &.{2.7,6.3,0.8,10.5,2.8,12.2,2.7,6.3},
+ },
+ },
+ .{
+ .name = "Bhutan",
+ .iso = "BT",
+ .rings = &.{
+ &.{91.7,27.8,88.8,27.1,91.7,27.8},
+ },
+ },
+ .{
+ .name = "Bolivia",
+ .iso = "BO",
+ .rings = &.{
+ &.{-69.5,-11.0,-65.3,-9.8,-65.4,-11.6,-60.5,-13.8,-60.2,-16.3,-58.2,-16.3,-57.9,-20.0,-61.8,-19.6,-62.7,-22.2,-67.8,-22.9,-69.5,-11.0},
+ },
+ },
+ .{
+ .name = "Bosnia and Herz.",
+ .iso = "BA",
+ .rings = &.{
+ &.{18.6,42.7,16.0,45.2,19.4,44.9,18.6,42.7},
+ },
+ },
+ .{
+ .name = "Botswana",
+ .iso = "BW",
+ .rings = &.{
+ &.{29.4,-22.1,25.7,-25.5,21.6,-26.7,19.9,-24.8,20.9,-18.3,25.3,-17.7,29.4,-22.1},
+ },
+ },
+ .{
+ .name = "Brazil",
+ .iso = "BR",
+ .rings = &.{
+ &.{-53.4,-33.8,-53.8,-32.0,-57.6,-30.2,-53.6,-26.1,-55.8,-22.4,-57.9,-22.1,-58.2,-16.3,-60.2,-16.3,-60.5,-13.8,-65.4,-11.6,-65.3,-9.8,-70.5,-11.0,-70.5,-9.5,-72.2,-10.1,-74.0,-7.5,-72.9,-5.3,-69.9,-4.3,-69.8,1.7,-65.5,0.8,-63.4,2.2,-64.8,4.1,-60.7,5.2,-59.0,1.3,-52.9,2.1,-51.3,4.2,-50.4,-0.1,-44.6,-2.7,-40.0,-2.9,-35.6,-5.1,-34.7,-7.3,-38.7,-13.1,-40.9,-21.9,-47.6,-24.9,-48.9,-28.7,-53.4,-33.8},
+ },
+ },
+ .{
+ .name = "Bulgaria",
+ .iso = "BG",
+ .rings = &.{
+ &.{22.7,44.2,28.6,43.7,28.0,42.0,23.0,41.3,22.7,44.2},
+ },
+ },
+ .{
+ .name = "Burkina Faso",
+ .iso = "BF",
+ .rings = &.{
+ &.{-5.4,10.4,-4.3,13.2,-1.1,15.0,2.2,12.6,0.9,11.0,-2.9,11.0,-2.8,9.6,-5.4,10.4},
+ },
+ },
+ .{
+ .name = "Burundi",
+ .iso = "BI",
+ .rings = &.{
+ &.{30.5,-2.4,29.3,-4.5,29.0,-2.8,30.5,-2.4},
+ },
+ },
+ .{
+ .name = "Cambodia",
+ .iso = "KH",
+ .rings = &.{
+ &.{102.6,12.2,103.0,14.2,107.6,13.5,106.2,11.0,103.5,10.6,102.6,12.2},
+ },
+ },
+ .{
+ .name = "Cameroon",
+ .iso = "CM",
+ .rings = &.{
+ &.{14.5,12.9,14.5,4.7,15.9,1.7,9.6,2.3,8.8,5.5,11.7,7.0,14.5,12.9},
+ },
+ },
+ .{
+ .name = "Canada",
+ .iso = "CA",
+ .rings = &.{
+ &.{-122.8,49.0,-127.4,50.8,-130.5,54.3,-130.0,55.9,-135.5,59.8,-137.5,58.9,-141.0,60.3,-141.0,69.7,-136.5,68.9,-128.1,70.5,-113.5,67.7,-106.1,68.8,-101.5,67.6,-97.7,68.6,-96.1,67.3,-94.2,69.1,-96.5,70.1,-95.2,71.9,-87.4,67.2,-85.5,69.9,-82.6,69.7,-81.4,67.1,-85.8,66.6,-90.7,63.6,-94.7,58.9,-92.3,57.1,-82.3,55.1,-79.9,51.2,-78.6,52.6,-79.8,54.7,-76.5,56.5,-78.5,58.8,-77.3,59.9,-78.1,62.3,-73.8,62.4,-69.6,61.1,-67.6,58.2,-64.6,60.3,-61.8,56.3,-57.3,54.6,-55.7,52.1,-60.0,50.2,-66.4,50.2,-71.1,46.8,-65.1,49.2,-64.5,46.2,-60.5,47.0,-59.8,45.9,-65.4,43.5,-66.2,44.5,-64.4,45.3,-67.1,45.1,-69.2,47.4,-71.5,45.0,-82.4,41.7,-82.6,45.3,-88.4,48.3,-122.8,49.0},
+ &.{-84.0,62.5,-81.9,62.9,-84.0,62.5},
+ &.{-79.8,72.8,-80.8,73.7,-76.3,72.8,-79.8,72.8},
+ &.{-93.6,75.0,-96.8,74.9,-93.6,75.0},
+ &.{-93.8,77.5,-96.4,77.8,-93.8,77.5},
+ &.{-96.8,78.8,-95.6,78.4,-98.6,78.9,-96.8,78.8},
+ &.{-88.2,74.4,-97.1,76.8,-79.8,74.9,-88.2,74.4},
+ &.{-111.3,78.2,-109.9,78.0,-113.5,77.7,-111.3,78.2},
+ &.{-111.0,78.8,-109.7,78.6,-112.5,78.4,-111.0,78.8},
+ &.{-55.6,51.3,-56.8,49.8,-53.5,49.2,-53.1,46.7,-59.3,47.6,-55.6,51.3},
+ &.{-83.9,65.1,-80.1,63.7,-87.2,63.5,-85.9,65.7,-83.9,65.1},
+ &.{-78.8,72.4,-68.8,70.5,-67.0,69.2,-68.8,68.7,-61.9,66.9,-63.9,65.0,-68.0,66.3,-64.7,63.4,-68.8,63.7,-66.2,61.9,-68.9,62.3,-78.6,64.6,-74.0,65.5,-73.3,68.1,-79.0,70.2,-88.7,70.4,-90.2,72.2,-85.8,73.8,-85.8,72.5,-82.3,73.8,-78.8,72.4},
+ &.{-94.5,74.1,-90.5,73.9,-95.4,72.1,-96.0,73.4,-94.5,74.1},
+ &.{-122.9,76.1,-116.2,77.6,-122.9,76.1},
+ &.{-132.7,54.0,-131.2,52.2,-132.7,54.0},
+ &.{-105.5,79.3,-99.7,77.9,-105.5,79.3},
+ &.{-123.5,48.5,-128.4,50.8,-123.5,48.5},
+ &.{-121.5,74.4,-115.5,73.5,-123.1,70.9,-125.9,71.9,-123.9,73.7,-124.9,74.3,-121.5,74.4},
+ &.{-107.8,75.8,-105.7,75.5,-117.7,75.2,-115.4,76.5,-107.8,75.8},
+ &.{-106.5,73.1,-101.1,69.6,-113.3,68.5,-117.3,70.0,-112.4,70.4,-119.4,71.6,-115.2,73.3,-108.2,71.7,-108.4,73.1,-106.5,73.1},
+ &.{-100.4,72.7,-101.5,73.4,-97.4,73.8,-96.5,72.6,-98.4,71.3,-102.5,72.5,-100.4,72.7},
+ &.{-106.6,73.6,-104.5,73.4,-106.6,73.6},
+ &.{-98.5,76.7,-98.2,75.0,-102.5,75.6,-98.5,76.7},
+ &.{-96.0,80.6,-92.4,81.3,-85.8,79.3,-92.9,78.3,-96.0,80.6},
+ &.{-91.6,81.9,-61.8,82.6,-76.9,79.3,-75.4,78.5,-80.6,76.2,-89.5,76.5,-88.3,77.9,-85.0,77.5,-88.0,78.4,-85.1,79.3,-86.9,80.3,-81.8,80.5,-91.6,81.9},
+ &.{-75.2,67.4,-77.2,67.6,-75.2,67.4},
+ &.{-96.3,69.5,-99.8,69.4,-96.3,69.5},
+ &.{-64.5,49.9,-61.8,49.1,-64.5,49.9},
+ &.{-64.0,47.0,-62.0,46.4,-64.0,47.0},
+ },
+ },
+ .{
+ .name = "Central African Rep.",
+ .iso = "CF",
+ .rings = &.{
+ &.{27.4,5.2,22.4,4.0,19.5,5.0,16.0,2.3,14.5,5.5,15.3,7.4,22.9,11.1,27.4,5.2},
+ },
+ },
+ .{
+ .name = "Chad",
+ .iso = "TD",
+ .rings = &.{
+ &.{23.8,19.6,23.9,15.6,21.9,12.6,22.9,11.1,15.3,7.4,13.5,14.4,15.9,20.4,14.9,22.9,23.8,19.6},
+ },
+ },
+ .{
+ .name = "Chile",
+ .iso = "CL",
+ .rings = &.{
+ &.{-68.6,-52.6,-68.6,-54.9,-67.0,-54.9,-68.1,-55.6,-74.7,-52.8,-71.1,-54.1,-68.6,-52.6},
+ &.{-69.6,-17.6,-67.0,-23.0,-70.5,-31.4,-69.8,-34.2,-72.1,-42.3,-71.2,-44.8,-73.4,-49.3,-71.9,-52.0,-68.6,-52.3,-71.4,-53.9,-74.9,-52.3,-75.6,-48.7,-74.1,-46.9,-75.6,-46.6,-72.7,-42.4,-74.3,-43.2,-69.6,-17.6},
+ },
+ },
+ .{
+ .name = "China",
+ .iso = "CN",
+ .rings = &.{
+ &.{109.5,18.2,108.6,19.4,110.8,20.1,109.5,18.2},
+ &.{80.3,42.3,80.0,44.9,87.8,49.3,91.0,46.9,90.9,45.3,96.3,42.7,109.2,42.5,111.9,45.1,119.7,46.7,115.5,48.1,122.2,53.4,125.9,52.8,131.0,47.8,135.0,48.5,133.1,45.1,131.0,45.0,130.6,42.4,121.1,38.9,121.6,40.9,117.5,38.7,122.4,37.5,119.2,34.9,121.9,31.7,121.7,28.2,118.7,24.5,110.4,20.3,105.3,23.4,101.7,22.3,101.8,21.2,99.2,22.1,97.6,23.9,98.7,27.5,96.1,29.5,88.8,27.3,78.7,31.5,78.9,34.3,73.7,39.4,80.3,42.3},
+ },
+ },
+ .{
+ .name = "Colombia",
+ .iso = "CO",
+ .rings = &.{
+ &.{-66.9,1.3,-69.8,1.7,-69.9,-4.3,-70.0,-2.7,-77.4,0.4,-79.0,1.7,-77.1,3.8,-77.5,8.5,-71.4,12.4,-73.3,9.2,-72.0,7.0,-67.3,6.1,-66.9,1.3},
+ },
+ },
+ .{
+ .name = "Congo",
+ .iso = "CG",
+ .rings = &.{
+ &.{18.5,3.5,16.0,-3.5,11.9,-5.0,11.5,-2.8,14.4,-1.3,13.1,2.3,15.9,1.7,18.5,3.5},
+ },
+ },
+ .{
+ .name = "Costa Rica",
+ .iso = "CR",
+ .rings = &.{
+ &.{-82.5,9.6,-83.0,8.2,-85.9,10.9,-82.5,9.6},
+ },
+ },
+ .{
+ .name = "Côte d'Ivoire",
+ .iso = "CI",
+ .rings = &.{
+ &.{-8.0,10.2,-2.8,9.6,-2.9,5.0,-7.7,4.4,-8.0,10.2},
+ },
+ },
+ .{
+ .name = "Croatia",
+ .iso = "HR",
+ .rings = &.{
+ &.{16.6,46.5,19.4,45.2,15.8,44.8,18.5,42.5,13.7,45.1,16.6,46.5},
+ },
+ },
+ .{
+ .name = "Cuba",
+ .iso = "CU",
+ .rings = &.{
+ &.{-82.3,23.2,-74.2,20.3,-77.8,19.9,-81.8,22.6,-85.0,21.9,-82.3,23.2},
+ },
+ },
+ .{
+ .name = "Cyprus",
+ .iso = "CY",
+ .rings = &.{
+ &.{32.7,35.1,34.0,35.0,32.7,35.1},
+ },
+ },
+ .{
+ .name = "Czechia",
+ .iso = "CZ",
+ .rings = &.{
+ &.{15.0,51.1,18.9,49.5,12.5,49.5,15.0,51.1},
+ },
+ },
+ .{
+ .name = "Dem. Rep. Congo",
+ .iso = "CD",
+ .rings = &.{
+ &.{29.3,-4.5,30.7,-8.3,28.7,-8.5,28.4,-11.8,29.7,-13.3,22.2,-11.1,21.7,-7.3,17.5,-8.1,16.3,-5.9,12.2,-5.8,16.0,-3.5,19.5,5.0,29.7,4.6,31.2,2.2,29.3,-4.5},
+ },
+ },
+ .{
+ .name = "Denmark",
+ .iso = "DK",
+ .rings = &.{
+ &.{9.9,55.0,8.1,56.5,10.6,57.7,9.9,55.0},
+ &.{12.4,56.1,12.1,54.8,11.0,55.4,12.4,56.1},
+ },
+ },
+ .{
+ .name = "Djibouti",
+ .iso = "DJ",
+ .rings = &.{
+ &.{42.4,12.5,42.8,10.9,42.4,12.5},
+ },
+ },
+ .{
+ .name = "Dominican Rep.",
+ .iso = "DO",
+ .rings = &.{
+ &.{-71.7,18.0,-71.6,19.9,-68.3,18.6,-71.7,18.0},
+ },
+ },
+ .{
+ .name = "Ecuador",
+ .iso = "EC",
+ .rings = &.{
+ &.{-75.4,-0.2,-78.6,-4.5,-80.4,-4.4,-80.1,0.8,-75.4,-0.2},
+ },
+ },
+ .{
+ .name = "Egypt",
+ .iso = "EG",
+ .rings = &.{
+ &.{36.9,22.0,25.0,22.0,25.2,31.6,34.3,31.2,34.2,27.8,32.3,29.8,36.9,22.0},
+ },
+ },
+ .{
+ .name = "El Salvador",
+ .iso = "SV",
+ .rings = &.{
+ &.{-89.4,14.4,-87.9,13.1,-90.1,13.7,-89.4,14.4},
+ },
+ },
+ .{
+ .name = "Eq. Guinea",
+ .iso = "GQ",
+ .rings = &.{
+ &.{9.6,2.3,11.3,1.1,9.5,1.0,9.6,2.3},
+ },
+ },
+ .{
+ .name = "Eritrea",
+ .iso = "ER",
+ .rings = &.{
+ &.{36.4,14.4,38.4,18.0,43.1,12.7,36.4,14.4},
+ },
+ },
+ .{
+ .name = "Estonia",
+ .iso = "EE",
+ .rings = &.{
+ &.{28.0,59.5,27.3,57.5,23.3,59.2,28.0,59.5},
+ },
+ },
+ .{
+ .name = "eSwatini",
+ .iso = "SZ",
+ .rings = &.{
+ &.{32.1,-26.7,31.0,-25.7,32.1,-26.7},
+ },
+ },
+ .{
+ .name = "Ethiopia",
+ .iso = "ET",
+ .rings = &.{
+ &.{47.8,8.0,45.0,5.0,39.6,3.4,36.2,4.4,33.0,7.8,37.9,15.0,41.6,13.5,43.7,9.2,47.8,8.0},
+ },
+ },
+ .{
+ .name = "Falkland Is.",
+ .iso = "FK",
+ .rings = &.{
+ &.{-61.2,-51.8,-57.7,-51.5,-61.2,-51.8},
+ },
+ },
+ .{
+ .name = "Finland",
+ .iso = "FI",
+ .rings = &.{
+ &.{28.6,69.1,31.1,62.4,28.1,60.5,21.3,60.7,21.5,63.2,25.4,65.1,20.6,69.1,24.7,68.6,27.7,70.2,28.6,69.1},
+ },
+ },
+ .{
+ .name = "Fr. S. Antarctic Lands",
+ .iso = "TF",
+ .rings = &.{
+ &.{68.9,-48.6,70.6,-49.3,68.7,-49.8,68.9,-48.6},
+ },
+ },
+ .{
+ .name = "France",
+ .iso = "FR",
+ .rings = &.{
+ &.{-51.7,4.2,-52.9,2.1,-54.5,2.3,-54.0,5.8,-51.7,4.2},
+ &.{6.2,49.5,8.1,49.0,6.0,46.7,7.4,43.7,1.8,42.3,-1.9,43.4,-1.2,46.0,-4.6,48.7,-1.6,48.6,-1.9,49.8,2.5,51.1,6.2,49.5},
+ &.{8.7,42.6,9.2,41.4,8.7,42.6},
+ },
+ },
+ .{
+ .name = "Gabon",
+ .iso = "GA",
+ .rings = &.{
+ &.{11.3,2.3,14.3,1.2,14.4,-1.3,11.1,-4.0,8.8,-1.1,11.3,2.3},
+ },
+ },
+ .{
+ .name = "Gambia",
+ .iso = "GM",
+ .rings = &.{
+ &.{-16.7,13.6,-13.8,13.5,-16.7,13.6},
+ },
+ },
+ .{
+ .name = "Georgia",
+ .iso = "GE",
+ .rings = &.{
+ &.{40.0,43.4,46.6,41.2,41.6,41.5,40.0,43.4},
+ },
+ },
+ .{
+ .name = "Germany",
+ .iso = "DE",
+ .rings = &.{
+ &.{14.1,53.8,15.0,51.1,12.2,50.3,12.9,47.5,7.5,47.6,8.1,49.0,6.0,50.1,7.1,53.7,9.9,55.0,14.1,53.8},
+ },
+ },
+ .{
+ .name = "Ghana",
+ .iso = "GH",
+ .rings = &.{
+ &.{0.0,11.0,1.1,5.9,-2.9,5.0,-2.9,11.0,0.0,11.0},
+ },
+ },
+ .{
+ .name = "Greece",
+ .iso = "GR",
+ .rings = &.{
+ &.{26.3,35.3,23.5,35.3,26.3,35.3},
+ &.{23.0,41.3,26.6,41.6,22.6,40.3,24.0,37.7,22.5,36.4,20.2,39.6,23.0,41.3},
+ },
+ },
+ .{
+ .name = "Greenland",
+ .iso = "GL",
+ .rings = &.{
+ &.{-46.8,82.6,-27.1,83.5,-20.8,82.7,-31.9,82.2,-12.2,81.3,-20.0,80.2,-17.7,80.1,-19.7,78.8,-18.5,77.0,-21.7,76.6,-19.4,74.3,-24.8,72.3,-21.8,70.7,-25.5,71.4,-26.4,70.2,-22.3,70.1,-39.8,65.5,-43.4,60.1,-48.3,60.9,-51.6,63.6,-54.0,67.2,-50.9,69.9,-54.7,69.6,-54.4,70.8,-51.4,70.6,-55.8,71.7,-54.7,72.6,-58.6,75.5,-68.5,76.1,-71.4,77.0,-66.8,77.4,-73.3,78.0,-65.7,79.4,-68.0,80.1,-62.7,81.8,-44.5,81.7,-46.8,82.6},
+ },
+ },
+ .{
+ .name = "Guatemala",
+ .iso = "GT",
+ .rings = &.{
+ &.{-92.2,14.5,-90.5,16.1,-91.0,17.8,-89.1,17.8,-88.2,15.7,-89.4,14.4,-92.2,14.5},
+ },
+ },
+ .{
+ .name = "Guinea",
+ .iso = "GN",
+ .rings = &.{
+ &.{-13.7,12.6,-9.1,12.3,-8.3,7.7,-11.1,10.0,-13.2,8.9,-15.1,11.0,-13.7,12.6},
+ },
+ },
+ .{
+ .name = "Guinea-Bissau",
+ .iso = "GW",
+ .rings = &.{
+ &.{-16.7,12.4,-13.7,11.8,-15.1,11.0,-16.7,12.4},
+ },
+ },
+ .{
+ .name = "Guyana",
+ .iso = "GY",
+ .rings = &.{
+ &.{-56.5,1.9,-59.6,1.8,-61.4,6.0,-59.8,8.4,-57.1,6.0,-58.0,4.1,-56.5,1.9},
+ },
+ },
+ .{
+ .name = "Haiti",
+ .iso = "HT",
+ .rings = &.{
+ &.{-71.7,19.7,-71.7,18.0,-74.5,18.3,-71.7,19.7},
+ },
+ },
+ .{
+ .name = "Honduras",
+ .iso = "HN",
+ .rings = &.{
+ &.{-83.1,15.0,-87.3,13.0,-89.4,14.4,-87.9,15.9,-83.1,15.0},
+ },
+ },
+ .{
+ .name = "Hungary",
+ .iso = "HU",
+ .rings = &.{
+ &.{22.1,48.4,21.0,46.3,16.2,46.9,17.0,48.1,22.1,48.4},
+ },
+ },
+ .{
+ .name = "Iceland",
+ .iso = "IS",
+ .rings = &.{
+ &.{-14.5,66.5,-13.6,65.1,-18.7,63.5,-22.8,64.0,-21.8,64.4,-24.0,64.9,-22.2,65.4,-24.3,65.6,-14.5,66.5},
+ },
+ },
+ .{
+ .name = "India",
+ .iso = "IN",
+ .rings = &.{
+ &.{97.3,28.3,92.7,22.0,91.2,23.5,92.4,25.0,88.6,26.4,88.9,21.7,80.3,15.9,79.9,10.4,77.5,8.0,72.6,21.4,70.5,20.9,68.2,23.7,71.0,24.4,69.5,26.9,75.3,32.3,73.7,34.3,77.8,35.5,78.7,31.5,81.1,30.2,80.1,28.8,83.3,27.4,88.1,26.4,88.7,28.1,92.0,26.8,96.1,29.5,97.3,28.3},
+ },
+ },
+ .{
+ .name = "Indonesia",
+ .iso = "ID",
+ .rings = &.{
+ &.{141.0,-2.6,141.0,-9.1,137.6,-8.4,137.9,-5.4,133.0,-4.1,132.0,-2.8,133.7,-2.2,130.5,-0.9,134.0,-0.8,135.5,-3.4,137.4,-1.7,141.0,-2.6},
+ &.{125.0,-8.9,123.5,-10.2,125.0,-8.9},
+ &.{117.9,4.1,119.0,0.9,116.1,-4.0,110.2,-2.9,109.7,2.0,110.5,0.8,113.8,1.2,115.9,4.3,117.9,4.1},
+ &.{129.4,-2.8,130.8,-3.9,127.9,-3.4,129.4,-2.8},
+ &.{127.9,2.2,128.1,-0.9,127.9,2.2},
+ &.{122.9,0.9,125.2,1.4,120.0,-0.5,123.3,-0.6,121.5,-1.9,123.2,-5.3,121.0,-2.6,119.8,-5.7,118.8,-2.8,119.8,0.2,122.9,0.9},
+ &.{120.3,-10.3,119.0,-9.6,120.3,-10.3},
+ &.{121.3,-8.5,122.9,-8.1,119.9,-8.8,121.3,-8.5},
+ &.{118.3,-8.4,116.7,-9.0,118.3,-8.4},
+ &.{108.5,-6.4,115.7,-8.4,105.4,-6.9,108.5,-6.4},
+ &.{104.4,-1.1,106.1,-3.1,105.8,-5.9,102.6,-4.2,95.3,5.5,97.5,5.2,104.4,-1.1},
+ },
+ },
+ .{
+ .name = "Iran",
+ .iso = "IR",
+ .rings = &.{
+ &.{48.6,29.9,45.4,34.0,46.1,35.7,44.1,39.4,48.1,39.6,50.8,36.9,56.6,38.1,61.1,36.5,60.9,29.8,63.3,26.8,61.5,25.1,57.4,25.7,48.6,29.9},
+ },
+ },
+ .{
+ .name = "Iraq",
+ .iso = "IQ",
+ .rings = &.{
+ &.{39.2,32.2,41.3,36.4,44.8,37.2,48.6,29.9,44.7,29.2,39.2,32.2},
+ },
+ },
+ .{
+ .name = "Ireland",
+ .iso = "IE",
+ .rings = &.{
+ &.{-6.2,53.9,-6.8,52.3,-10.0,51.8,-7.6,55.1,-6.2,53.9},
+ },
+ },
+ .{
+ .name = "Israel",
+ .iso = "IL",
+ .rings = &.{
+ &.{35.7,32.7,34.9,29.5,34.3,31.2,35.7,32.7},
+ },
+ },
+ .{
+ .name = "Italy",
+ .iso = "IT",
+ .rings = &.{
+ &.{10.4,46.9,13.8,46.5,12.6,44.1,18.3,39.8,16.9,40.4,15.7,37.9,15.4,40.0,10.2,43.9,7.4,43.7,6.8,46.0,10.4,46.9},
+ &.{14.8,38.1,15.1,36.6,12.4,37.6,14.8,38.1},
+ &.{8.7,40.9,9.8,40.5,8.8,38.9,8.7,40.9},
+ },
+ },
+ .{
+ .name = "Jamaica",
+ .iso = "JM",
+ .rings = &.{
+ &.{-77.6,18.5,-76.2,17.9,-77.6,18.5},
+ },
+ },
+ .{
+ .name = "Japan",
+ .iso = "JP",
+ .rings = &.{
+ &.{141.9,39.2,140.3,35.1,135.8,33.5,135.1,34.6,131.0,33.9,132.0,33.1,130.2,31.4,129.4,33.3,139.4,38.2,140.3,41.2,141.9,39.2},
+ &.{144.6,44.0,145.5,43.3,140.0,41.6,142.0,45.6,144.6,44.0},
+ &.{132.4,33.5,134.8,33.8,132.4,33.5},
+ },
+ },
+ .{
+ .name = "Jordan",
+ .iso = "JO",
+ .rings = &.{
+ &.{35.5,32.4,38.8,33.4,39.2,32.2,37.0,31.5,38.0,30.5,36.1,29.2,34.9,29.5,35.5,32.4},
+ },
+ },
+ .{
+ .name = "Kazakhstan",
+ .iso = "KZ",
+ .rings = &.{
+ &.{87.4,49.2,80.0,44.9,80.3,42.3,74.2,43.3,68.6,40.7,64.9,43.7,62.0,43.5,58.5,45.6,55.9,45.0,56.0,41.3,52.5,41.8,50.3,44.6,53.0,45.3,53.0,46.9,49.1,46.4,46.5,48.4,50.8,51.7,61.3,50.8,60.0,52.0,61.4,54.0,69.1,55.4,73.4,53.5,76.9,54.5,80.0,50.9,87.4,49.2},
+ },
+ },
+ .{
+ .name = "Kenya",
+ .iso = "KE",
+ .rings = &.{
+ &.{39.2,-4.7,33.9,-0.9,35.3,5.5,38.1,3.6,41.9,3.9,41.6,-1.7,39.2,-4.7},
+ },
+ },
+ .{
+ .name = "Kosovo",
+ .iso = "XK",
+ .rings = &.{
+ &.{20.6,41.9,20.6,43.2,21.8,42.7,20.6,41.9},
+ },
+ },
+ .{
+ .name = "Kuwait",
+ .iso = "KW",
+ .rings = &.{
+ &.{48.0,30.0,48.4,28.6,46.6,29.1,48.0,30.0},
+ },
+ },
+ .{
+ .name = "Kyrgyzstan",
+ .iso = "KG",
+ .rings = &.{
+ &.{71.0,42.3,74.2,43.3,80.3,42.3,73.7,39.4,69.5,39.5,73.1,40.9,71.0,42.3},
+ },
+ },
+ .{
+ .name = "Laos",
+ .iso = "LA",
+ .rings = &.{
+ &.{107.4,14.2,105.2,14.3,104.0,18.2,101.1,17.5,100.1,20.4,101.7,22.3,104.4,20.8,103.9,19.3,107.4,14.2},
+ },
+ },
+ .{
+ .name = "Latvia",
+ .iso = "LV",
+ .rings = &.{
+ &.{27.3,57.5,28.2,56.2,26.5,55.6,21.1,56.0,22.5,57.8,27.3,57.5},
+ },
+ },
+ .{
+ .name = "Lebanon",
+ .iso = "LB",
+ .rings = &.{
+ &.{35.8,33.3,36.4,34.6,35.8,33.3},
+ },
+ },
+ .{
+ .name = "Lesotho",
+ .iso = "LS",
+ .rings = &.{
+ &.{29.0,-29.0,28.1,-30.5,27.0,-29.9,29.0,-29.0},
+ },
+ },
+ .{
+ .name = "Liberia",
+ .iso = "LR",
+ .rings = &.{
+ &.{-8.4,7.7,-7.7,4.4,-11.4,6.8,-10.2,8.4,-8.4,7.7},
+ },
+ },
+ .{
+ .name = "Libya",
+ .iso = "LY",
+ .rings = &.{
+ &.{25.0,22.0,23.8,19.6,10.3,24.4,10.0,31.4,11.5,33.1,19.1,30.3,20.9,32.7,24.9,31.9,25.0,22.0},
+ },
+ },
+ .{
+ .name = "Lithuania",
+ .iso = "LT",
+ .rings = &.{
+ &.{26.5,55.6,23.5,53.9,21.1,56.0,26.5,55.6},
+ },
+ },
+ .{
+ .name = "Madagascar",
+ .iso = "MG",
+ .rings = &.{
+ &.{49.5,-12.5,50.4,-15.7,47.1,-24.9,45.4,-25.6,43.3,-22.8,44.0,-17.4,49.5,-12.5},
+ },
+ },
+ .{
+ .name = "Malawi",
+ .iso = "MW",
+ .rings = &.{
+ &.{32.8,-9.2,35.7,-14.6,35.0,-16.8,32.7,-13.7,32.8,-9.2},
+ },
+ },
+ .{
+ .name = "Malaysia",
+ .iso = "MY",
+ .rings = &.{
+ &.{100.1,6.5,103.0,5.5,104.2,1.3,101.4,2.8,100.1,6.5},
+ &.{117.9,4.1,115.9,4.3,114.6,1.4,109.8,1.3,115.3,4.3,116.7,6.9,119.2,5.4,117.9,4.1},
+ },
+ },
+ .{
+ .name = "Mali",
+ .iso = "ML",
+ .rings = &.{
+ &.{-11.5,12.4,-11.7,15.4,-5.5,15.5,-6.5,25.0,4.3,19.2,3.6,15.6,-4.0,13.5,-5.4,10.4,-11.5,12.4},
+ },
+ },
+ .{
+ .name = "Mauritania",
+ .iso = "MR",
+ .rings = &.{
+ &.{-17.1,21.0,-12.9,21.3,-12.0,25.9,-8.7,25.9,-8.7,27.4,-4.9,25.0,-6.5,25.0,-5.5,15.5,-12.2,14.6,-14.6,16.6,-16.5,16.1,-17.1,21.0},
+ },
+ },
+ .{
+ .name = "Mexico",
+ .iso = "MX",
+ .rings = &.{
+ &.{-117.1,32.5,-106.5,31.8,-103.9,29.3,-101.7,29.8,-97.1,25.9,-97.9,22.4,-95.9,18.8,-91.4,18.9,-90.3,21.0,-87.1,21.5,-87.8,18.3,-91.0,17.8,-90.5,16.1,-92.2,14.5,-103.5,18.3,-113.1,31.2,-114.9,31.4,-109.9,22.8,-115.1,27.7,-114.2,28.6,-117.1,32.5},
+ },
+ },
+ .{
+ .name = "Moldova",
+ .iso = "MD",
+ .rings = &.{
+ &.{26.6,48.2,30.0,46.4,28.2,45.5,26.6,48.2},
+ },
+ },
+ .{
+ .name = "Mongolia",
+ .iso = "MN",
+ .rings = &.{
+ &.{87.8,49.3,92.2,50.8,97.3,49.7,98.9,52.0,108.5,49.3,116.7,49.9,115.7,47.7,119.8,47.0,105.0,41.6,96.3,42.7,90.9,45.3,91.0,46.9,87.8,49.3},
+ },
+ },
+ .{
+ .name = "Montenegro",
+ .iso = "ME",
+ .rings = &.{
+ &.{20.1,42.6,18.5,42.5,20.1,42.6},
+ },
+ },
+ .{
+ .name = "Morocco",
+ .iso = "MA",
+ .rings = &.{
+ &.{-2.2,35.2,-1.3,32.3,-8.7,28.8,-8.8,27.1,-11.4,26.9,-14.8,21.5,-17.0,21.4,-14.4,26.3,-9.6,29.9,-8.7,33.2,-5.9,35.8,-2.2,35.2},
+ },
+ },
+ .{
+ .name = "Mozambique",
+ .iso = "MZ",
+ .rings = &.{
+ &.{34.6,-11.5,40.3,-10.3,40.8,-14.7,34.8,-19.8,35.5,-24.1,32.1,-26.7,31.2,-22.3,32.8,-16.7,30.2,-14.8,33.2,-14.0,35.0,-16.8,34.6,-11.5},
+ },
+ },
+ .{
+ .name = "Myanmar",
+ .iso = "MM",
+ .rings = &.{
+ &.{100.1,20.4,97.4,18.4,99.6,11.9,98.6,9.9,97.2,16.9,94.2,16.0,92.3,21.5,97.3,28.3,98.7,27.5,97.6,23.9,101.2,21.8,100.1,20.4},
+ },
+ },
+ .{
+ .name = "N. Cyprus",
+ .iso = "-99",
+ .rings = &.{
+ &.{32.7,35.1,34.6,35.7,32.7,35.1},
+ },
+ },
+ .{
+ .name = "Namibia",
+ .iso = "NA",
+ .rings = &.{
+ &.{19.9,-24.8,19.9,-28.5,16.3,-28.6,11.7,-17.3,25.1,-17.6,20.9,-18.3,19.9,-24.8},
+ },
+ },
+ .{
+ .name = "Nepal",
+ .iso = "NP",
+ .rings = &.{
+ &.{88.1,27.9,87.2,26.4,80.1,28.8,81.5,30.4,88.1,27.9},
+ },
+ },
+ .{
+ .name = "Netherlands",
+ .iso = "NL",
+ .rings = &.{
+ &.{6.9,53.5,6.2,50.8,3.3,51.3,6.9,53.5},
+ },
+ },
+ .{
+ .name = "New Caledonia",
+ .iso = "NC",
+ .rings = &.{
+ &.{165.8,-21.1,167.1,-22.2,164.0,-20.1,165.8,-21.1},
+ },
+ },
+ .{
+ .name = "New Zealand",
+ .iso = "NZ",
+ .rings = &.{
+ &.{176.9,-40.1,174.7,-41.3,174.7,-37.4,172.6,-34.5,176.0,-37.6,178.5,-37.7,176.9,-40.1},
+ &.{169.7,-43.6,172.8,-40.5,174.2,-41.3,170.6,-45.9,166.7,-46.2,169.7,-43.6},
+ },
+ },
+ .{
+ .name = "Nicaragua",
+ .iso = "NI",
+ .rings = &.{
+ &.{-83.7,10.9,-87.7,12.9,-83.1,15.0,-83.7,10.9},
+ },
+ },
+ .{
+ .name = "Niger",
+ .iso = "NE",
+ .rings = &.{
+ &.{14.9,22.9,15.9,20.4,13.5,14.4,14.2,12.5,5.4,13.9,3.6,11.7,1.0,12.9,0.4,14.9,3.6,15.6,4.3,19.2,12.0,23.5,14.9,22.9},
+ },
+ },
+ .{
+ .name = "Nigeria",
+ .iso = "NG",
+ .rings = &.{
+ &.{2.7,6.3,4.4,13.7,13.1,13.6,14.6,12.1,11.7,7.0,8.5,4.8,5.9,4.3,2.7,6.3},
+ },
+ },
+ .{
+ .name = "North Korea",
+ .iso = "KP",
+ .rings = &.{
+ &.{130.6,42.4,127.5,39.8,128.2,38.4,124.7,38.1,125.1,40.6,130.6,42.4},
+ },
+ },
+ .{
+ .name = "North Macedonia",
+ .iso = "MK",
+ .rings = &.{
+ &.{22.4,42.3,23.0,41.3,20.6,41.1,22.4,42.3},
+ },
+ },
+ .{
+ .name = "Norway",
+ .iso = "NO",
+ .rings = &.{
+ &.{15.1,79.7,21.5,79.0,15.9,76.8,10.4,79.7,15.1,79.7},
+ &.{31.1,69.6,18.0,68.6,12.6,64.1,11.0,58.9,5.7,58.6,5.0,62.0,19.2,69.8,28.2,71.2,31.1,69.6},
+ &.{27.4,80.1,17.4,80.3,27.4,80.1},
+ &.{24.7,77.9,20.7,77.7,24.7,77.9},
+ },
+ },
+ .{
+ .name = "Oman",
+ .iso = "OM",
+ .rings = &.{
+ &.{55.2,22.7,56.4,24.9,59.8,22.3,57.7,18.9,53.1,16.7,52.0,19.0,55.0,20.0,55.2,22.7},
+ },
+ },
+ .{
+ .name = "Pakistan",
+ .iso = "PK",
+ .rings = &.{
+ &.{77.8,35.5,73.7,34.3,75.3,32.3,69.5,26.9,71.0,24.4,61.5,25.1,63.3,26.8,60.9,29.8,66.3,29.9,71.8,36.5,75.2,37.1,77.8,35.5},
+ },
+ },
+ .{
+ .name = "Panama",
+ .iso = "PA",
+ .rings = &.{
+ &.{-77.4,8.7,-77.9,7.2,-79.1,9.0,-80.9,7.2,-82.9,9.5,-77.4,8.7},
+ },
+ },
+ .{
+ .name = "Papua New Guinea",
+ .iso = "PG",
+ .rings = &.{
+ &.{141.0,-2.6,147.6,-6.1,147.2,-7.4,150.7,-10.6,144.7,-7.6,141.0,-9.1,141.0,-2.6},
+ &.{152.6,-3.7,152.8,-4.8,150.7,-2.7,152.6,-3.7},
+ &.{151.3,-5.8,148.3,-5.7,152.1,-4.1,151.3,-5.8},
+ &.{154.8,-5.3,155.9,-6.8,154.8,-5.3},
+ },
+ },
+ .{
+ .name = "Paraguay",
+ .iso = "PY",
+ .rings = &.{
+ &.{-58.2,-20.2,-57.9,-22.1,-54.3,-24.0,-55.7,-27.4,-58.6,-27.1,-57.8,-25.2,-62.7,-22.2,-61.8,-19.6,-58.2,-20.2},
+ },
+ },
+ .{
+ .name = "Peru",
+ .iso = "PE",
+ .rings = &.{
+ &.{-69.9,-4.3,-72.9,-5.3,-74.0,-7.5,-68.7,-12.6,-70.4,-18.3,-76.0,-14.6,-81.4,-4.7,-80.3,-3.4,-78.6,-4.5,-75.1,-0.1,-73.1,-2.3,-70.0,-2.7,-69.9,-4.3},
+ },
+ },
+ .{
+ .name = "Philippines",
+ .iso = "PH",
+ .rings = &.{
+ &.{122.6,10.0,124.1,11.2,123.0,9.0,122.6,10.0},
+ &.{126.4,8.4,125.4,5.6,123.6,7.8,121.9,7.2,125.4,9.8,126.4,8.4},
+ &.{118.5,9.3,117.2,8.4,119.5,11.4,118.5,9.3},
+ &.{122.3,18.2,121.7,14.3,124.1,12.5,119.9,15.4,120.7,18.5,122.3,18.2},
+ &.{125.5,12.2,124.8,10.1,124.3,12.6,125.5,12.2},
+ },
+ },
+ .{
+ .name = "Poland",
+ .iso = "PL",
+ .rings = &.{
+ &.{23.5,53.9,24.0,50.7,22.8,49.0,16.2,50.4,14.1,53.0,17.6,54.9,23.5,53.9},
+ },
+ },
+ .{
+ .name = "Portugal",
+ .iso = "PT",
+ .rings = &.{
+ &.{-9.0,41.9,-6.4,41.4,-7.9,36.8,-9.5,38.7,-9.0,41.9},
+ },
+ },
+ .{
+ .name = "Puerto Rico",
+ .iso = "PR",
+ .rings = &.{
+ &.{-66.3,18.5,-67.2,17.9,-66.3,18.5},
+ },
+ },
+ .{
+ .name = "Qatar",
+ .iso = "QA",
+ .rings = &.{
+ &.{50.8,24.8,51.3,26.1,50.8,24.8},
+ },
+ },
+ .{
+ .name = "Romania",
+ .iso = "RO",
+ .rings = &.{
+ &.{28.2,45.5,29.6,45.3,28.6,43.7,22.9,43.8,20.2,46.1,26.6,48.2,28.2,45.5},
+ },
+ },
+ .{
+ .name = "Russia",
+ .iso = "RU",
+ .rings = &.{
+ &.{49.1,46.4,46.7,44.6,47.8,41.2,36.7,45.2,40.1,49.6,31.8,52.1,32.7,53.4,30.9,55.6,27.3,57.5,29.1,60.0,28.1,60.5,31.5,62.9,30.0,63.6,28.6,69.1,32.1,69.9,41.1,67.5,38.4,66.0,33.2,66.6,37.0,63.8,37.2,65.1,43.9,66.1,43.5,68.6,46.3,68.3,46.3,66.7,53.7,68.9,59.9,68.3,60.6,69.9,68.5,68.1,66.7,71.0,69.9,73.0,72.8,72.2,71.8,71.4,73.7,68.4,71.3,66.3,72.4,66.2,75.1,67.8,73.1,71.4,74.7,72.8,76.4,71.2,81.5,71.8,80.5,73.6,104.4,77.7,114.1,75.8,109.4,74.2,127.0,73.6,131.3,70.8,139.9,71.5,139.1,72.4,140.5,72.8,159.0,70.9,160.9,69.4,180.0,69.0,180.0,65.0,177.4,64.6,179.2,62.3,170.3,59.9,163.5,59.9,162.0,58.2,163.2,57.6,162.1,54.9,156.8,51.0,155.9,56.8,164.5,62.6,160.1,60.5,156.7,61.4,154.2,59.8,155.0,59.1,142.2,59.0,135.1,54.7,141.3,53.1,140.1,48.4,134.9,43.4,130.8,42.2,131.0,45.0,133.1,45.1,135.0,48.5,131.0,47.8,123.6,53.5,120.2,52.8,117.9,49.5,108.5,49.3,98.9,52.0,97.3,49.7,92.2,50.8,87.4,49.2,80.0,50.9,76.9,54.5,73.4,53.5,69.1,55.4,61.4,54.0,60.0,52.0,61.3,50.8,50.8,51.7,47.5,50.5,46.5,48.4,49.1,46.4},
+ &.{93.8,81.0,100.2,79.8,97.8,78.8,91.2,80.3,93.8,81.0},
+ &.{102.8,79.3,105.4,78.7,99.4,77.9,102.8,79.3},
+ &.{138.8,76.1,145.1,75.6,137.0,75.3,138.8,76.1},
+ &.{148.2,75.3,150.7,75.1,146.1,75.2,148.2,75.3},
+ &.{139.9,73.4,143.6,73.2,139.9,73.4},
+ &.{44.8,80.6,51.5,80.7,44.8,80.6},
+ &.{22.7,54.3,19.7,54.4,22.7,54.3},
+ &.{53.5,73.7,61.2,76.3,68.9,76.5,58.5,74.3,55.4,72.4,57.5,70.7,51.6,71.5,53.5,73.7},
+ &.{142.9,53.7,144.7,49.0,143.2,49.3,143.5,46.1,142.1,46.0,141.7,53.3,142.9,53.7},
+ &.{-174.9,67.2,-169.9,66.0,-173.0,64.3,-178.7,66.1,-180.0,65.0,-180.0,69.0,-174.9,67.2},
+ &.{-178.7,70.9,-180.0,71.5,-177.6,71.3,-178.7,70.9},
+ &.{33.4,46.0,36.5,45.5,33.9,44.4,32.5,45.3,33.4,46.0},
+ },
+ },
+ .{
+ .name = "Rwanda",
+ .iso = "RW",
+ .rings = &.{
+ &.{30.4,-1.1,29.0,-2.8,30.4,-1.1},
+ },
+ },
+ .{
+ .name = "S. Sudan",
+ .iso = "SS",
+ .rings = &.{
+ &.{30.8,3.5,23.9,8.6,25.8,10.4,31.4,9.8,33.2,12.2,33.0,7.8,35.3,5.5,30.8,3.5},
+ },
+ },
+ .{
+ .name = "Saudi Arabia",
+ .iso = "SA",
+ .rings = &.{
+ &.{35.0,29.4,39.2,32.2,47.5,29.0,52.0,23.0,55.2,22.7,55.0,20.0,47.0,16.9,43.4,17.6,42.8,16.3,35.0,29.4},
+ },
+ },
+ .{
+ .name = "Senegal",
+ .iso = "SN",
+ .rings = &.{
+ &.{-16.7,13.6,-17.6,14.7,-14.6,16.6,-11.5,12.4,-16.7,12.4,-13.8,13.5,-16.7,13.6},
+ },
+ },
+ .{
+ .name = "Serbia",
+ .iso = "RS",
+ .rings = &.{
+ &.{18.8,45.9,22.7,44.6,22.5,42.5,19.2,43.5,18.8,45.9},
+ },
+ },
+ .{
+ .name = "Sierra Leone",
+ .iso = "SL",
+ .rings = &.{
+ &.{-13.2,8.9,-11.1,10.0,-10.2,8.4,-11.4,6.8,-13.2,8.9},
+ },
+ },
+ .{
+ .name = "Slovakia",
+ .iso = "SK",
+ .rings = &.{
+ &.{22.6,49.1,16.9,48.5,22.6,49.1},
+ },
+ },
+ .{
+ .name = "Slovenia",
+ .iso = "SI",
+ .rings = &.{
+ &.{13.8,46.5,16.6,46.5,15.3,45.5,13.8,46.5},
+ },
+ },
+ .{
+ .name = "Solomon Is.",
+ .iso = "SB",
+ .rings = &.{
+ &.{159.6,-8.0,158.2,-7.4,159.6,-8.0},
+ },
+ },
+ .{
+ .name = "Somalia",
+ .iso = "SO",
+ .rings = &.{
+ &.{41.6,-1.7,41.0,2.8,45.0,5.0,48.9,9.5,48.9,11.4,51.1,12.0,48.6,5.3,41.6,-1.7},
+ },
+ },
+ .{
+ .name = "Somaliland",
+ .iso = "-99",
+ .rings = &.{
+ &.{48.9,11.4,47.8,8.0,42.6,10.6,48.9,11.4},
+ },
+ },
+ .{
+ .name = "South Africa",
+ .iso = "ZA",
+ .rings = &.{
+ &.{16.3,-28.6,19.9,-28.5,19.9,-24.8,21.6,-26.7,25.7,-25.5,29.4,-22.1,31.2,-22.3,31.9,-24.4,30.7,-26.7,32.8,-26.7,28.2,-32.8,20.1,-34.8,18.4,-34.1,16.3,-28.6},
+ },
+ },
+ .{
+ .name = "South Korea",
+ .iso = "KR",
+ .rings = &.{
+ &.{126.2,37.7,128.3,38.6,129.1,35.1,126.5,34.4,126.2,37.7},
+ },
+ },
+ .{
+ .name = "Spain",
+ .iso = "ES",
+ .rings = &.{
+ &.{-7.5,37.1,-6.4,41.4,-9.4,43.0,3.0,42.5,-2.1,36.7,-7.5,37.1},
+ },
+ },
+ .{
+ .name = "Sri Lanka",
+ .iso = "LK",
+ .rings = &.{
+ &.{81.8,7.5,80.3,6.0,80.1,9.8,81.8,7.5},
+ },
+ },
+ .{
+ .name = "Sudan",
+ .iso = "SD",
+ .rings = &.{
+ &.{24.6,8.2,21.9,12.6,25.0,22.0,36.9,22.0,38.4,18.0,34.0,8.7,32.7,12.2,31.4,9.8,25.1,10.3,24.6,8.2},
+ },
+ },
+ .{
+ .name = "Suriname",
+ .iso = "SR",
+ .rings = &.{
+ &.{-54.5,2.3,-56.5,1.9,-57.6,3.3,-57.1,6.0,-54.0,5.8,-54.5,2.3},
+ },
+ },
+ .{
+ .name = "Sweden",
+ .iso = "SE",
+ .rings = &.{
+ &.{11.0,58.9,12.6,61.3,11.9,63.1,16.8,68.0,20.6,69.1,23.5,67.9,23.9,66.0,17.8,62.7,17.1,61.3,18.8,60.1,15.9,56.1,12.9,55.4,11.0,58.9},
+ },
+ },
+ .{
+ .name = "Switzerland",
+ .iso = "CH",
+ .rings = &.{
+ &.{9.6,47.5,10.4,46.5,6.0,46.3,9.6,47.5},
+ },
+ },
+ .{
+ .name = "Syria",
+ .iso = "SY",
+ .rings = &.{
+ &.{35.7,32.7,36.7,36.8,42.3,37.2,41.0,34.4,35.7,32.7},
+ },
+ },
+ .{
+ .name = "Taiwan",
+ .iso = "TW",
+ .rings = &.{
+ &.{121.8,24.4,120.7,22.0,120.1,23.6,121.8,24.4},
+ },
+ },
+ .{
+ .name = "Tajikistan",
+ .iso = "TJ",
+ .rings = &.{
+ &.{67.8,37.1,67.7,39.6,70.7,41.0,69.5,39.5,73.7,39.4,75.0,37.4,71.8,36.7,70.8,38.5,67.8,37.1},
+ },
+ },
+ .{
+ .name = "Tanzania",
+ .iso = "TZ",
+ .rings = &.{
+ &.{33.9,-0.9,39.2,-4.7,39.5,-10.9,34.6,-11.5,29.6,-6.5,30.4,-1.1,33.9,-0.9},
+ },
+ },
+ .{
+ .name = "Thailand",
+ .iso = "TH",
+ .rings = &.{
+ &.{105.2,14.3,103.0,14.2,102.6,12.2,100.1,13.4,99.2,9.2,102.1,6.2,101.2,5.7,98.2,8.4,99.6,11.9,97.4,18.4,100.1,20.4,101.1,17.5,104.7,17.4,105.2,14.3},
+ },
+ },
+ .{
+ .name = "Timor-Leste",
+ .iso = "TL",
+ .rings = &.{
+ &.{125.0,-8.9,127.3,-8.4,125.0,-8.9},
+ },
+ },
+ .{
+ .name = "Togo",
+ .iso = "TG",
+ .rings = &.{
+ &.{0.9,11.0,1.1,5.9,0.9,11.0},
+ },
+ },
+ .{
+ .name = "Tunisia",
+ .iso = "TN",
+ .rings = &.{
+ &.{9.5,30.3,7.5,34.1,9.5,37.3,11.0,37.1,10.1,34.3,11.5,33.1,9.5,30.3},
+ },
+ },
+ .{
+ .name = "Turkey",
+ .iso = "TR",
+ .rings = &.{
+ &.{44.8,37.2,29.7,36.1,27.6,36.7,26.2,39.5,33.5,42.0,42.6,41.6,44.8,39.7,44.8,37.2},
+ &.{26.1,41.8,29.0,41.3,26.4,40.2,26.1,41.8},
+ },
+ },
+ .{
+ .name = "Turkmenistan",
+ .iso = "TM",
+ .rings = &.{
+ &.{52.5,41.8,57.1,41.3,58.6,42.8,66.5,37.4,62.2,35.3,57.3,38.0,53.9,37.2,52.7,40.0,54.7,41.0,52.5,41.8},
+ },
+ },
+ .{
+ .name = "Uganda",
+ .iso = "UG",
+ .rings = &.{
+ &.{33.9,-0.9,29.6,-1.3,31.2,3.8,34.5,3.6,33.9,-0.9},
+ },
+ },
+ .{
+ .name = "Ukraine",
+ .iso = "UA",
+ .rings = &.{
+ &.{31.8,52.1,40.1,49.6,39.7,47.9,35.0,45.7,31.7,46.7,28.7,45.3,30.0,46.4,28.7,48.1,22.1,48.4,23.5,51.6,31.8,52.1},
+ },
+ },
+ .{
+ .name = "United Arab Emirates",
+ .iso = "AE",
+ .rings = &.{
+ &.{51.6,24.2,56.3,25.7,55.0,22.5,51.6,24.2},
+ },
+ },
+ .{
+ .name = "United Kingdom",
+ .iso = "GB",
+ .rings = &.{
+ &.{-6.2,53.9,-7.6,55.1,-6.2,53.9},
+ &.{-3.1,53.4,-6.1,56.8,-5.0,58.6,-2.0,57.7,-3.1,56.0,1.7,52.7,1.4,51.3,-5.8,50.2,-3.4,51.4,-5.3,52.0,-4.6,53.5,-3.1,53.4},
+ },
+ },
+ .{
+ .name = "United States of America",
+ .iso = "US",
+ .rings = &.{
+ &.{-122.8,49.0,-88.4,48.3,-82.6,45.3,-82.7,41.7,-71.5,45.0,-69.2,47.4,-67.0,44.8,-70.1,43.7,-70.0,41.6,-75.5,39.5,-75.9,37.2,-76.3,39.2,-77.0,38.2,-75.7,35.6,-81.3,31.4,-80.4,25.2,-83.7,29.9,-86.4,30.4,-94.7,29.5,-97.5,25.8,-101.0,29.4,-103.9,29.3,-106.5,31.8,-117.1,32.5,-120.6,34.6,-124.4,40.3,-124.7,48.2,-122.6,47.1,-122.8,49.0},
+ &.{-166.5,60.4,-165.6,59.9,-167.5,60.2,-166.5,60.4},
+ &.{-153.2,58.0,-152.1,57.6,-154.5,57.0,-153.2,58.0},
+ &.{-141.0,69.7,-141.0,60.3,-137.5,58.9,-135.5,59.8,-130.0,55.9,-130.5,54.8,-134.1,58.1,-139.9,59.5,-147.1,60.9,-151.7,59.2,-150.6,61.3,-158.4,56.0,-164.9,54.6,-157.0,58.9,-162.0,58.7,-165.3,60.5,-165.7,62.1,-160.8,64.8,-168.1,65.7,-161.7,66.1,-166.2,68.9,-156.6,71.4,-141.0,69.7},
+ &.{-171.7,63.8,-168.7,63.3,-171.7,63.8},
+ },
+ },
+ .{
+ .name = "Uruguay",
+ .iso = "UY",
+ .rings = &.{
+ &.{-57.6,-30.2,-53.8,-32.0,-53.8,-34.4,-58.4,-33.9,-57.6,-30.2},
+ },
+ },
+ .{
+ .name = "Uzbekistan",
+ .iso = "UZ",
+ .rings = &.{
+ &.{56.0,41.3,55.9,45.0,58.5,45.6,62.0,43.5,64.9,43.7,68.3,40.7,71.0,42.3,73.1,40.9,67.7,39.6,67.8,37.1,58.6,42.8,56.0,41.3},
+ },
+ },
+ .{
+ .name = "Venezuela",
+ .iso = "VE",
+ .rings = &.{
+ &.{-60.7,5.2,-64.8,4.1,-63.4,2.2,-66.3,0.7,-67.8,2.8,-67.3,6.1,-72.0,7.0,-72.9,10.5,-71.3,11.8,-71.3,9.1,-69.9,12.2,-68.2,10.6,-61.9,10.7,-59.8,8.4,-60.7,5.2},
+ },
+ },
+ .{
+ .name = "Vietnam",
+ .iso = "VN",
+ .rings = &.{
+ &.{104.3,10.5,107.5,12.3,107.6,15.2,102.2,22.5,105.3,23.4,108.1,21.6,105.7,19.1,108.9,15.3,109.2,11.7,105.2,8.6,104.3,10.5},
+ },
+ },
+ .{
+ .name = "W. Sahara",
+ .iso = "EH",
+ .rings = &.{
+ &.{-8.7,27.7,-8.7,25.9,-12.0,25.9,-12.9,21.3,-17.1,21.0,-14.8,21.5,-11.4,26.9,-8.7,27.7},
+ },
+ },
+ .{
+ .name = "Yemen",
+ .iso = "YE",
+ .rings = &.{
+ &.{52.0,19.0,52.2,15.6,43.5,12.6,43.4,17.6,47.0,16.9,52.0,19.0},
+ },
+ },
+ .{
+ .name = "Zambia",
+ .iso = "ZM",
+ .rings = &.{
+ &.{30.7,-8.3,33.2,-9.7,33.2,-14.0,27.0,-17.9,23.2,-17.5,21.9,-12.9,24.0,-12.9,23.9,-10.9,29.7,-13.3,28.4,-9.2,30.7,-8.3},
+ },
+ },
+ .{
+ .name = "Zimbabwe",
+ .iso = "ZW",
+ .rings = &.{
+ &.{31.2,-22.3,28.0,-21.5,25.3,-17.7,30.3,-15.5,32.8,-16.7,31.2,-22.3},
+ },
+ },
+};
diff --git a/ports/zig/src/ui.zig b/ports/zig/src/ui.zig
index 72f2837..6d78c07 100644
--- a/ports/zig/src/ui.zig
+++ b/ports/zig/src/ui.zig
@@ -375,6 +375,7 @@ const Node = union(enum) {
calendar: w.CalendarOptions,
chart: w.ChartOptions,
shapes: graphics.CanvasOptions,
+ world_map: w.WorldMapOptions,
clear: w.ClearOptions,
fill: w.FillOptions,
sparkline: w.SparklineWidgetOptions,
@@ -467,6 +468,7 @@ fn drawNode(ctx: *Ctx, s: Surface, node: Node) anyerror!void {
.calendar => |o| w.drawCalendar(s, o),
.chart => |o| try w.drawChart(allocator, s, o),
.shapes => |o| try graphics.drawCanvas(allocator, s, o),
+ .world_map => |o| try w.drawWorldMap(allocator, s, o),
.clear => |o| w.drawClear(s, o),
.fill => |o| w.drawFill(s, o),
.sparkline => |o| w.drawSparkline(s, o),
@@ -888,6 +890,16 @@ pub const Container = struct {
try self.add(self.filling(), .{ .shapes = options });
}
+ /// A world map, and the country under whatever gets clicked.
+ ///
+ /// The click is answered by turning the cell back into degrees and testing
+ /// it against the outlines, so the answer is the country actually under the
+ /// cursor. Bounding boxes would be cheaper and wrong: Russia's covers most
+ /// of the northern hemisphere and Chile's covers Argentina.
+ pub fn worldMap(self: *Container, options: w.WorldMapOptions) !void {
+ try self.add(self.filling(), .{ .world_map = options });
+ }
+
pub fn sparkline(self: *Container, options: w.SparklineWidgetOptions) !void {
try self.add(self.leaf(1), .{ .sparkline = options });
}
diff --git a/ports/zig/src/widgets.zig b/ports/zig/src/widgets.zig
index ad4a2e4..acac111 100644
--- a/ports/zig/src/widgets.zig
+++ b/ports/zig/src/widgets.zig
@@ -7,6 +7,7 @@ pub const meters = @import("widgets/meters.zig");
pub const calendar = @import("widgets/calendar.zig");
pub const chart = @import("widgets/chart.zig");
pub const shadow = @import("widgets/shadow.zig");
+pub const world_map = @import("widgets/world.zig");
pub const surface_widgets = @import("widgets/surface.zig");
pub const scrollbar = @import("widgets/scrollbar.zig");
pub const table = @import("widgets/table.zig");
@@ -68,6 +69,9 @@ pub const CalendarOptions = calendar.CalendarOptions;
pub const calendarHeight = calendar.calendarHeight;
pub const drawCalendar = calendar.drawCalendar;
pub const ChartOptions = chart.ChartOptions;
+pub const WorldMapOptions = world_map.WorldMapOptions;
+pub const drawWorldMap = world_map.drawWorldMap;
+pub const countryAtCell = world_map.countryAtCell;
pub const ShadowOptions = shadow.ShadowOptions;
pub const dimRect = shadow.dimRect;
pub const drawShadow = shadow.drawShadow;
diff --git a/ports/zig/src/widgets/world.zig b/ports/zig/src/widgets/world.zig
new file mode 100644
index 0000000..30e0665
--- /dev/null
+++ b/ports/zig/src/widgets/world.zig
@@ -0,0 +1,74 @@
+//! A world map you can click.
+//!
+//! The drawing is the canvas doing what it already does -- polylines in the
+//! caller's own coordinates, which for a map are degrees. What this adds is the
+//! other direction: turning a click back into a country.
+
+const std = @import("std");
+
+const canvas_mod = @import("../graphics/canvas.zig");
+const color_mod = @import("../color.zig");
+const surface_mod = @import("../surface.zig");
+const world = @import("../graphics/world.zig");
+
+const Bounds = canvas_mod.Bounds;
+const Color = color_mod.Color;
+const CountryOutline = world.CountryOutline;
+const Surface = surface_mod.Surface;
+
+pub const WorldMapOptions = struct {
+ /// The window on the globe. Null means all of it.
+ x: ?Bounds = null,
+ y: ?Bounds = null,
+ /// Coastline colour.
+ color: ?Color = null,
+ /// Countries to pick out, by name or ISO code.
+ highlight: []const []const u8 = &.{},
+ highlight_color: ?Color = null,
+ background: ?Color = null,
+ grid: bool = false,
+
+ fn window(self: WorldMapOptions) struct { x: Bounds, y: Bounds } {
+ return .{ .x = self.x orelse world.WORLD_X, .y = self.y orelse world.WORLD_Y };
+ }
+};
+
+pub fn drawWorldMap(
+ allocator: std.mem.Allocator,
+ s: Surface,
+ options: WorldMapOptions,
+) !void {
+ if (s.isEmpty()) return;
+ const theme = s.theme;
+ const shapes = try world.worldShapes(allocator, .{
+ .color = options.color orelse theme.border,
+ .highlight = options.highlight,
+ .highlight_color = options.highlight_color orelse theme.accent,
+ });
+ defer world.freeShapes(allocator, shapes);
+
+ const win = options.window();
+ try canvas_mod.drawCanvas(allocator, s, .{
+ .shapes = shapes,
+ .x = win.x,
+ .y = win.y,
+ .background = options.background,
+ .grid = options.grid,
+ });
+}
+
+/// The country under a cell of a map drawn with these bounds.
+///
+/// Exposed so a caller can answer a hover as well as a click, and so the
+/// arithmetic that has to agree with the drawing lives in one place.
+pub fn countryAtCell(
+ column: usize,
+ row: usize,
+ width: usize,
+ height: usize,
+ options: WorldMapOptions,
+) ?CountryOutline {
+ const win = options.window();
+ const at = world.degreesAt(column, row, width, height, win.x, win.y) orelse return null;
+ return world.countryAt(at.lon, at.lat);
+}