From a59faacf495361b97b8b88221afd1b97e2230f94 Mon Sep 17 00:00:00 2001 From: kerthcet Date: Sun, 6 Sep 2026 23:27:40 +0100 Subject: [PATCH 1/6] Count the people a commit is co-authored by MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ky-hiverge belongs on the map — Cambridge, like kerthcet — and was not on it. The account has no commit and no pull request of its own in these repositories: what it has is five commits written with somebody else, where GitHub attributes the commit to one author and names the second in a `Co-authored-by:` trailer in the message. The trailer is in no field of the API's response, and this script read `author` and nothing else, so the second person was read as nobody. Not a corner case. Every project added to Awesome-LLMOps arrives as a pull request that inftyai-agent squash-merges, which makes that machine account the author — excluded here as a bot — and the person who did the work the co-author. So that work counted for nobody, while the same work in a repository whose merges a human presses counted in full, and about twenty of the people already on the map are Awesome-LLMOps submitters. This is one rule applied to both, not a new definition of contributor. A login is read from the trailer's address and from nowhere else, and only from `id+login@users.noreply.github.com`, the one form that names an account. The display name beside it is free text: `Kerry He` is `kerry-he` and looks guessable, `Se7en ` is `cr7258` and is not, and a name that happens to be a stranger's login would credit the stranger. The rest are reported at the end of the run — every one of them turned out to be somebody an ordinary commit or a noreply trailer elsewhere had already found. Logins are counted case-insensitively for the same reason: `googs1025` from the API and `86391540+googs1025@users.noreply` from a trailer were two contributors before. Three logins now 404 (renamed or deleted since their commit); they are skipped with a warning rather than counted, since there is no profile to place them by. 53 contributors becomes 72, 36 placed becomes 47, and the map goes from 23 marks to 29: London and Kuala Lumpur and Cleveland and Tampa and Romania and Italy appear, Bengaluru and Cambridge and Shanghai grow. Cambridge now reads `kerthcet, ky-hiverge`. One loss worth saying out loud: pranshuchittora writes "Bengaluru, IN", so with that alias learned there is a city in India, and a city prunes its country's fallback mark. SanjanShiv and VedantMadane, who say only "India", are now in the Asia share and on no card — the same trade the United States mark already makes. The six new dots displaced eleven labels, which are hand-placed again here: no label crosses another, sits on a dot that is not its own, or leaves the drawing, measured from the rendered page at 1440 and checked by eye at every mark. Paris now sits in the 15-unit gap between Berlin's dot and Italy's, London and Amsterdam stack above a cluster of five dots that no longer has room beside it, and New Zealand hangs below-left of its dot so that it stops reading as a second label for Melbourne's. --- data/contributors.json | 335 +++++++++++++++++++++++++++-------------- tools/contributors.mjs | 180 ++++++++++++++++++---- tools/locations.json | 83 ++++++++-- 3 files changed, 437 insertions(+), 161 deletions(-) diff --git a/data/contributors.json b/data/contributors.json index 43c9966..6618aaa 100644 --- a/data/contributors.json +++ b/data/contributors.json @@ -1,7 +1,7 @@ { - "generated": "2026-09-11", - "total": 56, - "located": 37, + "generated": "2026-09-06", + "total": 72, + "located": 47, "places": [ { "label": "Shanghai, China", @@ -14,7 +14,7 @@ "dy": 5, "anchor": "start" }, - "count": 6, + "count": 7, "people": [ { "login": "cr7258", @@ -36,6 +36,10 @@ "login": "rudeigerc", "avatar": "https://avatars.githubusercontent.com/u/18243819?v=4&s=64" }, + { + "login": "windsonsea", + "avatar": "https://avatars.githubusercontent.com/u/79828097?v=4&s=64" + }, { "login": "caozhuozi", "avatar": "https://avatars.githubusercontent.com/u/68834160?v=4&s=64" @@ -72,50 +76,49 @@ "more": 0 }, { - "label": "India", - "country": "India", - "lat": 22, - "lng": 79, - "countryOnly": true, - "short": "India", + "label": "Barcelona, Spain", + "country": "Spain", + "lat": 41.39, + "lng": 2.17, + "short": "Barcelona", "offset": { "dx": 0, - "dy": 23, - "anchor": "middle" + "dy": 22, + "anchor": "start" }, "count": 2, "people": [ { - "login": "SanjanShiv", - "avatar": "https://avatars.githubusercontent.com/u/90202905?v=4&s=64" + "login": "guillemwilly", + "avatar": "https://avatars.githubusercontent.com/u/161842218?v=4&s=64" }, { - "login": "VedantMadane", - "avatar": "https://avatars.githubusercontent.com/u/6527493?v=4&s=64" + "login": "vgmartinez", + "avatar": "https://avatars.githubusercontent.com/u/10614247?v=4&s=64" } ], "more": 0 }, { - "label": "Guangzhou, China", - "country": "China", - "lat": 23.13, - "lng": 113.26, - "short": "Guangzhou", + "label": "London, UK", + "country": "United Kingdom", + "lat": 51.51, + "lng": -0.13, + "short": "London", "offset": { - "dx": -9, - "dy": 20, - "anchor": "end" + "dx": 0, + "dy": -28, + "anchor": "middle" }, "count": 2, "people": [ { - "login": "kenwoodjw", - "avatar": "https://avatars.githubusercontent.com/u/10386710?v=4&s=64" + "login": "jaedmunt", + "avatar": "https://avatars.githubusercontent.com/u/76647805?v=4&s=64" }, { - "login": "Whitea029", - "avatar": "https://avatars.githubusercontent.com/u/145814986?v=4&s=64" + "login": "metrofun", + "avatar": "https://avatars.githubusercontent.com/u/946519?v=4&s=64" } ], "more": 0 @@ -145,66 +148,73 @@ "more": 0 }, { - "label": "Shenzhen, China", + "label": "Guangzhou, China", "country": "China", - "lat": 22.54, - "lng": 114.06, - "short": "Shenzhen", + "lat": 23.13, + "lng": 113.26, + "short": "Guangzhou", "offset": { - "dx": 12, - "dy": 11, - "anchor": "start" + "dx": -9, + "dy": 20, + "anchor": "end" }, "count": 2, "people": [ { - "login": "nayihz", - "avatar": "https://avatars.githubusercontent.com/u/65757763?v=4&s=64" + "login": "kenwoodjw", + "avatar": "https://avatars.githubusercontent.com/u/10386710?v=4&s=64" }, { - "login": "liangyuanpeng", - "avatar": "https://avatars.githubusercontent.com/u/28711504?v=4&s=64" + "login": "Whitea029", + "avatar": "https://avatars.githubusercontent.com/u/145814986?v=4&s=64" } ], "more": 0 }, { - "label": "South Africa", - "country": "South Africa", - "lat": -29, - "lng": 24.7, - "countryOnly": true, - "short": "South Africa", + "label": "Cambridge, UK", + "country": "United Kingdom", + "lat": 52.2, + "lng": 0.12, + "short": "Cambridge", "offset": { - "dx": 11, - "dy": 4, - "anchor": "start" + "dx": -11, + "dy": 15, + "anchor": "end" }, - "count": 1, + "count": 2, "people": [ { - "login": "AbdulDavids", - "avatar": "https://avatars.githubusercontent.com/u/125186956?v=4&s=64" + "login": "kerthcet", + "avatar": "https://avatars.githubusercontent.com/u/18364341?v=4&s=64" + }, + { + "login": "ky-hiverge", + "avatar": "https://avatars.githubusercontent.com/u/218585409?v=4&s=64" } ], "more": 0 }, { - "label": "Paris, France", - "country": "France", - "lat": 48.86, - "lng": 2.35, - "short": "Paris", + "label": "Shenzhen, China", + "country": "China", + "lat": 22.54, + "lng": 114.06, + "short": "Shenzhen", "offset": { - "dx": 17, - "dy": 28, + "dx": 12, + "dy": 11, "anchor": "start" }, - "count": 1, + "count": 2, "people": [ { - "login": "ThibautMelen", - "avatar": "https://avatars.githubusercontent.com/u/20891897?v=4&s=64" + "login": "nayihz", + "avatar": "https://avatars.githubusercontent.com/u/65757763?v=4&s=64" + }, + { + "login": "liangyuanpeng", + "avatar": "https://avatars.githubusercontent.com/u/28711504?v=4&s=64" } ], "more": 0 @@ -249,6 +259,27 @@ ], "more": 0 }, + { + "label": "Romania", + "country": "Romania", + "lat": 45.9, + "lng": 25, + "countryOnly": true, + "short": "Romania", + "offset": { + "dx": 11, + "dy": 4, + "anchor": "start" + }, + "count": 1, + "people": [ + { + "login": "damienen", + "avatar": "https://avatars.githubusercontent.com/u/44813558?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Toronto, Canada", "country": "Canada", @@ -276,9 +307,9 @@ "lng": -6.26, "short": "Dublin", "offset": { - "dx": 0, - "dy": -11, - "anchor": "middle" + "dx": -11, + "dy": 4, + "anchor": "end" }, "count": 1, "people": [ @@ -310,21 +341,21 @@ "more": 0 }, { - "label": "Cambridge, UK", - "country": "United Kingdom", - "lat": 52.2, - "lng": 0.12, - "short": "Cambridge", + "label": "Kuala Lumpur, Malaysia", + "country": "Malaysia", + "lat": 3.14, + "lng": 101.69, + "short": "Kuala Lumpur", "offset": { - "dx": -8, - "dy": 19, - "anchor": "end" + "dx": 11, + "dy": 4, + "anchor": "start" }, "count": 1, "people": [ { - "login": "kerthcet", - "avatar": "https://avatars.githubusercontent.com/u/18364341?v=4&s=64" + "login": "heiwen", + "avatar": "https://avatars.githubusercontent.com/u/3409733?v=4&s=64" } ], "more": 0 @@ -390,6 +421,47 @@ ], "more": 0 }, + { + "label": "Italy", + "country": "Italy", + "lat": 42.8, + "lng": 12.6, + "countryOnly": true, + "short": "Italy", + "offset": { + "dx": 11, + "dy": 9, + "anchor": "start" + }, + "count": 1, + "people": [ + { + "login": "nicoloboschi", + "avatar": "https://avatars.githubusercontent.com/u/23314389?v=4&s=64" + } + ], + "more": 0 + }, + { + "label": "Bengaluru, India", + "country": "India", + "lat": 12.97, + "lng": 77.59, + "short": "Bengaluru", + "offset": { + "dx": 0, + "dy": 19, + "anchor": "middle" + }, + "count": 1, + "people": [ + { + "login": "pranshuchittora", + "avatar": "https://avatars.githubusercontent.com/u/32242596?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Melbourne, Australia", "country": "Australia", @@ -417,9 +489,9 @@ "lng": -87.63, "short": "Chicago", "offset": { - "dx": 8, - "dy": 19, - "anchor": "start" + "dx": 0, + "dy": -17, + "anchor": "middle" }, "count": 1, "people": [ @@ -450,6 +522,26 @@ ], "more": 0 }, + { + "label": "Cleveland, USA", + "country": "United States", + "lat": 41.5, + "lng": -81.69, + "short": "Cleveland", + "offset": { + "dx": 11, + "dy": 12, + "anchor": "start" + }, + "count": 1, + "people": [ + { + "login": "santoshkumarradha", + "avatar": "https://avatars.githubusercontent.com/u/29346072?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Minsk, Belarus", "country": "Belarus", @@ -470,6 +562,26 @@ ], "more": 0 }, + { + "label": "Paris, France", + "country": "France", + "lat": 48.86, + "lng": 2.35, + "short": "Paris", + "offset": { + "dx": 11, + "dy": 8, + "anchor": "start" + }, + "count": 1, + "people": [ + { + "login": "ThibautMelen", + "avatar": "https://avatars.githubusercontent.com/u/20891897?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Palo Alto, USA", "country": "United States", @@ -491,84 +603,79 @@ "more": 0 }, { - "label": "Barcelona, Spain", - "country": "Spain", - "lat": 41.39, - "lng": 2.17, - "short": "Barcelona", + "label": "Amsterdam, Netherlands", + "country": "Netherlands", + "lat": 52.37, + "lng": 4.9, + "short": "Amsterdam", "offset": { - "dx": 0, - "dy": 22, + "dx": 11, + "dy": -25, "anchor": "start" }, "count": 1, "people": [ { - "login": "vgmartinez", - "avatar": "https://avatars.githubusercontent.com/u/10614247?v=4&s=64" + "login": "vivekchand", + "avatar": "https://avatars.githubusercontent.com/u/987905?v=4&s=64" } ], "more": 0 }, { - "label": "Amsterdam, Netherlands", - "country": "Netherlands", - "lat": 52.37, - "lng": 4.9, - "short": "Amsterdam", + "label": "Tampa, USA", + "country": "United States", + "lat": 27.95, + "lng": -82.46, + "short": "Tampa", "offset": { - "dx": 12, - "dy": 23, + "dx": 11, + "dy": 4, "anchor": "start" }, "count": 1, "people": [ { - "login": "vivekchand", - "avatar": "https://avatars.githubusercontent.com/u/987905?v=4&s=64" + "login": "zwright8", + "avatar": "https://avatars.githubusercontent.com/u/24713584?v=4&s=64" } ], "more": 0 } ], - "cities": 21, - "countryCount": 16, + "cities": 26, + "countryCount": 18, "regions": [ { "name": "Asia", - "count": 15, - "percent": 27 + "count": 18, + "percent": 25 }, { - "name": "North America", - "count": 9, - "percent": 16 + "name": "Europe", + "count": 13, + "percent": 18 }, { - "name": "Europe", - "count": 7, - "percent": 12 + "name": "North America", + "count": 11, + "percent": 15 }, { "name": "Oceania", "count": 3, - "percent": 5 + "percent": 4 }, { "name": "South America", "count": 2, - "percent": 4 - }, - { - "name": "Africa", - "count": 1, - "percent": 2 + "percent": 3 }, { "name": "Others", - "count": 19, + "count": 25, "note": "Contributors whose GitHub profile does not say where they are.", - "percent": 34 + "percent": 35 } ] } diff --git a/tools/contributors.mjs b/tools/contributors.mjs index cfe90da..f836071 100644 --- a/tools/contributors.mjs +++ b/tools/contributors.mjs @@ -70,7 +70,11 @@ if (!token) { console.warn("warning: no GITHUB_TOKEN set, expect to be rate limited\n"); } -async function request(path) { +// `missing`: what to return for a 404 instead of stopping the run. Only the profile +// lookups pass it, and only because a login read out of a commit is a login as it was +// on the day of the commit — an account renamed or deleted since is a 404 and is not a +// reason to leave the whole map stale. +async function request(path, { missing } = {}) { const headers = { Accept: "application/vnd.github+json", "User-Agent": "inftyai-website", @@ -81,14 +85,16 @@ async function request(path) { path.startsWith("https://") ? path : `https://api.github.com/${path}`, { headers }, ); + if (response.status === 404 && missing !== undefined) return null; if (!response.ok) { exit(`GET ${path} responded ${response.status} ${response.statusText}`); } return response; } -async function github(path) { - return (await request(path)).json(); +async function github(path, options) { + const response = await request(path, options); + return response === null ? options.missing : response.json(); } // Every page, not just the first. A repository with more than one page of commits @@ -123,33 +129,108 @@ async function githubAll(path) { // The cost is pagination — about twenty requests across the seven projects instead of // seven — which is nothing against the hourly limit, and the per-contributor profile // lookups below already dwarf it. -const logins = new Set(); -// How much each person has done, for the panel the legend opens on hover. Both come -// out of the commit pages that are already being read for the headcount, so the -// ranking costs no extra requests — which is the only reason it is commit-based -// rather than something the API would have to be asked for separately. -const commitsBy = new Map(); -const reposBy = new Map(); +// +// Everyone with a commit, keyed by their login lowercased: GitHub logins are +// case-insensitive, and the same person arrives spelled two ways — `googs1025` from +// the API's author field and `CYJiang <86391540+googs1025@users.noreply.github.com>` +// from a co-author trailer. Keyed by the login itself, that was two contributors. +// +// How much each has done travels with them, for the order the cards list faces in. +// Both numbers come out of the commit pages that are already being read for the +// headcount, so the ranking costs no extra requests — which is the only reason it is +// commit-based rather than something the API would have to be asked for separately. +const contributors = new Map(); + +// A commit's `author` is the one person GitHub attributed it to. Where two people +// wrote it, the second is a `Co-authored-by:` trailer in the message and appears in no +// field of the API's response — so reading `author` alone silently dropped them. +// +// Not a corner case here. Every project added to Awesome-LLMOps arrives as a pull +// request that inftyai-agent squash-merges, which makes that machine account the +// author and the person who did the work the co-author. They were counted as nobody, +// while the same contribution to a repo whose merges a human presses counted as a +// contributor — and around twenty of the people already on the map are Awesome-LLMOps +// submitters, so the map was applying two rules to one kind of work. +const COAUTHOR = /^co-authored-by:\s*(.*?)\s*<([^>]+)>\s*$/gim; + +// The only address that names a GitHub account rather than a mailbox: +// `id+login@users.noreply.github.com`, or the older `login@…`. The login is taken from +// there and nowhere else — the display name beside it is free text, and guessing from +// it would be wrong in both directions: `Kerry He` is the account `kerry-he`, which +// looks guessable, and `Se7en ` is `cr7258`, which does not. A +// name that happens to be somebody else's login would credit a stranger. Unresolved +// trailers are reported at the end instead, and in practice they are people a +// noreply trailer or an ordinary commit elsewhere has already found. +// +// Loose about the login's own shape, because the profile lookup below is what decides +// whether an account exists. +const NOREPLY = /^(?:\d+\+)?([\w-]+)@users\.noreply\.github\.com$/i; + +// Co-authors that are not people and have no account to look for: the assistants that +// sign commits in these repositories. Skipped rather than reported, so the report of +// unresolved trailers stays a list of contributors to look into rather than one line +// of noise per model release. +const NOT_PEOPLE = new Set(["noreply@anthropic.com"]); + +// Trailers naming somebody no login could be read from, for the report at the end. +const unresolved = new Map(); + +// Adds one commit to somebody's total, and returns the spelling of their login that +// the rest of the run uses. Null for anyone who is not a person to count. +function credit(login, repo) { + const key = login.toLowerCase(); + // `[bot]` is the suffix GitHub gives every App's account, and EXCLUDE covers the + // machine accounts that are ordinary users as far as the API is concerned. + if (key.endsWith("[bot]") || EXCLUDE.has(key)) return null; + let entry = contributors.get(key); + if (!entry) { + entry = { login, commits: 0, repos: new Set() }; + contributors.set(key, entry); + } + entry.commits += 1; + entry.repos.add(repo); + return entry.login; +} + for (const repo of repos) { const commits = await githubAll(`repos/${repo}/commits?per_page=100`); const seen = new Set(); let unattributed = 0; - for (const { author } of commits) { - // No linked account: a commit whose author email belongs to no GitHub user. - // Nothing to look a location up for, so it can only be counted and reported. - if (!author) { - unattributed += 1; - continue; + for (const commit of commits) { + const { author } = commit; + // Everyone this commit is the work of: the author GitHub linked to an account, + // then every co-author a login can be read from. A Map keyed on the lowercased + // login, so that the common `Signed-off-by` plus `Co-authored-by` pair naming the + // author again is one commit for them rather than two. + const writers = new Map(); + const writer = (login) => { + if (!writers.has(login.toLowerCase())) + writers.set(login.toLowerCase(), login); + }; + // `type` is "Bot" for GitHub Apps. A missing author is a commit whose author + // email belongs to no GitHub user — there is nothing to look a location up for, + // and a co-author trailer may yet name somebody, so it is left to the count below. + if (author?.type === "User") writer(author.login); + for (const [, name, email] of commit.commit.message.matchAll(COAUTHOR)) { + if (NOT_PEOPLE.has(email.toLowerCase())) continue; + const login = NOREPLY.exec(email)?.[1]; + if (login) { + writer(login); + continue; + } + if (name.toLowerCase().endsWith("[bot]")) continue; + const trailer = `${name} <${email}>`; + unresolved.set(trailer, (unresolved.get(trailer) ?? 0) + 1); } - // `type` is "Bot" for GitHub Apps; EXCLUDE covers machine accounts that are - // ordinary users as far as the API is concerned. - if (author.type !== "User") continue; - if (EXCLUDE.has(author.login.toLowerCase())) continue; - seen.add(author.login); - logins.add(author.login); - commitsBy.set(author.login, (commitsBy.get(author.login) ?? 0) + 1); - if (!reposBy.has(author.login)) reposBy.set(author.login, new Set()); - reposBy.get(author.login).add(repo); + const credited = [...writers.values()] + .map((login) => credit(login, repo)) + .filter((login) => login !== null); + // A commit whose author email belongs to no GitHub user and whose trailers named + // nobody either: there is no account to look a location up for, so all it can be + // is counted and reported. A bot's commit is not this — it has an account, and + // that account is deliberately not a person. + if (!author && !credited.length) unattributed += 1; + for (const login of credited) seen.add(login); } console.log( `${repo}: ${seen.size} contributors across ${commits.length} commits` + @@ -179,8 +260,24 @@ let located = 0; // from this, matched back to its place below. const people = []; -for (const login of [...logins].sort()) { - const { location, avatar_url: avatar } = await github(`users/${login}`); +for (const [id, entry] of [...contributors].sort(([a], [b]) => + a.localeCompare(b), +)) { + const { login, commits, repos } = entry; + const profile = await github(`users/${login}`, { missing: null }); + + // A login is a login as it was on the day of the commit it was read from, and three + // of the ones in these repositories now 404: renamed or deleted since. Dropped + // rather than counted, because there is no profile to place them by and no avatar to + // show — all they could add is another line to the "Others" share. + if (profile === null) { + contributors.delete(id); + console.warn( + `warning: no account for ${login}, named in a commit — skipping`, + ); + continue; + } + const { location, avatar_url: avatar } = profile; // `place` stays null until one is matched, and a null is somebody no card will // ever list: no location, a location the table has not learned, and a known @@ -195,8 +292,8 @@ for (const login of [...logins].sort()) { // Only the ranking, and only until the sort below — neither number reaches // data/contributors.json. The card shows a face and a handle; a commit count // beside them invited a reading of the map as a scoreboard. - commits: commitsBy.get(login) ?? 0, - repos: reposBy.get(login)?.size ?? 0, + commits, + repos: repos.size, place: null, }; people.push(person); @@ -392,7 +489,7 @@ const ranked = [...regionCounts] // `note` is the only explanation the row gets — a visible one was tried and cut. // It reaches the tooltip and the accessibility tree, and the label alone would // otherwise suggest these people are somewhere else rather than unstated. -const unlocated = logins.size - located; +const unlocated = contributors.size - located; if (unlocated > 0) { ranked.push({ name: "Others", @@ -403,14 +500,14 @@ if (unlocated > 0) { const shares = share( ranked.map((r) => r.count), - logins.size, + contributors.size, ); const output = { // Not rendered anywhere — it is here so that a reader of the file, or of a // diff, can tell how old the snapshot is without going through git log. generated: new Date().toISOString().slice(0, 10), - total: logins.size, + total: contributors.size, located, // One mark each on the map. Largest first, so the template draws the big dots // before the small ones and a city of one is never hidden underneath a city of @@ -504,6 +601,25 @@ if (unmatched.size) { } } +// Co-authors written with an ordinary email address, which names a mailbox and not an +// account. Reported rather than resolved, for the reasons NOREPLY carries: there is no +// rule that turns "Se7en " into `cr7258`, and a guess from the name +// would sooner or later credit a stranger. +// +// Not a list of people missing from the map. Most of these are somebody an ordinary +// commit or a noreply trailer elsewhere has already found — the list is here so that +// one who is not can be noticed, and the answer to that is a commit of their own, not +// a table of email addresses to keep. +if (unresolved.size) { + console.log( + `\n${unresolved.size} co-author(s) named by email rather than by account, so not` + + ` counted from those commits (they may be counted from others):`, + ); + for (const [trailer, count] of [...unresolved].sort((a, b) => b[1] - a[1])) { + console.log(` ${trailer}${count > 1 ? ` (x${count})` : ""}`); + } +} + function exit(message) { console.error(`error: ${message}`); process.exit(1); diff --git a/tools/locations.json b/tools/locations.json index a2d0f26..ad51bf6 100644 --- a/tools/locations.json +++ b/tools/locations.json @@ -93,6 +93,12 @@ "lat": 1.35, "lng": 103.82 }, + "kuala lumpur": { + "label": "Kuala Lumpur, Malaysia", + "country": "Malaysia", + "lat": 3.14, + "lng": 101.69 + }, "bengaluru": { "label": "Bengaluru, India", "country": "India", @@ -375,6 +381,12 @@ "lat": 41.88, "lng": -87.63 }, + "cleveland": { + "label": "Cleveland, USA", + "country": "United States", + "lat": 41.5, + "lng": -81.69 + }, "boston": { "label": "Boston, USA", "country": "United States", @@ -399,6 +411,12 @@ "lat": 33.75, "lng": -84.39 }, + "tampa": { + "label": "Tampa, USA", + "country": "United States", + "lat": 27.95, + "lng": -82.46 + }, "toronto": { "label": "Toronto, Canada", "country": "Canada", @@ -673,6 +691,13 @@ "lng": 19.4, "countryOnly": true }, + "romania": { + "label": "Romania", + "country": "Romania", + "lat": 45.9, + "lng": 25, + "countryOnly": true + }, "sweden": { "label": "Sweden", "country": "Sweden", @@ -792,9 +817,12 @@ "seoul, south korea": "seoul", "seoul, korea": "seoul", "korea": "south korea", + "kuala lumpur, malaysia": "kuala lumpur", + "kl": "kuala lumpur", "bangalore": "bengaluru", "bangalore, india": "bengaluru", "bengaluru, india": "bengaluru", + "bengaluru, in": "bengaluru", "mumbai, india": "mumbai", "delhi": "new delhi", "delhi, india": "new delhi", @@ -855,6 +883,8 @@ "denver, co": "denver", "austin, tx": "austin", "chicago, il": "chicago", + "cleveland, oh": "cleveland", + "cleveland, ohio": "cleveland", "boston, ma": "boston", "cambridge, ma": "boston", "new york, ny": "new york", @@ -866,6 +896,8 @@ "washington, d.c.": "washington, dc", "dc": "washington, dc", "atlanta, ga": "atlanta", + "tampa, fl": "tampa", + "tampa, florida": "tampa", "toronto, canada": "toronto", "toronto, on": "toronto", "montreal, canada": "montreal", @@ -937,6 +969,7 @@ "Israel": "Asia", "Italy": "Europe", "Japan": "Asia", + "Malaysia": "Asia", "Kenya": "Africa", "Mexico": "North America", "Morocco": "Africa", @@ -998,8 +1031,8 @@ "anchor": "middle" }, "Cambridge, UK": { - "dx": -8, - "dy": 19, + "dx": -11, + "dy": 15, "anchor": "end" }, "Berlin, Germany": { @@ -1012,15 +1045,10 @@ "dy": 4, "anchor": "end" }, - "New Zealand": { - "dx": -8, - "dy": 19, - "anchor": "end" - }, "Dublin, Ireland": { - "dx": 0, - "dy": -11, - "anchor": "middle" + "dx": -11, + "dy": 4, + "anchor": "end" }, "Palo Alto, USA": { "dx": -12, @@ -1028,8 +1056,8 @@ "anchor": "end" }, "Amsterdam, Netherlands": { - "dx": 12, - "dy": 23, + "dx": 11, + "dy": -25, "anchor": "start" }, "Barcelona, Spain": { @@ -1043,13 +1071,38 @@ "anchor": "start" }, "Paris, France": { - "dx": 17, - "dy": 28, + "dx": 11, + "dy": 8, "anchor": "start" }, "Chicago, USA": { - "dx": 8, + "dx": 0, + "dy": -17, + "anchor": "middle" + }, + "London, UK": { + "dx": 0, + "dy": -28, + "anchor": "middle" + }, + "New Zealand": { + "dx": -8, + "dy": 19, + "anchor": "end" + }, + "Italy": { + "dx": 11, + "dy": 9, + "anchor": "start" + }, + "Bengaluru, India": { + "dx": 0, "dy": 19, + "anchor": "middle" + }, + "Cleveland, USA": { + "dx": 11, + "dy": 12, "anchor": "start" } } From 524f8bf2a904bbe43dc0a9b93a9600ead78908f3 Mon Sep 17 00:00:00 2001 From: kerthcet Date: Sun, 6 Sep 2026 23:59:47 +0100 Subject: [PATCH 2/6] Keep the mark a country's own contributors are on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A country-level mark was a fallback that lost its place to the first city in the same country: "a blob over China beside five Chinese cities reads as a sixth city that nobody lives in", which is a fair thing to say about the blob and the wrong thing to weigh against. The mark was never the only thing dropped. The people it stood for went with it — off the card that names them, into a region percentage — so writing "India" rather than "Bengaluru" was enough to be counted and not shown. Four of the forty-seven placed contributors were in that position: awdemos and twinsgeeks under the United States, SanjanShiv and VedantMadane under India. The contributors are the baseline, so nothing that has somebody under it is dropped for the drawing's sake. 29 marks becomes 31, and the counts on the marks now add up to `located` exactly — 47 of 47 people on a card, where it was 43. Nothing else moves: the shares were always computed from every matched place rather than from the surviving ones, so the legend is unchanged to the point. What is left of the blob problem is a label, and the label is the answer to it. "India" beside a dot in the middle of India is not a city, and the card it opens lists the two people who wrote it that way. Four labels are re-placed around the two new dots: India to the left of its own dot with Bengaluru moved below its own, United States over Nevada with its dot 11 units to the right, and San Francisco out over the Pacific, since its old spot above the dot ran into the new one in Kansas. The report of orphaned contributors goes with the pruning — it existed to make this loss visible, and there is no longer such a thing. Measured after: 31 targets over 31 dots, each centred on its own to within 0.06px, every card opening on top with both avatars loaded and none crossing a viewport edge at 320, 390, 768 or 1440, and all closed again at rest. No label crosses another, sits on a dot that is not its own, or leaves the drawing. --- data/contributors.json | 56 +++++++++++++++++++++++++++++++++-- tools/contributors.mjs | 67 +++++++++++++++--------------------------- tools/locations.json | 17 +++++++---- 3 files changed, 88 insertions(+), 52 deletions(-) diff --git a/data/contributors.json b/data/contributors.json index 6618aaa..53b89a0 100644 --- a/data/contributors.json +++ b/data/contributors.json @@ -54,9 +54,9 @@ "lng": -122.42, "short": "San Francisco", "offset": { - "dx": 0, - "dy": -14, - "anchor": "start" + "dx": -11, + "dy": 4, + "anchor": "end" }, "count": 3, "people": [ @@ -75,6 +75,31 @@ ], "more": 0 }, + { + "label": "United States", + "country": "United States", + "lat": 39.5, + "lng": -98.35, + "countryOnly": true, + "short": "United States", + "offset": { + "dx": -11, + "dy": -8, + "anchor": "end" + }, + "count": 2, + "people": [ + { + "login": "awdemos", + "avatar": "https://avatars.githubusercontent.com/u/159745380?v=4&s=64" + }, + { + "login": "twinsgeeks", + "avatar": "https://avatars.githubusercontent.com/u/261838102?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Barcelona, Spain", "country": "Spain", @@ -219,6 +244,31 @@ ], "more": 0 }, + { + "label": "India", + "country": "India", + "lat": 22, + "lng": 79, + "countryOnly": true, + "short": "India", + "offset": { + "dx": -11, + "dy": 4, + "anchor": "end" + }, + "count": 2, + "people": [ + { + "login": "SanjanShiv", + "avatar": "https://avatars.githubusercontent.com/u/90202905?v=4&s=64" + }, + { + "login": "VedantMadane", + "avatar": "https://avatars.githubusercontent.com/u/6527493?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Sao Paulo, Brazil", "country": "Brazil", diff --git a/tools/contributors.mjs b/tools/contributors.mjs index f836071..4380620 100644 --- a/tools/contributors.mjs +++ b/tools/contributors.mjs @@ -313,8 +313,8 @@ for (const [id, entry] of [...contributors].sort(([a], [b]) => if (place === null) continue; // The mark whose card will list them. The label, because that is what identifies - // one mark on the map — and note that it can be a country-level fallback, which - // may not survive the pruning below. + // one mark on the map — and it can be a country-level fallback, which is a mark + // like any other and gets a card like any other. person.place = place.label; const existing = places.get(place.label); @@ -334,29 +334,22 @@ for (const [id, entry] of [...contributors].sort(([a], [b]) => located += 1; } -// A country-level place is a fallback: it says "somewhere in this country", which -// is worth drawing only while nothing better is known about that country. Once a -// city there is on the map, the country's own mark goes — a blob over China beside -// five Chinese cities reads as a sixth city that nobody lives in. +// Every mark that was matched is drawn, country-level fallbacks included. // -// Snapshot first, because everyone a dropped mark stood for still belongs to a -// region: their country is known, only their position is not. Everything derived -// below counts `matched` rather than `places`, and that is what holds the -// percentages still while the map loses a mark. +// This used to prune them: a country mark said "somewhere in this country", and once +// a city there was on the map the country's own mark went, on the grounds that a blob +// over China beside five Chinese cities reads as a sixth city that nobody lives in. +// That is true of the blob and it was the wrong thing to weigh. The mark was not the +// only thing dropped — the people it stood for went with it, off the card that names +// them and into a region percentage, so writing "India" instead of "Bengaluru" was +// enough to be counted and not shown. The contributors are the baseline here: a mark +// exists to name whoever is under it, so no mark that has somebody under it is +// dropped for the drawing's sake. // -// Data-dependent, so it is reversible: if every contributor in a Chinese city left -// and only a bare "China" remained, the country mark would come back. That is the -// rule working, but it does mean the map can change shape from the contributor list -// alone, with no edit to this file. +// What is left of the blob problem is a label, and the label is the answer to it: +// "India" beside a dot in the middle of India is not a city, and the card it opens +// lists the people who wrote it that way. const matched = [...places.values()]; -const mappedCities = new Set( - matched.filter((place) => !place.countryOnly).map((place) => place.country), -); -for (const place of matched) { - if (place.countryOnly && mappedCities.has(place.country)) { - places.delete(place.label); - } -} // The summary beside the map is by region, not by city or country. Ten cities was // a longer list than the map has dots worth explaining, and five of them were @@ -426,22 +419,10 @@ for (const place of places.values()) { } } -// Everyone the table placed in a country whose fallback mark was then pruned: located, -// counted in their region's share, and on no dot — so no card lists them. Reported -// because it is otherwise invisible, and because the fix is a line in -// tools/locations.json for the city they are actually in. -const orphaned = people.filter( - (person) => person.place && !places.has(person.place), -); -if (orphaned.length) { - console.log( - `\n${orphaned.length} contributor(s) matched a country-level mark that a city has` + - ` since replaced, so no card lists them:`, - ); - for (const person of orphaned) { - console.log(` ${person.login} (${person.place})`); - } -} +// Nobody the table placed is off the map any more: the report that used to stand here +// listed the contributors whose country mark a city had replaced, and there is no +// longer such a thing. Every located person is on the mark their `place` names, which +// the check above proves mark by mark. // Shares of every contributor, as whole numbers that still add to 100. // @@ -511,7 +492,8 @@ const output = { located, // One mark each on the map. Largest first, so the template draws the big dots // before the small ones and a city of one is never hidden underneath a city of - // six. Country-level fallbacks that have yielded to a city are already gone. + // six. A country-level fallback is one of these marks, drawn wherever somebody + // gave their country and no more than that. // // `people` is the card the mark opens: a handle and an avatar each, capped at ten // because the card is a panel floating over the map and a mark of thirty would cover @@ -535,10 +517,9 @@ const output = { // fallback is a mark too. Only used by the map's accessible name, which called // India a city before this existed. cities: matched.filter((place) => !place.countryOnly).length, - // How many countries the located contributors are in — from `matched`, so a - // country whose fallback mark was dropped still counts. Not shown, but it is - // what the map's accessible name says, being more use to a screen reader than - // "4 regions". + // How many countries the located contributors are in, which is fewer than there + // are marks: six of these are American. Not shown, but it is what the map's + // accessible name says, being more use to a screen reader than "4 regions". countryCount: new Set(matched.map((p) => p.country)).size, // Regions plus the "Others" remainder: one row each in the summary. `count` is // not rendered — `percent` is — but it stays so the file can be checked against diff --git a/tools/locations.json b/tools/locations.json index ad51bf6..c1d2de9 100644 --- a/tools/locations.json +++ b/tools/locations.json @@ -1026,9 +1026,9 @@ "anchor": "end" }, "India": { - "dx": 0, - "dy": 23, - "anchor": "middle" + "dx": -11, + "dy": 4, + "anchor": "end" }, "Cambridge, UK": { "dx": -11, @@ -1066,9 +1066,9 @@ "anchor": "start" }, "San Francisco, USA": { - "dx": 0, - "dy": -14, - "anchor": "start" + "dx": -11, + "dy": 4, + "anchor": "end" }, "Paris, France": { "dx": 11, @@ -1104,6 +1104,11 @@ "dx": 11, "dy": 12, "anchor": "start" + }, + "United States": { + "dx": -11, + "dy": -8, + "anchor": "end" } } } From 4bc0258a68a730d00054125d383908d7258224ef Mon Sep 17 00:00:00 2001 From: kerthcet Date: Fri, 11 Sep 2026 11:31:47 +0100 Subject: [PATCH 3/6] Put Limassol on the map, and refresh it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generator's report of locations it cannot place named one: "limassol", which is a city in Cyprus and now an entry with the rest. Cyprus is filed under Asia, which is where the UN geoscheme puts it and where Turkey and Israel already are in the region table. Refreshed with it: 48 of 75 contributors placed, in 27 cities across 20 countries, on 33 marks. Two are new since the last run — South Africa and Limassol — and the report is clean apart from three names with no GitHub account behind them, which nothing here can look a location up for. Cambridge drops from two contributors to one: ky-hiverge has cleared the Location field on their profile, and an empty location is not something this can place. They are still counted among the 75. Co-Authored-By: Claude Opus 5 --- data/contributors.json | 110 ++++++++++++++++++++++++++++------------- tools/locations.json | 8 +++ 2 files changed, 84 insertions(+), 34 deletions(-) diff --git a/data/contributors.json b/data/contributors.json index 53b89a0..c7cfb0b 100644 --- a/data/contributors.json +++ b/data/contributors.json @@ -1,7 +1,7 @@ { - "generated": "2026-09-06", - "total": 72, - "located": 47, + "generated": "2026-09-11", + "total": 75, + "located": 48, "places": [ { "label": "Shanghai, China", @@ -196,30 +196,6 @@ ], "more": 0 }, - { - "label": "Cambridge, UK", - "country": "United Kingdom", - "lat": 52.2, - "lng": 0.12, - "short": "Cambridge", - "offset": { - "dx": -11, - "dy": 15, - "anchor": "end" - }, - "count": 2, - "people": [ - { - "login": "kerthcet", - "avatar": "https://avatars.githubusercontent.com/u/18364341?v=4&s=64" - }, - { - "login": "ky-hiverge", - "avatar": "https://avatars.githubusercontent.com/u/218585409?v=4&s=64" - } - ], - "more": 0 - }, { "label": "Shenzhen, China", "country": "China", @@ -269,6 +245,27 @@ ], "more": 0 }, + { + "label": "South Africa", + "country": "South Africa", + "lat": -29, + "lng": 24.7, + "countryOnly": true, + "short": "South Africa", + "offset": { + "dx": 11, + "dy": 4, + "anchor": "start" + }, + "count": 1, + "people": [ + { + "login": "AbdulDavids", + "avatar": "https://avatars.githubusercontent.com/u/125186956?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Sao Paulo, Brazil", "country": "Brazil", @@ -410,6 +407,26 @@ ], "more": 0 }, + { + "label": "Cambridge, UK", + "country": "United Kingdom", + "lat": 52.2, + "lng": 0.12, + "short": "Cambridge", + "offset": { + "dx": -11, + "dy": 15, + "anchor": "end" + }, + "count": 1, + "people": [ + { + "login": "kerthcet", + "avatar": "https://avatars.githubusercontent.com/u/18364341?v=4&s=64" + } + ], + "more": 0 + }, { "label": "New Zealand", "country": "New Zealand", @@ -471,6 +488,26 @@ ], "more": 0 }, + { + "label": "Limassol, Cyprus", + "country": "Cyprus", + "lat": 34.71, + "lng": 33.02, + "short": "Limassol", + "offset": { + "dx": 11, + "dy": 4, + "anchor": "start" + }, + "count": 1, + "people": [ + { + "login": "nadyyym", + "avatar": "https://avatars.githubusercontent.com/u/96404149?v=4&s=64" + } + ], + "more": 0 + }, { "label": "Italy", "country": "Italy", @@ -693,18 +730,18 @@ "more": 0 } ], - "cities": 26, - "countryCount": 18, + "cities": 27, + "countryCount": 20, "regions": [ { "name": "Asia", - "count": 18, + "count": 19, "percent": 25 }, { "name": "Europe", - "count": 13, - "percent": 18 + "count": 12, + "percent": 16 }, { "name": "North America", @@ -721,11 +758,16 @@ "count": 2, "percent": 3 }, + { + "name": "Africa", + "count": 1, + "percent": 1 + }, { "name": "Others", - "count": 25, + "count": 27, "note": "Contributors whose GitHub profile does not say where they are.", - "percent": 35 + "percent": 36 } ] } diff --git a/tools/locations.json b/tools/locations.json index c1d2de9..0db9fc6 100644 --- a/tools/locations.json +++ b/tools/locations.json @@ -171,6 +171,12 @@ "lat": 41.01, "lng": 28.98 }, + "limassol": { + "label": "Limassol, Cyprus", + "country": "Cyprus", + "lat": 34.71, + "lng": 33.02 + }, "london": { "label": "London, UK", "country": "United Kingdom", @@ -835,6 +841,7 @@ "dubai, uae": "dubai", "tel aviv, israel": "tel aviv", "istanbul, turkey": "istanbul", + "limassol, cyprus": "limassol", "london, uk": "london", "london, england": "london", "england": "united kingdom", @@ -992,6 +999,7 @@ "Taiwan": "Asia", "Thailand": "Asia", "Turkey": "Asia", + "Cyprus": "Asia", "Ukraine": "Europe", "United Arab Emirates": "Asia", "United Kingdom": "Europe", From 52badfab5be41053d9a7b8abad9faee2f0cdc4d6 Mon Sep 17 00:00:00 2001 From: kerthcet Date: Fri, 11 Sep 2026 11:33:00 +0100 Subject: [PATCH 4/6] Give the phone gap the room the seventh face needs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shanghai gained a contributor in the refresh, which is 28px more card: 249px against a 109px map at 320px wide, hanging 56px past its bottom edge where the 3.5rem gap covered 42px. The card's edge was back over the legend's "Asia" row by 6px. 4.5rem, measured the same way — 9px above the legend's box — and the comment now carries what moves it: 12px per face, 7rem for a full card of ten. That is spacing a 109px map cannot carry, so the note says what has to give if it comes to it. Co-Authored-By: Claude Opus 5 --- assets/scss/common/_custom.scss | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/assets/scss/common/_custom.scss b/assets/scss/common/_custom.scss index 24f706a..c69f63e 100644 --- a/assets/scss/common/_custom.scss +++ b/assets/scss/common/_custom.scss @@ -374,15 +374,21 @@ $map-pulse-alpha: 0.18; align-items: center; // Room for a card to open into rather than separation, which is what this is for at // every other width. Stacked on a phone the map is shorter than the cards standing on - // it: 109px tall at 320px wide, where Shanghai's six faces make a 221px card. A card + // it: 109px tall at 320px wide, where Shanghai's seven faces make a 249px card. A card // is centred on its dot, so one in the lower half of a map that short hangs past its - // bottom edge — Shanghai's by 42px, which over a 2rem gap laid the card's bottom edge + // bottom edge — Shanghai's by 56px, which over a 2rem gap laid the card's bottom edge // and its shadow across the legend's "Asia" row. // - // 3.5rem clears it: at 320px the widest card the data makes stops 9px above the - // legend's box and 14px above the first row's text. The separation the two elements - // actually want is the 2rem below. - gap: 3.5rem; + // 4.5rem clears it: at 320px the tallest card stops 9px above the legend's box and + // 14px above the first row's text. The separation the two elements actually want is + // the 2rem below. + // + // It is measured against the data, so it is worth knowing what moves it: every face + // the tallest card gains is 12px more overhang here (3.5rem was enough for six of + // them), and the list stops at ten and an "and N more" line, which would want 7rem. + // A gap that big under a 109px map is spacing that reads as a mistake, so if the map + // ever needs it, the card is what should give — not this. + gap: 4.5rem; } // From `sm` up the container has a fixed width, so the map stops shrinking: it is 175px From 7bfa53e9367e021f15164686fd107f3b7eeb3be7 Mon Sep 17 00:00:00 2001 From: kerthcet Date: Fri, 11 Sep 2026 15:09:09 +0100 Subject: [PATCH 5/6] Shade the country, dot the city MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The map had one kind of mark for two different claims. A contributor who gave "San Francisco" and one who gave "United States" both became a dot, and the second one landed on a national centroid — 39.5N 98.35W, a field in Kansas — drawn exactly like a city 500 units away that is genuinely known. So the two claims are now drawn differently: every country anybody is in is filled whole, and the cities inside it keep their dots on top. The shade says somewhere in here, the dot says here. tools/worldmap.mjs takes its geometry from world-atlas' countries-110m.json instead of land-110m.json. Same coastline, and this file carries the land silhouette and the per-country outlines built from one shared set of arcs — two files would have simplified the same coast twice, and disagreements of a pixel would have shown as a fringe of unshaded land along every shaded coast. It emits an outline for every country tools/locations.json can place anybody in, not the countries on the map today: data/contributors.json is regenerated on every contributor change, and this file would otherwise need rebuilding over the network to catch up. Fifty-three outlines, 19 KB in a file nothing downloads; the page carries the twenty that are used, which is 10 KB of path. Three things follow for a country-level mark. Its dot goes, and so does its pulse — a ring growing out of open country with no mark at its centre reads as an artefact. And its name is only written where the country is the only thing there: "United States" beside San Francisco, Palo Alto, Los Angeles, Chicago, Cleveland and Tampa was a label for the shape those six dots already stand on, and it was spending room in the most crowded corner of the map. Romania, Italy, South Africa and New Zealand — the four countries with no city on the map — keep theirs. The marks themselves stay: those contributors gave a country and no city, so the card that opens on the country's centre is the only place their names appear. The shade is a darker neutral, not a tint of the mark's colour, which is where this started at 35% alpha. The trouble was never the strength: North America, China, India and Australia are all shaded, so the largest warm shapes on the map were the ones carrying the vaguest information. $map-region is the land's own hue at 84% lightness against its 93%, so the dots stay the only colour on the map. 86% was tried first and lost Romania, Italy, Ireland and Cyprus entirely, which is where the shade is the only thing saying anybody is there. Each country also takes the hairline of page background the dots use, for the same reason the dots have it: without it the United States and Canada fused into one mass from the Arctic to Mexico, China and India into another, and eight European countries into a blob — a map saying "this continent" where the data says which countries. 0.8 units against the dots' 2.5, because this is a border and not a keyline. --- assets/scss/common/_custom.scss | 53 +++++++++++++++++++++- data/worldmap.json | 59 ++++++++++++++++++++++++- hugo_stats.json | 1 + layouts/_default/home.html | 77 +++++++++++++++++++++++++++++++- tools/worldmap.mjs | 78 +++++++++++++++++++++++++++++---- 5 files changed, 254 insertions(+), 14 deletions(-) diff --git a/assets/scss/common/_custom.scss b/assets/scss/common/_custom.scss index c69f63e..4f43463 100644 --- a/assets/scss/common/_custom.scss +++ b/assets/scss/common/_custom.scss @@ -341,13 +341,38 @@ $feature-gap: 1.25rem; // smudges in the land. // // The cost is contrast, and it is worth stating plainly: 2.2:1 against the white -// keyline that rings each dot, 1.9:1 against the land. WCAG 2.1 asks 3:1 of a +// keyline that rings each dot, 1.9:1 against the land — 1.5:1 where the dot stands +// on a shaded country, which is where most of them stand. WCAG 2.1 asks 3:1 of a // graphic needed to understand the content, and this no longer meets it. What makes // that a considered trade rather than an oversight is that nothing here is only on // the map: every city is named in the card that opens on its dot, along with the // people who are there, and the region shares are the text list beside it. Going // back to 72% (#E47351) restores 3.1:1 on the keyline. +// +// The keyline is what keeps the dots countable over the shade, and it gains what the +// fill loses: white on a shaded country is 1.5:1 against 1.17:1 on bare land. $map-land: #e9edf2; +// The countries somebody is in, filled whole. The land's own colour a step darker +// rather than a colour of its own, which is what this started as — the mark's hue at +// 35%, and the trouble was never the strength but that half the land ended up in the +// accent family. North America, China, India and Australia are all shaded, so the +// largest warm shapes on the map were the ones carrying the vaguest information: a +// country is where somebody is *somewhere* in, and the dots are where they actually +// are. A darker neutral says which countries without spending the accent on them, so +// the dots stay the only colour the map has. +// +// #c8d4e3 is the land's own hue at 84% lightness against its 93%, a little more +// saturated. 86% was the first attempt and it is the number to know about: at that +// lightness the United States, China and Brazil read fine and Romania, Italy, +// Ireland and Cyprus — the countries that are a few pixels of coastline at this +// scale — did not read at all, which loses the shade exactly where it is the only +// thing saying anybody is there. Darker than this and the shades stop being marks on +// one landmass and become a second silhouette over it. +// +// Flat and opaque rather than a tint: the shades are one per country and never +// overlap, so there is nothing for an alpha to buy — and an alpha over the land is +// this colour anyway, computed at render time instead of being chosen. +$map-region: #c8d4e3; $map-mark: mix($primary, #fff, 52%); // City labels and the legend's figures. Lighter than the body ink, which made both // heavier than the map they annotate. Safe at this lightness only because each is @@ -530,6 +555,32 @@ $map-pulse-alpha: 0.18; fill: $map-land; } +// Every country a contributor is in, filled over the land. See `$map-region` for why +// it is a darker neutral and not a tint of the mark's colour. +.worldmap-country { + fill: $map-region; + // A hairline of page background around each country, which is the same device the + // dots use and it is here for the same reason: without it, neighbours that both + // have contributors fuse into one shape. The United States and Canada became a + // single mass from the Arctic to Mexico, China and India one from the Pacific to + // the Arabian Sea, and eight European countries one blob — a map that said "this + // continent" where the data says which countries. + // + // The stroke straddles the outline, so half of it lands outside: on the sea, where + // it is the same white and invisible, and on an unshaded neighbour, where it is a + // barely-there 1.17:1 lightening of the land. Only where two shades meet do both + // halves paint, and that is the one place a seam is wanted. + // + // 0.8 units against the dots' 2.5. This is a border and not a keyline: at 2.5 the + // seams read as white rivers between countries, and Italy — 15 units of coastline — + // loses a fifth of its area to its own outline. + stroke: var(--bs-body-bg, #fff); + stroke-width: 0.8; + // The pointer belongs to the hover target in the html layer above, as it does for + // the dots — a shade is not a bigger hit area, it is a bigger mark. + pointer-events: none; +} + .worldmap-dot { fill: $map-mark; // A hairline of page background around each mark. Guangzhou and Shenzhen are diff --git a/data/worldmap.json b/data/worldmap.json index b987d25..19218e3 100644 --- a/data/worldmap.json +++ b/data/worldmap.json @@ -1,9 +1,64 @@ { - "source": "https://cdn.jsdelivr.net/npm/world-atlas@2/land-110m.json", + "source": "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json", "license": "Natural Earth, public domain", "width": 1000, "height": 394.4, "k": 2.7777777777777777, "latTop": 84, - "land": "M312 383l3 2 4 0-1 2-3 0-1-1-1 1-2 1-3-1-2 0-3-1-4-1-3-2-3-3 2 1 4 2 3 1 2-2 1-2 2-1 2 1 1 1ZM337 375l3 2-5 1-1-1-3 2-1-2 3-1 3 0ZM695 371l-4 1 0-2 0-1 2 0 3 1ZM904 347l3 1 1-1 2 0 2 0 0 3-1 3-3 2-2-1-2-2 0-2-2-2 0-2ZM981 347l0 1 2-1 1 1 0 1-1 2-2 2-1 1 1 1-2 0-3 1 0 2-2 3-2 1-2 1-2 0-2-1-3 0 0-1 1-2 4-3 1-1 2-1 3-1 1-2 2-2 1 0 0-2 2-1ZM985 334l2 3 0-2 1 1 1 2 2 1 2 0 1-1 2 0-1 3-1 1-2 0 0 2-2 2-1 2-2 1-2-1 2-2-1-2-3-1 0-1 2-1 0-2 0-2-1-2-1-1-2-3-1-2 1 0 1 2 2 0ZM964 295l-1 1-1-1-2-1-2-2-2-2 2 0 2 1 3 3ZM995 281l1 3-2 0-1 0-1-1 2-1 1 0ZM0 279l0-1 0 1ZM1000 279l-2 1-2 1 0-1 1-1 3-1 0 1ZM966 279l-2-1 0-1ZM964 275l1 2-2 0 0-3ZM639 271l0 3 1 2 0 1-1-1 0 3-1 1-1 3-1 3-1 4-2 6-1 3-1 4-2 0-3 1-1 0-3-1 0-2-1-2-1-2 0-2 1-2 1-1 1-3 1-2-1-1-1-2 0-2 1-2 0-2 2 0 1 0 1-1 2 0 1-1 3-2 0-2 0-1 1 1 2-2 0-2 1-1 1 1 0 1ZM899 272l1 2 2-1 0 1 2 1 0 1 0 3 1 1 1 3 0 1 1 2 3 1 2 2 1 1 2 2 1 3 1 0 1 1 1 3 2 1 1 1 3 3 0 2 0 1 0 2 2 2-1 3 0 1-1 3 0 1 0 2-2 3-2 1-1 2-1 2 0 2-1 1-1 2 0 2-2 2-3 0-3 1-1 2-2 1-2-1-2-1 1-1-2 0-2 2-2-1-2 0-2 0-2-1-2-2-1-2 0-1-2-1-2-1 1-1-1-2-1 2-3 0 2-1 0-2 1-1 0-2-2 2-2 1-1 2-2-1 0-1-2-2-2-1-3-3-2 0-2-1-5 0-4 1-3 1-3 0-3 1-2 1 0 1-2 1-2 1-2 0-2-1-2 1-2 0-2 1-2 1-1 1-2 0-2 0-3-2-1-1 0-1 1-1 0-1 1-2-1-2-1-3-1-1 1-2-2-2-1-2 0-2-2-2 0-1 1 1-1-2 1 0 1 1 0-1-1-2-1-2 0-2 1-2 0-1 1-2 0 2 1-2 3-1 1-1 2-1 1 0 3-1 2 0 1-1 2 0 3-1 1-1 1-2 2-1 0-1 0-2 2-2 1 2 1 0-1-2 1-1 1 1 0-2 2-2 1-1 1 0 1-1 2-1 1 0 2 1 2 2 1 0 2 0-1-2 2-2 1-1 1-2 2-1 1 1 2-1 0-1-2-1 2-1 1 1 2 1 2 1 3 0 1 0 1 0 2 1 0 1-1 1-1 1-1 2-1 1 3 3 2 0 1 1 2 2 2 1 3 2 2-1 1-2 0-1 1-2 1-2-1-1 0-3 0-2 1-1 0-2 1-1 1-2 1 1 0 2 1 2 1 1 0 2ZM950 262l-1 1-1-1 2 0ZM835 262l-1 0-4-2 3-1 2 1 0 1ZM947 261l-1 0-2 0 0-2 1 0 1 1ZM949 260l-2-2-1-2 2 3ZM846 261l-3 1 0-1 1-2 3-1 3-1 2 0 2 0-1 0-3 2-3 0ZM827 256l1 1 2-1 1 2-3 0-2 0-2 0 1-1 2 0ZM841 256l0 1-4 1-4 0 0-1 2-1 2 1 2 0ZM944 256l-2 0-1-1-2-1 2 0 2 2ZM938 254l-2-1-1-1 1 1ZM802 252l5 0 1-1 5 2 1 1 4 1 3 2-3 1-3-1-2 0-3-1-3 0-3-1-2 0-1 0-5-1-1-1-2-1 2-2 3 0 2 1 1 0ZM874 251l-1 1 0-2 1-2 0 1ZM933 252l-2-1-1-1-1-2 2 1 1 2 1 0ZM922 249l-1 0-2 1-2 1-1 0-2-1-2-1 3 0 1 0 1-2 0 2 2-1 2-1 0-2 2 0 0 2ZM853 243l-1 1-1-1-1-1 3 0ZM862 242l1 2-2-1-2 0-2 0-2 0 1-2 3 0ZM925 246l-1-1-1-1-1-1-1-1-2-1 2 0 2 1 1 2 1 0ZM873 237l0 4 3 2 3-3 3-2 2 0 3 1 2 1 3 1 4 1 6 2 2 1 1 2 1 2 4 1 1 2-3 0 1 2 2 2 2 3 2 0 0 1 2 0 2 2-2 1-3-1-3-1-2-1-2-2-1-2-4-1-2 0-2 1 0 2-2 1-1 0-3 0-3-3-3 0-4 1 1-3 2 0-1-3-1-3-5-2-3 0-4-3-1 2-1 0 0-1 0-1-2-2 3-1 2 0-5-1-1-1-2-1-1-1 3-1 2-1 4 1ZM848 229l-2 3-2 1-3-1-5 0-2 1-1 2 3 2 1-1 6-1-1 1-1 0-1 2-2 1 2 3 2 4 0 2-1 1-1-1 1-2-3 1 0-2-2-2 0-2-2 0 0 4 1 4-2 0-1-1 0-2 0-3-1 0-1-2 1-2 0-2 2-4 0-1 3-2 2 1 3 0 4 0 2-2ZM857 230l0 3-1-1-1 2 2 1-2-1-1-3 0-3 1-1 1 2 1 0ZM794 250l-3 0-2-3-4-2-1-2-2-2-2-2-2-4-2-2-1-3-1-2-3-1-1-3-2-1-3-3 0-2 1 0 5 1 2 2 2 2 2 2 3 3 2 0 3 1 1 3 2 1-1 2 2 1 1 0 0 2 1 2 2 0 2 2-1 3ZM827 228l4 3-4 0-1 2 0 3-2 1 0 3-1 4-4 1-1-2-2 0-1-1-4 1-1-1-2 0-2-1 0-3-2-1-1-2 0-3 0-2 2-2 2 1 2-1 0-2 2-1 3 0 2-2 1-2 1-1 3-2 2-2 1-2 1 0 2 1 0 2 2 0 2 1 0 1-2 1 0 1-2 1-1 2 2 3ZM851 210l0 2 0 1 0 3-1-3-2 1 1 3-1 1-3-2-1-2 1-1-2-1-1 1-1 0-2 1 1-3 2-1 1-1 1 1 2 0 1-2 2 0-1-2 3 2 0 1ZM726 216l-3 1-1-2-1-4 2-5 2 2 1 2 1 2 0 3ZM331 205l-3 1 1-1 0-2 1 0 1 0ZM844 205l-1 1 0 1-3-1 1-1 1-2 1 0 0 2 2-3ZM829 207l-4 3 2-2 2-2 2-1 1-3 0 2-1 2ZM839 200l1 1 2 0 0 1-1 1-2 1 0-1 0-1ZM849 200l0 3-2-1 1 3-1 0 0-2-2-2 2 1 0-1-2-3 3 1ZM838 197l-1 2-1-1-2-2 3 0ZM837 182l2 1 1 0-1 1 1 2 0 2-2 1 0 2 0 3 2 0 1 0 3 1 0 2 1 2-3-2-1-1 0 1-2-2-3 0-1 0 0-1 0-2 0 1-1-1-1-1 0-3 1 1 0-5 1-2ZM318 183l-3 0-2 0 0-1 3 0 1 0ZM286 184l-2 0-2-1 2-1 2 0 2 1 0 1ZM298 178l3 1 2-1 2 1 1 1 2 0 1 1 1 1-1 1-1-1-1 0-2 1-1 0-1 0-1 1-1-1-2 0-1 0-2 0-1 0-2-1 3 0 2 0 1-1-1-1 0-1-2 0 1-1ZM806 181l-2 2-2-1 0-2 1-2 3-1 2 1 0 1-1 1ZM68 180l-1 0-1-1 1-2 1 1 2 1-1 1ZM66 176l-1 0 2 0ZM65 175l-2 0 0-1ZM62 174l-1 0 0-1ZM57 172l-1 0 1 0ZM279 170l1 1 2 0 4 2 1 1 3 1 2 1 2 1-2 1-2 0-2 0-4 0 2-1-1-1-2 0-2-3-1 0-3 0-4-1 0-2-3 0-2 2-1 0-2 1-1 0 1-1 1-1 1-1 2-1 2 0 3 0 2 0ZM285 167l-2-1-1-1 1-2 2 3ZM837 170l-2 2-1-2 0-2 1-3 2-2 2 1-1 2ZM284 159l-3 1 0-1 1 0 2 0ZM286 159l0 2-1-1-1-1 0-1ZM874 138l-1 3-1-1-2 1-1 1-1 0 0-2 1-1 2 0 1-1ZM596 134l-2 1-2 2-2 0 0-1 1 0 3-1ZM566 134l1 1 3 0 2 0 1 0-4 1-4-1ZM543 127l-1 2 0 3-2-1-2-1-3-1 0-2 3 1 3-1ZM526 119l1 2 0 4-1-1-2 1-1 0 0-4 0-1 1 0ZM892 130l-1 2 0 2-1 2-4 1-5 0-4 3-2-1 0-2-5 1-3 1-3 0 3 2-2 5-2 1-1-1 0-2-1-1-2-2 3-1 2-2 2-1 2-2 6-1 3 1 3-5 2 1 4-3 1-1 2-3 0-3 1-2 3-1 1 4 0 2-2 3ZM527 116l-1 2-2 0 0-2 0-1 2-1ZM900 111l2 0 2-1 0 3-4 1-2 3-5-2-1 3-3 0-1-3 2-2 3 0 1-4 0-2 4 3ZM323 104l2 0 3 0-2 1-1 1-3-2 0-1ZM328 97l-1 0-4-1-2-1 4 0 3 1ZM157 99l-1 0-5-1-3-2-4-2-1-1 3 0 2 0 3 1 2 2 3 1ZM344 93l-2 2 2-1 2 1 1 1 2 0 2 1 0 2 2-1 0 2 1 1-1 3-2 0-1-1 0-2-4 2-2 0 2-1-2-1-3 0-6 0 2-2-2 0 3-2 3-5 1-1 3-1 1 0 0 1ZM131 83l3 0-1 3 3 2-1 0-2-1-1-1-2-1 0-1 0-1ZM899 92l3 5-4-1-2 4 3 3 0 2-2-2-2 3-1-3 0-2 0-3 1-2 0-4-2-3 1-4 2-1-1-1 1-1 1 2 1 3 0 3ZM481 88l-5 2-4-1 3-3-2-2 4-3 2-1 2 0 3 2-1 2 0 2ZM535 79l-1 2-3-1-1-2 4-1ZM75 75l-3 1-1-1-1-1 3-1 1-1 2 0 1 1ZM492 70l-3 3 3 0 3 0-1 2-3 3 3 0 3 4 2 0 2 3 0 1 4 1-1 2-1 0 1 2-2 1-4 0-5 1-1 0-2 1-3-1-2 2-1-1 4-3 3-1-5 0-1-1 3-1-1-1 0-2 4 0 1-2-2-1-3-1 0-2-3 1 0-3-1-1 1-3 2-3 2 1ZM40 67l-2 0-1 0-2-1 3 0 2 0ZM280 61l-1 1-1 0-1-1 1-1 1 0ZM273 59l-4 2-2 0 2-2 4 0ZM23 56l2 1 1-1 3 1 2 1-2 0-2 0-1-1-3 0ZM263 51l1 1 1 0 2 0 3 1 3 1 0 2 3-1 1 1-2 1-4 0-2-2-3 2-4 1-1-1-3 0 2-2 0-2 1-2ZM460 49l-1 2 3 1-3 3-8 1-3 1-3 0-8-1 3-2-7-1 5 0-6-2 2-2 5 0 4 2 4-2 4 1 4-1ZM289 47l-3 0-1-1 2-2 2 0 2 0 0 2ZM0 42l7 2 7 3 0 1 2 1-1-2 8 0 5 3-3 1-4 1 0 2-1 1-3 0-2-1-4-1 0-1-3-1-3 1-2-1 1-1-4 0 2 2-2 1 0-11ZM1000 42l0 11-4 1-3 0 2 1 2 3 1 0 1 2-6 0-8 2-3 0-4 2-4 2-1 1-4-2-7 2-1-1-3 1-4 0-1 2-3 3 0 1 3 0 0 4-3 0-1 3 1 1-5 1 0 4-5 0 0 3-4 3-2-2-1-4-1-7 1-3 2-2 1-1 4-1 5-3 5-3 5-3 2-3-4 0-1 2-7 3-2-3-8 1-7 4 3 2-7 1-4 0 0-2-4 0-3 1-9 0-9 0-9 6-11 6 5 1 1 1 3 1 2-1 3 0 4 3 0 2-2 3-1 3-1 5-4 4-1 2-4 3-4 3-1 2-4 1-2 0-2-1-3 2-2 1-1 1-1 3-1 0-2 2-2 0-1 1 0 1 3 2 2 3 1 2 0 3-1 2-3 0-2 2-3 0 0-2 1-2-2-3 2 0-2-3-1 0-1 1-2-2 1-1 1-2-2-1-1-1-4 1-2 2-3 0 2-1-1-1 2-2-1-1-3 1-3 2-1 1-3 0-2 2 2 2 2 0 0 1 3 1 3-2 2 1 2 0 0 2-4 1-1 1-3 1-1 2 3 2 1 3 2 2 2 2 0 2-2 1 1 2 1 0 0 3-1 2-2 0-2 3-2 4-2 3-4 3-4 2-3 0-2 2-2 0-4 1-3 1-1 3-2 0 0-2 0-1-4-1-1 0-4 3-2 2 0 2 2 3 2 4 3 2 1 2 2 5-1 5-2 2-3 2-2 2-4 2-1-1 1-2-2-2-3 0-1-2-1-3-3-1-2 0 0-2-2 0 0 3-2 4-1 3 1 2 1 0 2 2 0 3 2 1 1 1 2 1 2 2 1 2 0 2 0 1 1 3 2 3-2 1-3-2-3-2-1-2-1-2-1-2-1-1 1-2-1-2-1-1-1-1-1-2-1-1-1 2 0-2 0-2 1-2-1-2 1-2-1-2 1-3-1-2-1-3-1-3-1-3-2 2-3 2-2-1-1 0 1-4-1-2-2-3 0-1-1-1-2-2-1-2 0-1-1-1-1-2-3 0 1 1-1 2-1-1-2 0-1 0 0 1-2 0-3 1 0 2-2 1-4 2-3 3-2 2-3 2 0 1-1 1-3 1-1 0-1 2 1 4 0 2-1 3 0 5-2 0-1 2-2 2 0 1-2 1-2-2-2-4-1-3-1-1-1-3-1-4 0-1-3-4-1-5-1-4 0-3 0-3-4 2-2-1-4-3 1-1 0-1-4-2-2-1 0-2-3-2-5 0-4 0-4 1-5-1-3-1-4 0-1-4-1 0-2 0-3 2-3-1-3-2-3-1-2-3-2-3-1 0-2-1-1 1-2 0 1 1 0 3 2 2 1 1 2 2 0 1 1 2 1 2 0-1 1-2 1 1 0 1 0 2 0 1 3 0 2 0 2 0 2-2 2-1 2-2 1-1 0 1 0 3 1 2 1 1 2 0 2 1 1 1 2 2-1 2-1 2-2 2-1-1-1 3 1 1-2 1-2 1 0 1-2 1-1 0 0 1-2 1-1 0-2 1-1 0-2 1-1 1-3 2-4 1-3 2-1 1-2 1-2 0-3 0-1 1-1 1-1 0-3 0-1-1 0-2-1-3-1-1 1-1 0-2-1-1-1-2-1-1-1-3-1-2-2-2-1 0-2-3-1-2 1-1-2-3-1-2-2 0-1-2 0-2-2-2-2-3-1-2-2 0 1-1 0-2-1 3-1 1-2 0-1-2-2-4 1 4 2 2 2 5 1 1 1 2 2 3 0 2 3 3 1 3 0 3 1 4 1 0 2 1 1 4 1 2 2 1 3 3 2 2 1 1 3 2 0 2-1 1 1 0 1 2 2 1 1 0 3 0 3-1 2-1 1 0 3 0 2-1 2 0 1-1 1 0 0 2 0 2-1 1-1 3-1 3-2 3-2 5-2 3-4 3-2 3-5 2-2 3-3 3-1 1-2 2-1 1-1 0-1 2 0 2-1 1-1 1-1 4 0 1 2 1-1 3 0 2 1 2 1 2 1 1 0 1 0 3 1 2 0 5 0 1-1 2-1 2-1 2-3 1-3 1-3 3-1 1-2 2-1 0-1 2 2 2 0 2 1 3-1 2 0 1-1 1-2 1-3 2-2 1 1 1 0 2-1 2 0 2-1 1-1 2-2 1-1 2-2 2-3 3-2 1-2 2-3 1-1 0-2 0-1 1-3-1-2 1-1-1-3 1-3 1-1 1-2 0-1-1-2-1 0-2-1-2 1-3-2-2-2-3-2-3-2-2-1-2 0-3-1-2-1-4 0-4 0-1-1-1-2-3-1-3-1-2-2-2 0-3-1-1 1-3 1-2 0-2 1-2 0-1 2-2 1-1 0-2 0-2-2-3 0-1 1-2-1-3-1-1-1-2 0-2-1-2-2-3-3-2-2-3-2-3 1-1 1-2 0-2 1-4 0-2-1-2-1-1-1-1 0-1-3 1-1 0-1 1-3-1-1-1-1-2-2-2-2 0-3 0-2 0-2 1-4 1-2 1-2 1-3-1-1 0-2 0-2 0-3 0-2 1-3 1-1 0-3-1-3-2-2-2-2-2-3-1-1-1 0-1-1-2-1-2-1-1-1-1-1-1-1-1-2-1-1-1 0-1-1-1-1-2 1-1-2-3-1-1 1 0 2-2 0-1 0-2 1-1 0-3 0-3 0-1 0-1-1-2-1-1 0-1 0-1 1-1 1-2 1-3 1-1 2-2 0-2 1-2 2-1 2-2 1-2 3 0 2-2 1 0 2-3 0-3 1-3 0-1 2-2 3-1 2-1 2-3 1-2 2 0 1 1 3 0 3 1 1 0 3-2 3 0 1-2 3 0 5-1 4 0 2 0 2-1 3 0 1 1 2 0 3-1 2 0 0 1 3-1-2 2 0 1 1 4-2 1 1 1 1 1 1 1 1 0 3 1 1 0 3 0 3 2 2 2 2 1 4 1 3 1 1 0 2-2-1-2 1-1 2-2 2 0 4 1 1 1 1 0 3 1 5 1 2 0 3 1 1 1 2-1 2-1 2 0 2 0 1 1 3 0 2 0 1 0 1-1 1-2 0-2 2-3 1-2 0-2 0-1-1-1 1-1-1 0-3-1-1 2-5 0-2-1-3-1-1 2-2 0-2-2-3 1-2-3-2-2 1-2-1-1 3-3 4 0 1-2 6 0 3-2 3 0 5 0 5 2 4 1 3-1 2 0 3-1 1-1-1-2-1-1-2 0-1-1-4-3-3-1-2-1 2-1 2-2-1-1 4-1-3-1-2 1-2 1-2 0-3 1 0 2 2 0 2 0-3 2-4 1-1 0 0-2-3-1 3-1-5-1 0-1-3 0-1 1-2 3-1 1-2 3-1 1-1 2 1 2 0 1 3 1-4 0-1 1-3 2-1-1-1-1-2 0-3 0 2 2-2 0-1 0-2-1 0 2 2 1-1 1 1 1 2 1 0 2-3-1 1 1-2 1 1 2-2 0-2-1-1-2 0-2-1-1-2-2-1-2-1 0 0-2 0-2 0-1-2-1-1-1-2-1-2-1-3-1-2-2-1-1-1-1-1 1-1-2-1-1-3 1 0 2 1 2 3 1 1 3 3 2 2 0 0 1 3 1 2 1 2 1 0 2-2-2-2 0-1 2 2 1-1 1-1 0-1 3-1 0 0-2 0-3-1-1-1 0-1-1-2-1-1-1-2 0-2-2-3-1-2-2-1-3-1 0-2-1-2 0-1 2-1 0-3 2-5-1-4 1-1 1 0 2-2 2-4 0-2 3-1 2 1 2-1 1-1 2-2 0-2 2-3 1-3-1-2 1-1 1-1 0-1-1-1-1-3-1-1 1-1 0-2 0 1-2-1-2-1 0 0-1 0-2 1-1 0-1 1-2 0-1-1-2 0-2-1-1 4-2 3 0 4 0 3 1 2 0 5 0 1-2 1-5-3-3-2-2-4-1-1-2 4-1 5 1-1-3 2 1 7-2 1-2 2-1 2 0 2-1 2-4 4-1 2 0 3-1 2-1 0-1 0-1-1-2-1-3 2-1 2 0 3-2 0 1 0 2 1 1 0 1-3 1 1 2 0 1 2 0 3 1 2-1 3 1 1 1 2-1 4-1 4-1 3 0 3 1 0-1 4-1-1-2 1-2 1-2 3-1 2 2 2 0 1-2 0-2-1 1-2-1 0-2 3-1 4 0 3 0 3 0 3-1-3-2-5 0-5 2-4 0-2-1-3-1 1-3-2-3 2-1 2-2 7-3 2-1-1-1-4-1-4 1-3 2 0 1-4 3-5 2-2 4 2 2 2 1-2 4-3 0-1 5-2 2-3 0-2 2-3 1-1-3-2-3-2-4-2-2-6 3-3 1-4-1-1-3-1-7 2-2 8-2 5-3 5-4 7-5 5-2 7-4 6-1 5 0 4-2 5 0 5 0 9 2-4 0 3 2 3-1 5 2 7 0 11 4 2 1 0 2-3 1-4 1-13-2-2 0 5 2 0 4 4 1 2 1 0-1-2-2 2-1 7 2 2-1-2-2 7-2 2 0 3 1 2-2-3-2 2-1-2-2 7 1 2 1-3 1 0 1 2 1 4 0 1-2 6-2 9-2 2 0-2 2 3 0 2-1 5 0 4-1 4 2 3-2-3-2 1-1 8 1 4 1 10 3 2-1-3-2-3-1 1-1-2-2 5-3 2-3 2-1 8 1 0 2-2 2 1 1 1 2 0 4 3 1-2 2-5 4 3 1 1-1 3-1 1-2 2-1-1-1 1-2-3-1-1-1 3-3-4-2 5-2-1-2 2 0 1 2-1 2 3 1-1-2 4-1 6-1 5 2-2-2 0-3 4-1 7 0 6 0-2-1 3-2 3 0 6-2 7 0 8-1 3 0 6-1 5 0 1-1 2-1 7-1 5 0-4 1 6 1 1 1 2-1 9 0 6 2 2 1-1 1-3 1-7 1-2 1 3 1 4 0 3 0 1 2 2-1 4-1 9 1 1 1 11 1 0-2 6 0 5 0 4 1 1 2-1 1 3 3 5 1 2-3 5 1 5-1 5 1 2-1 5 1-2-3 3-1 25 2 3 1 7 3 11-1 6 0 2 2 0 2 3 0 4 0 5 0 5 0 5 0 5 3 3-1-2-2 1-1 9 0 6 0 8 2ZM636 119l2 2 1 0-1 1-1 2-1 2 0 1 1 2 2 1 2 1 4 0 5 0-1-3 1-3-2-1 0-2-2 0 1-2 3 0 2 0-2-2-1-2-2 1 0 2-1-2 0-2-3-1-2-3-1-1 2 0 1-2 2-1 2 1 1-3-1-2-2 0-3 0-3 1-3 0-1 2-3 1-2 2 2 3 0 2 3 3ZM234 41l-1 1-4-1-2 1-4-1 2-1 2-2 3 1 2 1 1 0ZM0 35l0 2 0-2ZM1000 35l0 2-3 0-1-1ZM0 35l3 0 4 0-3 1-4 1ZM1000 35l0 2ZM248 40l0 3 4-2 4 2-1 2 2 2 3-2 2-3 0-3 4 0 4 1 4 1 0 2-2 1 2 2 0 1-6 2-3 0-3-1-1 2-3 2-1 1-3 2-4 0-2 2 0 1-3 1-4 2-3 3-1 2 0 4 4 0 1 3 2 2 4-1 5 1 2 1 2 2 4 1 3 1 4 0 3 0 0 3 1 2 2 3 4 3 2-1 2-3-2-4-2-2 5-1 3-2 1-2 0-2-2-2-3-2 3-3-1-2-1-5 2 0 5 0 3 1 2-1 2 1 4 1 1 2 5 0 0 2 1 4 2 0 2 2 4-2 3-3 2-1 2 2 3 4 3 3-1 2 4 1 3 2 4 1 2 1 1 2 2 0 1 1 0 3-2 2-2 0-4 1-4 3-4 0-6 0-5 0-2 0-3 2-3 1-4 4-4 2 3 0 4-4 6-2 4 0 3 1-3 2 1 3 1 2 4 1 4 0 3-3 0 2 2 1-4 2-6 1-2 1-4 2-2 0 0-2 5-2-4 0-3 0-3 2-3 1-3 1-1 2-1 2 1 1 1 0 0-1 1 2-2 0-2 0-2 1-1 0-1 0-3 1 4-1-3 1-2 0 0 3-2 2-2-1 1 1 1 2-1 1-2 2 1-2-2-1 0-2-1 1 1 2-2-1 2 1 0 3 1 1 1 3-2 2-3 0-2 2-1 0-2 1-3 3-2 1-1 2 0 2 0 2 1 2 1 2 0 1 2 4 0 2-1 1 0 1-2 0-1-1-1-1-1-2-2-2 0-1 0-2 0-2-3-2-1 0-2 1-2-1-2-1-3 0-3 0-2 0-1 1 1 1-2 1-2 0-3-1-2 0-2 0-2 0-2 0-3 3-2 1-2 1 0 1 0 2 0 2-1 3 0 2-1 4 0 1 1 2 0 1 1 2 2 2 1 2 1 1 3 1 1 1 2-1 2 0 2-1 2 0 2-1 1-2 0-2 2-2 3 0 3 0 1-1 1 2-2 2 0 2 0 1-1 3-1-1 0 1 0 1 0 2 0 1-1 1 0 2 1 0 2-1 2 1 1-1 1 0 2 0 1 0 1 0 1 1 2 1 1 1 0 2-1 2 0 1 0 2-1 1 0 2 1 1 0 1 1 2 2 1 1 1 1 0 1 1 1 0 1-1 2-1 2 0 2 0 1 1 2 1 2 0 2-2 1 0 0-3 2-1 2-1 2 0 2-1 1-1 2-1 1 1 0 1-2 1 1 1 0 1-1 2 1 2 1 0 1-2-1-3 3-1 0-2 2 2 2 0 1 1 3 1 3 0 2 1 2 0 1-1 4 0 3 0-2 0 1 2 2 0 2 1 0 2 2 0 1 1 2 1 2 2 0 1 1 0 1 2 1 1 4 0 2 0 3 0 3 1 3 3 1 1 1 1 2 5 1 1 0 1-2 3 6 1 0 3 2-2 4 1 4 2 2 1-1 2 3-1 6 1 4 0 4 3 4 3 2 1 2 0 1 1 1 3 1 2-2 4-1 2-4 4-2 3-2 3-1 2 0 5-1 4 0 2-1 1 0 4-3 3-1 3-2 1-1 2-3 0-4 1-2 1-3 1-3 2-3 3 0 2 0 2 0 3-1 1-2 2-3 4-2 3-2 1-1 3-2 1-1 2-4 1-2 0-1 0-3-1-2 0-1-1 0 1 3 2 0 2 1 1 0 2-2 3-5 2-5 0-3 0 0 2 0 2 0 1-1 1-3 1-3-1-1 0 1 3 1 1 2-1 1 2-3 0-2 2 0 3-1 1-3 0-2 2-1 2 3 2 3 1-1 2-4 2-1 3-3 1-1 1 1 3 2 2-1 0-3 0-1 0-3 1 0 3-1 0-4-1-3-2-3-1-1-2 1-2-2-2 0-4 1-3 3-2-4-1 3-3 0-4 4 1 1-6-2-1-1 4-1-1 0-4 1-5 2-2-1-2 0-3 1-1 1-4 2-4 2-5-1-4 1-2-1-3 2-4 1-5 0-6 1-6 0-5 0-4-3-1-1-1-5-3-5-3-2-2-1-2-2-5-3-5-3-5-1-2-1-2-2-1-2-2 1-1-1-3 1-1 2-2 1-2 0-1-1 1-2-1 0-4 2-2 1-2 0-1 1 0 2-2 1-1-1-1 1-1 2 0 1-2 1-1-1-1 0-2 0-2 0-3-1-2-2-2 0-2-2-1-1 1-2 1 1 2-1 1-2 0 0-1-1 0-1-1-1 0-2-1-2 0 0-1-1-1-1-1-1-1-1 1-2-1 0-2-1-1 0-1-1-1-2-2-2-2 1-1-1 1-2 0-1-1-1 0-1 0-3-1-1 0-2-1-1-1-3-3-2-1-2-1-2 1-2 1-1 0-2-1-2 0-3-2-2 0-3-1-2-2-3-1-2 0-2-2-3-1-1-2-1-1 1-1 1-2-1-1-1-2-1-1-2-3-3-2-1-2-2-1-1-2-1-1-2-1 0-2-2 0-1-2-2-1-1-3-1-2 0-1-2-1-3-1 0 1 0 1 0 3 1 1 2 2 2 1 1 2 1 2 2 1 1 3 1 1 0 1 1 2 1 0 1 1 1 1-1 2-1-2-2-1-2-2-2 0 0-2 0-2-1-1-2-1-1 0-2-1-2-2 2 0 1-1 0-1-2-2-2-1-1-2-1-1-1-3-1-2-1-1-2-2-1 0-2-1-1-1-2 0-1-2-3-3-2-4-2-2-2-2 0-2-2-2 1-2 0-2-1-2 1-3 1-5-1-4-1-2 0-2 4 1 1 3 1-4-1-2-6-3-2-1-5-1-2-2 1-2-4-1 0-2-4-2 0-2-1-1-3-1 0-2-4-2-1-3-3 0-5 0-3-1-6-3-2-1-5-1-4 1-5-2-4-1-3 1 1 2-2 0-3 0-2 1-3 1-1-2 1-2 3-1-4 1-2 1-4 2 2 2-2 2-3 1-3 1-1 1-4 1-1 2-3 1-2 0-3 1-3 0-2 1-5 1 3-2 3 0 3-2 3 0 1-1 4-2 3-2 0-2 2-1-3 1-3 0-2-1-1-1-3 1-2 0 0-1 0-1-1-1-4 0-2-1-2-1 0-1-2-1 1-2 2-2 1-1 2 0 2 0 2-1 2 0 2-1 0-1-2-1 2-1-1 0-3 1-3 0-4 0-4 0-1-1-4-2 4-1 6-2 2 0 0 2 6 0-2-2-4-1-2-1-2-2-4-1 1-1 5 0 4-1 0-2 3-1 3 0 5-2 3 1 4-2 4 1 2 1 2-1 4 1 4 1 3-1 6 1 5 1 2 0 4-1 4 1 3 1 6 0 4 2 3 0 2-1 4-1 4 0 4-1 4-1 2 2 2-1 1-1 2 0 5 2 3-2 1 3 3-1 1-1 4 0 4 2 6 1 4 0 3 0 4 1-4 2 5 0 7 0 3-1 3 2 3-1-3-1 2-1 3 0 2-1 2 1 3 1 3 0 5 1 4 0 5 0-1-2 3 0 4 1 0 2 2-2 2 0 1-3-3-1-3-1 0-3 4-2 3 0 3 1 4 3-3 2ZM183 30l-2 2 7-1 4 1 3-1 2 1 2 2 2-1-2-3 2 0 3 0 3 1 2 3 1 2 4 2 6 1-1 1-4 0 1 1-1 1-5 0-4-1-4 0-5 1-8 1-4 0-1-1-4-1-3 0-3-2 2 0 4-1 4 0 4 0-6-1-6 0-4 0-1-1 6-1-4 0-5 0 3-3 2-1 7-1ZM210 29l-3 2-4-2 5 0ZM288 30l-3 1-3 0-3 0-4-1 0-1 2-1 6 1ZM260 30l2 2 2-2 7-2 5 3 0 2 5-1 3-1 6 2 4 1 0 1 5 0 3 1 7 1 2 2 3 2-5 1 6 2 5 1 4 2 4 1-1 2-5 3-3-1-4-3-4 0 0 2 3 1 3 2 2 0 1 3-1 2-3-1-7-2 4 3 3 1-7 0-6-1-4-2-3-2-4-1-8 1-2-1 2-2 5 0 6 0 0-3 3-3-1-1-1-1-4-1-6-1 2-1-3-1-2-1-2-1-2 1-5 1-10-1-6-1-4 0-3-1 3-1-4-1-1-2 3-3 2-1 8-1ZM221 28l4 1 5-1-2 3 4 1-1 2-4 1-3 0-2-1-7-2 6-1-3-1ZM899 30l-4 0-6 0 2-2 4 0 4 1ZM241 31l-3 2-3 0-2-2 0-2 2-1 2-1 6 0 6 1-5 3ZM165 35l-7 1-1-1-7-1 3-4 3-1-3-2 9 0 4 0 7 0 3 1 3 1-3 1-7 2-4 2ZM919 25l-4 1-4 0-5-1 6-1ZM240 25l-2 1-4 0-3-1 2-1 4-1 2 1ZM903 23l-2 2-10 0-5 1-6-2 2-2 4 0 7 0ZM226 20l3 2 0 1-2 2-4 0-3 0 0-2-5 0 0-2 3 0 4-1 4 1ZM199 22l2 1 2-1 3 0 0 2-1 1-10 0-7 2-4 0 5-2-12 0-4-1 4-2 2-1 8 1 5 1 5 1-4-3 3-1 2 0ZM660 37l-2 0-9 0-1-1-5-1 0-2 3 0 0-2 5-2-2-1 6-2 0-1 6-2 9-1 9-1 5-1 5 0 2 1-2 1-9 1-9 1-9 3-4 3-4 2 1 3ZM237 19l3 1 6 0 2 1-1 1 3 1 2 0 4 0 4 1 4-1 6 0 5 0 3 1 0 1-1 1-5 1-3-1-8 1-6 0-4-1-8-1-1-1 0-1-3-2-6 0-3-1 1-1ZM177 18l0 2-2 1-3 0-5 1-4 1-4-1 4-2 6-2 4 0ZM239 18l-1 0-5 0 5-1 2 1ZM194 18l-5 0-4-1 2 0 4-1 4 1ZM569 17l-7 1-4 0 1-1-1-1 6-1 1 1ZM195 15l-3 1-5 0 3-2 2 0ZM234 17l-4 0-3-1-1-1 0-1 4 0 1 1 4 1ZM222 16l1 1-4 0-5-1-6 0 3-1-4-1 0-1 5 0 8 1ZM792 16l-16 1 5-4 3 0 2 0 7 2ZM551 12l9 2-7 1-2 2-2 1-1 2-4 0-6-2 3-1-4 0-6-3-2-2 8-1 1 1 4 0 1-1 4 0ZM571 10l5 1-4 1-8 1-8-1-5-1-3-1 9-1 4 1 3-1ZM642 10l-4 0-2 0-4 1-3-1 2 0-6-1 5 0 4 0 2 0 3 0 4 0ZM778 14l-6 1-8-1-5-1-2-2-4-1 7-2 6 0 6 1 6 3ZM258 12l4 1-4 1-5 2-5 0-6 0-3-1 0-1 2-1-5 0-3-1-2-1 2-2 2 0 3-1-1 0 6 0 4 1 5 1 4 0ZM310 2l7 1 6 0 5 1-7 2-6 0-3 1 6 0-6 2-5 0-5 3-5 0-2 1-9 0 4 0-2 1 3 1-3 1-4 1-2 1-4 1 6 0-8 3-7-1-8 0-4 0-6 0 0-1 5-1-1-2 2 0 7 1-4-2-4 0 2-1 5-1-3-2-1-2 7 1 2 0 5-1-7 0-9 0-5-1-3-1-3-1 4-2 3 0 5 0 5-1 3 0 3 1 2-2 4 0 5-1 8 0 2 1 8-1 6 0ZM425 1l17 3-5 1-11 0-15 0 2 0 10 0 8 1 5-1 3 1-3 2 7-1 13-1 9 0 1 2-11 2-2 0-9 1 7 0-3 2-3 2 0 3 4 1-5 1-4 0 5 2 1 2-3 1 3 2-6 0 3 1-5 2-3 0 3 2 0 1-5-1-2 0 4 1 4 2 1 2-5 1-3-1-3-2 1 2-3 1 7 0 4 1-8 2-7 2-8 1-3 0-3 1-4 3-6 2-2 0-4 1-4 1-2 1 0 2-1 2-5 2 1 2-1 3-1 2-4 1-5-3-5 0-3-1-2-3-4-3-2-2 0-3-4-2 1-2-2-1 3-4 4-1 1-1 1-2-3 1-2 0-2 1-4-1 0-2 1-1 3 0 5 0-5-1-2-1-3 0-2-1 3-2-2-1-2-2-3-3-4-1 0-1-7-2-6 0-8 0-6 0-4-1-4-2 7 0 6-1-12 0-7-1 1-2 10-1 10-1 2-1-8-1 2-1 10-2 4-1-1-1 7-1 8 0 9 0 3 1 7-2 7 1 4 1 5 0-6-1 0-1 9-2 10 0 4-1 10 0Z" + "land": "M995 282l1 0 0 2-2 0-1 0-1-1 2-1ZM0 278l0 1 0-1ZM1000 278l0 1-2 1-2 1 0-1 1-1 3-1ZM609 246l-1 4 0 1 2 1-1 3 0 2 1 2 1 2 1 1 0 1 0 3 1 2 0 5 0 1-1 2-1 2-1 2-3 1-3 1-3 3-1 1-2 2-1 0-1 2 2 2 0 2 1 3-1 2 0 1-1 1-2 1-3 2-2 1 1 1 0 2-1 2 0 2-1 1-1 2-2 1-1 2-2 2-3 3-2 1-2 2-3 1-1 0-2 0-1 1-3-1-2 1-1-1-3 1-3 1-1 1-2 0-1-1-2-1 0-2-1-2 1-3-2-2-2-3-2-3-2-2-1-2 0-3-1-2-1-4 0-4 0-1-1-1-2-3-1-3-1-2-2-2 0-3-1-1 1-3 1-2 0-2 1-2 0-1 2-2 1-1 0-2 0-2-2-3 0-1 1-2-1-3-1-1-1-2 0-2-1-2-2-3-3-2-2-3-2-3 1-1 1-2 0-2 1-4 0-2-1-2-1-1-1-1 0-1-3 1-1 0-1 1-3-1-1-1-1-2-2-2-2 0-3 0-2 0-2 1-4 1-2 1-2 1-3-1-1 0-2 0-2 0-3 0-2 1-3 1-1 0-3-1-3-2-2-2-2-2-3-1-1-1 0-1-1-2-1-2-1-1-1-1-1-1-1-1-2-1-1-1 0-1-1-1-1-2 1-1-2-3-1-1 1 0 2-2 0-1 0-2 1-1 0-3 0-3 0-1 0-1-1-2-1-1 0-1 0-1 1-1 1-2 1-3 1-1 2-2 0-2 1-2 2-1 2-2 1-2 3 0 2-2 1 0 2-3 0-3 1-3 0-1 2-2 3-1 2-1 2-3 1-2 2 0 1 1 3 0 3 1 1 0 3-2 3 0 1-2 3 0 5-1 4 0 2 0 2-1 3 0 1 1 2 0 3-1 2 0 0 1 3-1-2 2 0 1 1 4-2 1 1 1 1 1 1 1 1 0 3 1 1 0 3 0 3 2 2 2 2 1 4 1 3 1 1 0 2-2-1-2 1-1 2-2 2 0 4 1 1 1 1 0 3 1 5 1 2 0 3 1 1 1 2-1 2-1 2 0 2 0 1 1 3 0 2 0 1 0 1-1 1-2 0-2 2-3 1-2 0-2 0-1-1-1 1-1-1 0-3-1-1 2-5 0-2-1-3-1-1 2-2 0-2-2-3 1-2-3-2-2 1-2-1-1 3-3 4 0 1-2 6 0 3-2 3 0 5 0 5 2 4 1 3-1 2 0 3-1 1-1-1-2-1-1-2 0-1-1-4-3-3-1-2-1 2-1 2-2-1-1 4-1-3-1-2 1-2 1-2 0-3 1 0 1 2 1 2 0-3 2-4 1-1 0 0-2-3-1 3-1-5-1 0-1-3 0-1 1-2 3-1 1-2 3-1 1-1 2 1 2 0 1 3 1-4 0-1 1-3 2-1-1-1-1-2 0-3 0 2 2-2 0-1 0-2-1 0 2 2 1-1 1 1 1 2 1 0 2-3-1 1 1-2 1 1 2-2 0-2-1-1-2 0-2-1-1-2-2-1-2-1 0 0-2 0-2 0-1-2-1-1-1-2-1-2-1-3-1-2-2-1-1-1-1-1 1-1-2-1-1-3 1 0 2 1 2 3 1 1 3 3 2 2 0 0 1 3 1 2 1 2 1 0 2-2-2-2 0-1 2 2 1-1 1-1 0-1 3-1 0 0-2 0-3-1-1-1 0-1-1-2-1-1-1-2 0-2-2-3-1-2-2-1-3-1 0-2-1-2 0-1 2-1 0-3 2-5-1-4 1-1 1 0 2-2 2-4 0-2 3-1 2 1 2-1 1-1 2-2 0-2 2-3 1-3-1-2 1-1 1-1 0-1-1-1-1-3-1-1 1-1 0-2 0 1-2-1-2-1 0 0-1 0-2 1-1 0-1 1-2 0-1-1-2 0-2-1-1 4-2 3 0 4 0 3 1 2 0 5 0 1-2 1-5-3-3-2-2-4-1-1-2 4-1 5 1-1-3 2 1 7-2 1-2 2-1 2 0 2-1 2-4 4-1 2 0 3-1 2-1 0-1 0-1-1-2-1-3 2-1 2 0 3-2 0 1 0 2 1 1 0 1-3 1 1 2 0 1 2 0 3 1 2-1 3 1 1 1 2-1 4-1 4-1 3 0 3 1 0-1 4-1-1-2 1-2 1-2 3-1 2 2 2 0 1-2 0-2-1 1-2-1 0-2 3-1 4 0 3 0 3 0 3-1-3-2-5 0-5 2-4 0-2-1-3-1 1-3-2-3 2-1 2-2 7-3 2-1-1-1-4-1-4 1-3 2 0 1-4 3-5 2-2 4 2 2 2 1-2 4-3 0-1 5-2 2-3 0-2 2-3 1-1-3-2-3-2-4-2-2-6 3-3 1-4-1-1-3-1-7 2-2 8-2 5-3 5-4 7-5 5-2 7-4 6-1 5 0 4-2 5 0 5 0 9 2-4 0 3 2 3-1 5 2 7 0 11 4 2 1 0 2-3 1-4 1-13-2-2 0 5 2 0 2 0 2 4 1 2 1 0-1-2-2 2-1 7 2 2-1-2-2 7-2 2 0 3 1 2-2-3-2 2-1-2-2 7 1 2 1-3 1 0 1 2 1 4 0 1-2 6-2 9-2 2 0-2 2 3 0 2-1 5 0 4-1 4 2 3-2-3-2 1-1 8 1 4 1 10 3 2-1-3-2-3-1 1-1-2-2 5-3 2-3 2-1 8 1 0 2-2 2 1 1 1 2 0 4 3 1-2 2-5 4 3 1 1-1 3-1 1-2 2-1-1-1 1-2-3-1-1-1 3-3-4-2 5-2-1-2 2 0 1 2-1 2 3 1-1-2 4-1 6-1 5 2-2-2 0-3 4-1 7 0 6 0-2-1 3-2 3 0 6-2 7 0 8-1 3 0 6-1 5 0 1-1 2-1 7-1 5 0-4 1 6 1 1 1 2-1 9 0 6 2 2 1-1 1-3 1-7 1-2 1 3 1 4 0 3 0 1 2 2-1 4-1 9 1 1 1 11 1 0-2 6 0 5 0 4 1 1 2-1 1 3 3 5 1 2-3 5 1 5-1 5 1 2-1 5 1-2-3 3-1 25 2 3 1 7 3 11-1 6 0 2 2 0 2 3 0 4 0 5 0 5 0 5 0 5 3 3-1-2-2 1-1 9 0 6 0 8 2 4 1 0 11-4 1-3 0 2 1 2 3 1 0 1 2-6 0-8 2-3 0-4 2-4 2-1 1-4-2-7 2-1-1-3 1-4 0-1 2-3 3 0 1 3 0 0 4-3 0-1 3 1 1-5 1 0 4-5 0 0 3-4 3-2-2-1-4-1-7 1-3 2-2 1-1 4-1 5-3 5-3 5-3 2-3-4 0-1 2-7 3-2-3-8 1-7 4 3 2-7 1-4 0 0-2-4 0-3 1-9 0-9 0-9 6-11 6 5 1 1 1 3 1 2-1 3 0 4 3 0 2-2 3-1 3-1 5-4 4-1 2-4 3-4 3-1 2-4 1-2 0-2-1-3 2-2 1-1 1-1 3-1 0-2 2-2 0-1 1 0 1 3 2 2 3 1 2 0 3-1 2-3 0-2 2-3 0 0-2 1-2-2-3 2 0-2-3-1 0-1 1-2-2 1-1 1-2-2-1-1-1-4 1-2 2-3 0 2-1-1-1 2-2-1-1-3 1-3 2-1 1-3 0-2 2 2 2 2 0 0 1 3 1 3-2 2 1 2 0 0 2-4 1-1 1-3 1-1 2 3 2 1 3 2 2 2 2 0 2-2 1 1 2 1 0 0 3-1 2-2 0-2 3-2 4-2 3-4 3-4 2-3 0-2 2-2 0-4 1-3 1-1 3-2 0 0-2 0-1-4-1-1 0-4 3-2 2 0 2 2 3 2 4 3 2 1 2 2 5-1 5-2 2-3 2-2 2-4 2-1-1 1-2-2-2-3 0-1-2-1-3-3-1-2 0 0-2-2 0 0 3-2 4-1 3 1 2 1 0 2 2 0 3 2 1 1 1 2 1 2 2 1 2 0 2 0 1 1 3 2 3-2 1-3-2-3-2-1-2-1-2-1-2-1-1 1-2-1-2-1-1-1-1-1-2-1-1-1 2 0-2 0-2 1-2-1-2 1-2-1-2 1-3-1-2-1-3-1-3-1-3-2 2-3 2-2-1-1 0 1-4-1-2-2-3 0-1-1-1-2-2-1-2 0-1-1-1-1-2-3 0 1 1-1 2-1-1-2 0-1 0 0 1-2 0-3 1 0 2-2 1-4 2-3 3-2 2-3 2 0 1-1 1-3 1-1 0-1 2 1 4 0 2-1 3 0 5-2 0-1 2-2 2 0 1-2 1-2-2-2-4-1-3-1-1-1-3-1-4 0-1-3-4-1-5-1-4 0-3 0-3-4 2-2-1-4-3 1-1 0-1-4-2-2-1 0-2-3-2-5 0-4 0-4 1-5-1-3-1-4 0-1-4-1 0-2 0-3 2-3-1-3-2-3-1-2-3-2-3-1 0-2-1-1 1-2 0 1 1 0 3 2 2 1 1 2 2 0 1 1 2 1 2 0-1 1-2 1 1 0 1 0 2 0 1 3 0 2 0 2 0 2-2 2-1 2-2 1-1 0 1 0 3 1 2 1 1 2 0 2 1 1 1 2 2-1 2-1 2-2 2-1-1-1 3 1 1-2 1-2 1 0 1-2 1-1 0 0 1-2 1-1 0-2 1-1 0-2 1-1 1-3 2-4 1-3 2-1 1-2 1-2 0-3 0-1 1-1 1-1 0-3 0-1-1 0-2-1-3-1-1 1-1 0-2-1-1-1-2-1-1-1-3-1-2-2-2-1 0-2-3-1-2 1-1-2-3-1-2-2 0-1-2 0-2-2-2-2-3-1-2-2 0 1-1 0-2-1 3-1 1-2 0-1-2-2-4 1 4 2 2 2 5 1 1 1 2 2 3 0 2 3 3 1 3 0 3 1 4 1 0 2 1 1 4 1 2 2 1 3 3 2 2 1 1 3 2 0 2-1 1 1 0 1 2 2 1 1 0 3 0 3-1 2-1 1 0 3 0 2-1 2 0 1-1 1 0 0 2 0 2-1 1-1 3-1 3-2 3-2 5-2 3-4 3-2 3-5 2-2 3-3 3-1 1-2 2-1 1-1 0-1 2 0 2-1 1ZM0 42l7 2 7 3 0 1 2 1-1-2 8 0 5 3-3 1-4 1 0 2-1 1-3 0-2-1-4-1 0-1-3-1-3 1-2-1 1-1-4 0 2 2-2 1ZM636 127l1 2 2 1 2 1 4 0 5 0-1-3 1-3-2-1 0-2-2 0 1-2 3 0 2 0-2-2-1-2-2 1 0 2-1-2 0-2-3-1-2-3-1-1 2 0 1-2 2-1 2 1 1-3-1-2-2 0-3 0-3 1-3 0-1 2-3 1-2 2 2 3 0 2 3 3 1 2 2 2 1 0-1 1-1 2-1 2ZM108 40l6 0 4 2 3 0 2-1 4-1 4 0 4-1 4-1 2 2 2-1 1-1 2 0 5 2 3-2 1 3 3-1 1-1 4 0 4 2 6 1 4 0 3 0 4 1-4 2 5 0 7 0 3-1 3 2 3-1-3-1 2-1 3 0 2-1 2 1 3 1 3 0 5 1 4 0 5 0-1-2 3 0 4 1 0 2 2-2 2 0 1-3-3-1-3-1 0-3 4-2 3 0 3 1 4 3-3 2 5 0 0 3 4-2 4 2-1 2 2 2 3-2 2-3 0-3 4 0 4 1 4 1 0 2-2 1 2 2 0 1-6 2-3 0-3-1-1 2-3 2-1 1-3 2-4 0-2 2 0 1-3 1-4 2-3 3-1 2 0 4 4 0 1 3 2 2 4-1 5 1 2 1 2 2 4 1 3 1 4 0 3 0 0 3 1 2 2 3 4 3 2-1 2-3-2-4-2-2 5-1 3-2 1-2 0-2-2-2-3-2 3-3-1-2-1-5 2 0 5 0 3 1 2-1 2 1 4 1 1 2 5 0 0 2 1 4 2 0 2 2 4-2 3-3 2-1 2 2 3 4 3 3-1 2 4 1 3 2 4 1 2 1 1 2 2 0 1 1 0 3-2 2-2 0-4 1-4 3-4 0-6 0-5 0-2 0-3 2-3 1-4 4-4 2 3 0 4-4 6-2 4 0 3 1-3 2 1 3 1 2 4 1 4 0 3-3 0 2 2 1-4 2-6 1-2 1-4 2-2 0 0-2 5-2-4 0-3 0-3 2-3 1-3 1-1 2-1 2 1 1 1 0 0-1 1 2-2 0-2 0-2 1-1 0-1 0-3 1 4-1-3 1-2 0 0 3-2 2-2-1 1 1 1 2-1 1-2 2 1-2-2-1 0-2-1 1 1 2-2-1 2 1 0 3 1 1 1 3-2 2-3 0-2 2-1 0-2 1-3 3-2 1-1 2 0 2 0 2 1 2 1 2 0 1 2 4 0 2-1 1 0 1-2 0-1-1-1-1-1-2-2-2 0-1 0-2 0-2-3-2-1 0-2 1-2-1-2-1-3 0-3 0-2 0-1 1 1 1-2 1-2 0-3-1-2 0-2 0-2 0-2 0-3 3-2 1-2 1 0 1 0 2 0 2-1 3 0 2-1 4 0 1 1 2 0 1 1 2 2 2 1 2 1 1 3 1 1 1 2-1 2 0 2-1 2 0 2-1 1-2 0-2 2-2 3 0 3 0 1-1 1 2-2 2 0 2 0 1-1 3-1-1 0 1 0 1 0 2 0 1-1 1 0 2 1 0 2-1 2 1 1-1 1 0 2 0 1 0 1 0 1 1 2 1 1 1 0 2-1 2 0 1 0 2-1 1 0 2 1 1 0 1 1 2 2 1 1 1 1 0 1 1 1 0 1-1 2-1 2 0 2 0 1 1 2 1 2 0 2-2 1 0 0-3 2-1 2-1 2 0 2-1 1-1 2-1 1 1 0 1-2 1 1 1 0 1-1 2 1 2 1 0 1-2-1-3 3-1 0-2 2 2 2 0 1 1 3 1 3 0 2 1 2 0 1-1 4 0 3 0-2 0 1 2 2 0 2 1 0 2 2 0 1 1 2 1 2 2 0 1 1 0 1 2 1 1 4 0 2 0 3 0 3 1 3 3 1 1 1 1 2 5 1 1 0 1-2 3 6 1 0 3 2-2 4 1 4 2 2 1-1 2 3-1 6 1 4 0 4 3 4 3 2 1 2 0 1 1 1 3 1 2-2 4-1 2-4 4-2 3-2 3-1 2 0 5-1 4 0 2-1 1 0 4-3 3-1 3-2 1-1 2-3 0-4 1-2 1-3 1-3 2-3 3 0 2 0 2 0 3-1 1-2 2-3 4-2 3-2 1-1 3-2 1-1 2-4 1-2 0-1 0-3-1-2 0-1-1 0 1 3 2 0 2 1 1 0 2-2 3-5 2-5 0-3 0 0 2 0 2 0 1-1 1-3 1-3-1-1 0 1 3 1 1 2-1 1 2-3 0-2 2 0 3-1 1-3 0-2 2-1 2 3 2 3 1-1 2-4 2-1 3-3 1-1 1 1 3 2 2-1 0-3 0-1 0-3 1 0 3-1 0-4-1-3-2-3-1-1-2 1-2-2-2 0-4 1-3 3-2-4-1 3-3 0-4 4 1 1-6-2-1-1 4-1-1 0-4 1-5 2-2-1-2 0-3 1-1 1-4 2-4 2-5-1-4 1-2-1-3 2-4 1-5 0-6 1-6 0-5 0-4-3-1-1-1-5-3-5-3-2-2-1-2-2-5-3-5-3-5-1-2-1-2-2-1-2-2 1-1-1-3 1-1 2-2 1-2 0-1-1 1-2-1 0-4 2-2 1-2 0-1 1 0 2-2 1-1-1-1 1-1 2 0 1-2 1-1-1-1 0-2 0-2 0-3-1-2-2-2 0-2-2-1-1 1-2 1 1 2-1 1-2 0 0-1-1 0-1-1-1 0-2-1-2 0 0-1-1-1-1-1-1-1-1 1-2-1 0-2-1-1 0-1-1-1-2-2-2-2 1-1-1 1-2 0-1-1-1 0-1 0-3-1-1 0-2-1-1-1-3-3-2-1-2-1-2 1-2 1-1 0-2-1-2 0-3-2-2 0-3-1-2-2-3-1-2 0-2-2-3-1-1-2-1-1 1-1 1-2-1-1-1-2-1-1-2-3-3-2-1-2-2-1-1-2-1-1-2-1 0-2-2 0-1-2-2-1-1-3-1-2 0-1-2-1-3-1 0 1 0 1 0 3 1 1 2 2 2 1 1 2 1 2 2 1 1 3 1 1 0 1 1 2 1 0 1 1 1 1-1 2-1-2-2-1-2-2-2 0 0-2 0-2-1-1-2-1-1 0-2-1-2-2 2 0 1-1 0-1-2-2-2-1-1-2-1-1-1-3-1-2-1-1-2-2-1 0-2-1-1-1-2 0-1-2-3-3-2-4-2-2-2-2 0-2-2-2 1-2 0-2-1-2 1-3 1-3 0-2-1-4-1-2 0-2 4 1 1 3 1-3-1-3-6-3-2-1-5-1-2-2 1-2-4-1 0-2-4-2 0-2-1-1-3-1 0-2-4-2-1-3-3 0-5 0-3-1-6-3-2-1-5-1-4 1-5-2-4-1-3 1 1 2-2 0-3 0-2 1-3 1-1-2 1-2 3-1-4 1-2 1-4 2 2 2-2 2-3 1-3 1-1 1-4 1-1 2-3 1-2 0-3 1-3 0-2 1-5 1 3-2 3 0 3-2 3 0 1-1 4-2 3-2 0-2 2-1-3 1-3 0-2-1-1-1-3 1-2 0 0-1 0-1-1-1-4 0-2-1-2-1 0-1-2-1 1-2 2-2 1-1 2 0 2 0 2-1 2 0 2-1 0-1-2-1 2-1-1 0-3 1-3 0-4 0-4 0-1-1-4-2 4-1 6-2 2 0 0 2 6 0-2-2-4-1-2-1-2-2-4-1 1-1 5 0 4-1 0-2 3-1 3 0 5-2 3 1 4-2 4 1 2 1 2-1 4 1 4 1 3-1 6 1 5 1 2 0 4-1 4 1ZM267 60l2-1 4 0-4 2-2 0ZM278 31l-3-1 0-1 2-1 6 1 5 1-3 1-3 0-3 0ZM277 61l1-1 1 0 0 2-1 0-1-1ZM240 25l-2 1-4 0-3-1 2-1 4-1 2 1ZM239 18l-1 0-5 0 5-1 2 1ZM231 15l4 1-1 1-4 0-3-1-1-1 0-1 4 0ZM255 27l-4-1-8-1-1-1 0-1-3-2-6 0-3-1 1-1 6 0 3 1 6 0 2 1-1 1 3 1 2 0 4 0 4 1 4-1 6 0 5 0 3 1 0 1-1 1-5 1-3-1-8 1ZM191 16l4 1-6 1-4-1 2 0ZM192 14l3 1-3 1-5 0 3-2ZM346 91l-2 2-2 2 2-1 2 1 1 1 2 0 2 1 0 2 2-1 0 2 1 1-1 3-2 0-1-1 0-2-4 2-2 0 2-1-2-1-3 0-6 0 2-2-2 0 3-2 3-5 1-1 3-1 1 0ZM267 52l3 1 3 1 0 2 3-1 1 1-2 1-4 0-2-2-3 2-4 1-1-1-3 0 2-2 0-2 1-2 2 0 1 1 1 0ZM281 32l3-1 6 2 4 1 0 1 5 0 3 1 7 1 2 2 3 2-5 1 6 2 5 1 4 2 4 1-1 2-5 3-3-1-4-3-4 0 0 2 3 1 3 2 2 0 1 3-1 2-3-1-7-2 4 3 3 1-7 0-6-1-4-2-3-2-4-1-8 1-2-1 2-2 5 0 6 0 0-3 3-3-1-1-1-1-4-1-6-1 2-1-3-1-2-1-2-1-2 1-5 1-10-1-6-1-4 0-3-1 3-1-4-1-1-2 3-3 2-1 8-1-2 2 2 2 2-2 7-2 5 3 0 2ZM237 27l6 0 6 1-5 3-3 0-3 2-3 0-2-2 0-2 2-1ZM159 22l4-2 6-2 4 0 4 0 0 2-2 1-3 0-5 1-4 1ZM131 83l3 0-1 3 3 2-1 0-2-1-1-1-2-1 0-1 0-1ZM207 13l5 0 8 1 2 2 1 1-4 0-5-1-6 0 3-1-4-1ZM157 99l-1 0-5-1-3-2-4-2-1-1 3 0 2 0 3 1 2 2 3 1ZM162 27l4 0 7 0 3 1 3 1-3 1-7 2-4 2 0 1-7 1-1-1-7-1 1-1 2-3 3-1-3-2ZM201 23l2-1 3 0 0 2-1 1-10 0-7 2-4 0 5-2-12 0-4-1 4-2 2-1 8 1 5 1 5 1-4-3 3-1 2 0 1 2ZM204 30l3 1 2 3 1 2 4 2 6 1-1 1-4 0 1 1-1 1-5 0-4-1-4 0-5 1-7 1-5 0-1-1-4-1-3 0-3-2 2 0 4-1 4 0 4 0-6-1-6 0-4 0-1-1 6-1-4 0-5 0 3-3 2-1 7-1 3 0-2 2 7-1 4 1 3-1 2 1 2 2 2-1-2-3 2 0ZM221 31l-3-1 3-2 4 1 5-1-2 3 4 1-1 2-4 1-3 0-2-1-7-2 0-1ZM204 29l4 0 2 0-3 2-4-2ZM226 20l3 2 0 1-2 2-4 0-3 0 0-2-5 0 0-2 3 0 4-1 4 1ZM233 9l2 0 3-1-1 0 6 0 4 1 5 1 4 0 2 2 4 1-4 1-5 2-5 0-6 0-3-1 0-1 2-1-5 0-3-1-2-1ZM246 6l4-1 3 0 5 0 5-1 3 0 3 1 2-2 4 0 5-1 8 0 2 1 8-1 6 0 6 0 7 1 6 0 5 1-7 2-6 0-3 1 6 0-6 2-5 0-5 3-5 0-2 1-9 0 4 0-2 1 3 1-3 1-4 1-2 1-4 1 6 0-8 3-7-1-8 0-4 0-6 0 0-1 5-1-1-2 2 0 7 1-4-2-4 0 2-1 5-1-3-2-1-2 7 1 2 0 5-1-7 0-9 0-5-1-3-1-3-1ZM291 46l-2 1-3 0-1-1 2-2 2 0 2 0 0 2ZM233 40l1 1-1 1-4-1-2 1-4-1 2-1 2-2 3 1 2 1ZM321 95l4 0 3 1-1 1-4-1ZM322 103l1 1 2 0 3 0-2 1-1 1-3-2-1 0ZM68 178l2 1-1 1-1 1-1-1-1-1 1-2ZM67 176l-1 0-1-1 1 0ZM65 175l-2 0 0-1ZM61 173l1 1-1 0-1-1ZM57 172l-1 0 1 0ZM38 66l2 0 0 1-2 0-1 0-2-1ZM74 72l2 0 1 1-2 2-3 1-1-1-1-1 3-1ZM23 56l2 1 1-1 3 1 2 1-2 0-2 0-1-1-3 0ZM892 241l4 1 6 2 2 1 1 2 1 2 4 1 1 2-3 0 1 2 2 2 2 3 2 0 0 1 2 0 2 2-2 1-3-1-3-1-2-1-2-2-1-2-4-1-2 0-2 1 0 2-2 1-1 0-3 0-3-3-3 0-4 1 1-3 2 0-1-3-1-3-5-2-3 0-4-3-1 2-1 0 0-1 0-1-2-2 3-1 2 0-5-1-1-1-2-1-1-1 3-1 2-1 4 1 1 2 0 4 3 2 3-3 3-2 2 0 3 1 2 1ZM924 244l1 0 0 2-1-1-1-1-1-1-1-1-2-1 2 0 2 1ZM920 250l-1 0-2 1-1 0-2-1-2-1 3 0 1 0 1-2 0 2 2-1 2-1 0-2 2 0 0 2-1 2-1 0ZM930 248l2 3 1 0-1 2-1-2-1-1-1-2 1-1ZM847 259l-1 2-3 1 0-1 1-2 3-1 3-1 2 0 2 0-1 0-3 2ZM873 252l0-2 1-2 0 3ZM821 218l2-2 1-2 1 0 2 1 0 2 2 0 2 1 0 1-2 1 0 1-2 1-1 2 2 3-1 1 4 3-4 0-1 2 0 3-2 1 0 3-1 4-4 1-1-2-2 0-1-1-4 1-1-1-2 0-2-1 0-3-2-1-1-2 0-3 0-2 2-2 2 1 2-1 0-2 2-1 3 0 2-2 1-2 1-1ZM859 241l3 1 1 2-2-1-2 0-2 0-2 0 1-2ZM852 244l-1-1-1-1 3 0 0 1ZM855 227l1 2 1 0 0 1 0 3-1-1-1 2 2 1-2-1-1-3 0-3ZM841 231l4 0 2-2-1 3-2 1-3-1-5 0-2 1-1 2 3 2 1-1 6-1-1 1-1 0-1 2-2 1 2 3 2 4 0 2-1 1-1-1 1-2-3 1 0-2-2-2 0-2-2 0 0 4 1 4-2 0-1-1 0-2 0-3-1 0-1-2 1-2 0-2 2-4 0-1 3-2 2 1ZM834 262l-4-2 3-1 2 1 0 2ZM837 257l2 0 2-1 0 1-4 1-4 0 0-1 2-1ZM828 257l2-1 1 2-3 0-2 0-2 0 1-1 2 0 0-1ZM801 251l6 1 1-1 5 2 1 1 4 1 3 2-3 1-3-1-2 0-3-1-3 0-3-1-2 0-1 0-5-1-1-1-2-1 2-2 3 0 2 1ZM790 236l0 2 1 2 2 0 2 2-1 3 0 5-3 0-2-3-4-2-1-2-2-2-2-2-2-4-2-2-1-3-1-2-3-1-1-3-2-1-3-3 0-2 1 0 5 1 2 2 2 2 2 2 3 3 2 0 3 1 1 3 2 1-1 2 2 1ZM309 380l1 1 2 2 3 2 4 0-1 2-3 0-1-1-1 1-2 1-2 0-1-1-2 0-3-1-4-1-3-2-3-3 2 1 4 2 3 1 2-2 1-2 2-1ZM301 183l-2 0-1 0-2 0-1 0-2-1 3 0 2 0 1-1-1-1 0-1-2 0 2-1 3 1 2-1 2 1 1 1 2 0 1 1 1 1-1 1-1-1-1 0-2 1-1 0-1 0-1 1-1 0ZM760 8l6 0 6 1 6 3 0 2-6 1-8-1-5-1-2-2-4-1ZM786 13l7 2-1 1-16 1 5-4 3 0ZM886 22l7 0 10 1-2 2-10 0-5 1-6-2 2-2ZM912 24l7 1-4 1-4 0-5-1 1-1ZM889 30l2-2 4 0 4 1-4 1-6 0ZM625 9l5 0 4 0 2 0 3 0 4 0-1 1-4 0-2 0-4 1-3-1 2 0ZM649 28l6-2 0-1 6-2 9-1 9-1 5-1 5 0 2 1-2 1-9 1-9 1-9 3-4 3-4 2 1 3 5 2-2 0-9 0-1-1-5-1 0-2 3 0 0-2 5-2ZM897 84l1 3 0 3 1 2 3 5-4-1-2 4 3 3 0 2-2-2-2 3-1-3 0-2 0-3 1-2 0-4-2-3 1-4 2-1-1-1 1-1ZM0 35l3 0 4 0-3 1-4 1 0-2ZM1000 35l0 2-3 0-1-1ZM281 159l1 0 2 0-3 1ZM284 158l2 1 0 2-1-1-1-1ZM283 163l2 3 0 1-2-1-1-1ZM330 377l3-1 3 0 1-1 3 2-5 1-1-1-3 2ZM542 12l1-1 4 0 4 1 9 2-7 1-2 2-2 1-1 2-4 0-6-2 3-1-4 0-6-3-2-2 8-1 1 1ZM576 11l-4 1-8 1-8-1-5-1-3-1 9-1 4 1 3-1 7 1ZM569 17l-7 1-4 0 1-1-1-1 6-1 1 1ZM370 4l9-2 10 0 4-1 10 0 22 0 17 3-5 1-11 0-15 0 2 0 10 0 8 1 5-1 3 1-3 2 7-1 13-1 9 0 1 2-11 2-2 0-9 1 7 0-3 2-3 2 0 3 4 1-5 1-4 0 5 2 1 2-3 1 3 2-6 0 3 1-5 2-3 0 3 2 0 1-5-1-2 0 4 1 4 2 1 2-5 1-3-1-3-2 1 2-3 1 7 0 4 1-8 2-7 2-8 1-3 0-3 1-4 3-6 2-2 0-4 1-4 1-2 1 0 2-1 2-5 2 1 2-1 3-1 2-4 1-5-3-5 0-3-1-2-3-4-3-2-2 0-3-4-2 1-2-2-1 3-4 4-1 1-1 1-2-3 1-2 0-2 1-4-1 0-2 1-1 3 0 5 0-5-1-2-1-3 0-2-1 3-2-2-1-2-2-3-3-4-1 0-1-7-2-6 0-8 0-6 0-4-1-4-2 7 0 6-1-12 0-7-1 1-2 10-1 10-1 2-1-8-1 2-1 10-2 4-1-1-1 7-1 8 0 9 0 3 1 7-2 7 1 4 1 5 0-6-1ZM691 368l2 1 3 1-1 1-4 1 0-2 0-1ZM524 115l2-1 1 2-1 2-2 0 0-2ZM316 182l1 0 0 1-2 0-2 0 0-1 1 0ZM285 182l1 0 2 1-2 1-2 0-2-1 2-1ZM271 169l3 0 2 0 3 1 1 1 2 0 4 2 1 1 3 1 2 1 2 1-2 1-2 0-2 0-4 0 2-1-1-1-2 0-2-3-1 0-3 0-4-1 0-2-3 0-2 2-1 0-2 1-1 0 1-1 1-1 1-1 2-1 2 0ZM638 268l0 1 1 2 0 3 1 2 0 1-1-1 0 3-1 1-1 3-1 3-1 4-2 6-1 3-1 4-2 0-3 1-1 0-3-1 0-2-1-2-1-2 0-2 1-2 1-1 1-3 1-2-1-1-1-2 0-2 1-2 0-2 2 0 1 0 1-1 2 0 1-1 3-2 0-2 0-1 1 1 2-2 0-2 1-1ZM964 277l2 2-2-1ZM963 277l0-3 1 1 1 2-1 0ZM573 135l-4 1-4-1 1-1 1 1 3 0 2 0 0 1ZM483 84l0 2-2 2-5 2-4-1 3-3-2-2 4-3 2-1 2 0 3 2ZM960 292l3 2 1 1-1 1-1-1-2-1-2-2-2-2 2 0 2 1ZM950 262l-1 1-1-1 2 0ZM949 260l-2-2-1-2 2 3ZM947 261l-1 0-2 0 0-2 1 0 1 1ZM943 256l1 1-2-1-1-1-2-1 2 0ZM936 253l2 1-2-1-1-1ZM991 345l-1 1-1 2-2 1-2-1 2-2-1-2-3-1 0-1 2-1 0-2 0-2-1-2-1-1-2-3-1-2 1 0 1 2 2 0 1 3 2 3 0-2 1 1 1 2 2 1 2 0 1-1 2 0-1 3-1 1-2 0 0 2ZM971 354l3-1 1-2 2-2 1 0 0-2 2-1 1 1 0 1 2-1 1 1 0 1-1 2-2 2-1 1 1 1-2 0-3 1 0 2-2 3-2 1-2 1-2 0-2-1-3 0 0-1 1-2 4-3 1-1ZM910 347l2 0 0 3-1 3-3 2-2-1-2-2 0-2-2-2 0-2 2 1 3 1 1-1ZM850 323l-3 1-2 1 0 1-2 1-2 1-2 0-2-1-2 1-2 0-2 1-2 1-1 1-2 0-2 0-3-2-1-1 0-1 1-1 0-1 1-2-1-2-1-3-1-1 1-2-2-2-1-2 0-2-2-2 0-1 1 1-1-2 1 0 1 1 0-1-1-2-1-2 0-2 1-2 0-1 1-2 0 2 1-2 3-1 1-1 2-1 1 0 3-1 2 0 1-1 2 0 3-1 1-1 1-2 2-1 0-1 0-2 2-2 1 2 1 0-1-2 1-1 1 1 0-2 2-2 1-1 1 0 1-1 2-1 1 0 2 1 2 2 1 0 2 0-1-2 2-2 1-1 1-2 2-1 1 1 2-1 0-1-2-1 2-1 1 1 2 1 2 1 3 0 1 0 1 0 2 1 0 1-1 1-1 1-1 2-1 1 3 3 2 0 1 1 2 2 2 1 3 2 2-1 1-2 0-1 1-2 1-2-1-1 0-3 0-2 1-1 0-2 1-1 1-2 1 1 0 2 1 2 1 1 0 2 0 1 1 2 2-1 0 1 2 1 0 1 0 3 1 1 1 3 0 1 1 2 3 1 2 2 1 1 2 2 1 3 1 0 1 1 1 3 2 1 1 1 3 3 0 2 0 1 0 2 2 2-1 3 0 1-1 3 0 1 0 2-2 3-2 1-1 2-1 2 0 2-1 1-1 2 0 2-2 2-3 0-3 1-1 2-2 1-2-1-2-1 1-1-2 0-2 2-2-1-2 0-2 0-2-1-2-2-1-2 0-1-2-1-2-1 1-1-1-2-1 2-3 0 2-1 0-2 1-1 0-2-2 2-2 1-1 2-2-1 0-1-2-2-2-1-3-3-2 0-2-1-5 0-4 1-3 1ZM727 212l0 3-1 1-3 1-1-2-1-4 2-5 2 2 1 2ZM804 183l-2-1 0-2 1-2 3-1 2 1 0 1-1 1-1 1ZM838 166l-1 4-2 2-1-2 0-2 1-3 2-2 2 1ZM541 127l2 0-1 2 0 3-2-1-2-1-3-1 0-2 3 1ZM524 120l2-1 1 2 0 4-1-1-2 1-1 0 0-4 0-1ZM534 77l1 2-1 2-3-1-1-2ZM491 85l1-2-2-1-3-1 0-2-3 1 0-3-1-1 1-3 2-3 2 1 4-1-3 3 3 0 3 0-1 2-3 3 3 0 3 4 2 0 2 3 0 1 4 1-1 2-1 0 1 2-2 1-4 0-5 1-1 0-2 1-3-1-2 2-1-1 4-3 3-1-5 0-1-1 3-1-1-1 0-2ZM460 49l-1 2 3 1-3 3-8 1-3 1-3 0-8-1 3-2-7-1 5 0-6-2 2-2 5 0 4 2 4-2 4 1 4-1ZM836 198l-2-2 3 0 1 1-1 2ZM841 206l1-3 1 0 0 2 2-3-1 3-1 1 0 1-3-1ZM851 210l0 2 0 1 0 3-1-3-2 1 1 3-1 1-3-2-1-2 1-1-2-1-1 1-1 0-2 1 1-3 2-1 1-1 1 1 2 0 1-2 2 0-1-2 3 2 0 1ZM829 207l-4 3 2-2 2-2 2-1 1-3 0 2-1 2ZM840 183l-1 1 1 2 0 2-2 1 0 2 0 3 2 0 1 0 3 1 0 2 1 2-3-2-1-1 0 1-2-2-3 0-1 0 0-1 0-2 0 1-1-1-1-1 0-3 1 1 0-5 1-2 2 0 2 1 1-1ZM839 202l0-2 1 1 2 0 0 1-1 1-2 1 0-1ZM849 200l0 3-2-1 1 3-1 0 0-2-2-2 2 1 0-1-2-3 3 1ZM894 124l-2 3 0 3-1 2 0 2-1 2-4 1-5 0-4 3-2-1 0-2-5 1-3 1-3 0 3 2-2 5-2 1-1-1 0-2-1-1-2-2 3-1 2-2 2-1 2-2 6-1 3 1 3-5 2 1 4-3 1-1 2-3 0-3 1-2 3-1 1 4ZM902 111l2-1 0 3-4 1-2 3-5-2-1 3-3 0-1-3 2-2 3 0 1-4 0-2 4 3 2 1ZM868 140l1-1 2 0 1-1 2 0-1 3-1-1-2 1-1 1-1 0ZM591 136l3-1 2-1-2 1-2 2-2 0 0-1ZM329 203l1 0 1 2-3 1 1-1Z", + "countries": { + "Argentina": "M309 380l1 1 2 2 3 2 4 0-1 2-3 0-1-1-2 0-3 0ZM340 317l-1 2-1 3 1 3-1 3 0 1 3 2 0 2 1 1 0 2-2 3-5 2-5 0-3 0 0 2 0 2 0 1-1 1-3 1-3-1-1 0 1 3 1 1 2-1 1 2-3 0-2 2 0 3-1 1-3 0-2 2-1 2 3 2 3 1-1 2-4 2-1 3-3 1-1 1 1 3 2 2-1 0-3-1-7 0-1-2 0-2-2 0-1-1 0-3 2-1 1-2 0-1 1-2 1-4 0-2 1 0 0-1-1-1 0-1-1-1 0-3 1-1-1-3 1-3 1-3 1-1-1-2 0-3 3-2-1-2 2-3 0-2-2-6 2-2 0-3 1-3 1-2 2-2-1-1 1-6 3-1 1-3 2-3 4 1 1 2 1-3 3 1 6 5 2 0 4 2 3 1 0 1-3 5 3 0 3 1 2-1 3-2 0-2 2-1 1 2 0 2-2 2-2 1-3 2Z", + "Australia": "M910 347l2 0 0 3-1 3-3 2-2-1-2-2 0-2-2-2 0-2 2 1 3 1 1-1ZM850 323l-3 1-2 1 0 1-2 1-2 1-2 0-2-1-2 1-2 0-2 1-2 1-1 1-2 0-2 0-3-2-1-1 0-1 1-1 0-1 1-2-1-2-1-3-1-1 1-2-2-2-1-2 0-2-2-2 0-1 1 1-1-2 1 0 1 1 0-1-1-2-1-2 0-2 1-2 0-1 1-2 0 2 1-2 3-1 1-1 2-1 1 0 3-1 2 0 1-1 2 0 3-1 1-1 1-2 2-1 0-1 0-2 2-2 1 2 1 0-1-2 1-1 1 1 0-2 2-2 1-1 1 0 1-1 2-1 1 0 2 1 2 2 1 0 2 0-1-2 2-2 1-1 1-2 2-1 1 1 2-1 0-1-2-1 2-1 1 1 2 1 2 1 3 0 1 0 1 0 2 1 0 1-1 1-1 1-1 2-1 1 3 3 2 0 1 1 2 2 2 1 3 2 2-1 1-2 0-1 1-2 1-2-1-1 0-3 0-2 1-1 0-2 1-1 1-2 1 1 0 2 1 2 1 1 0 2 0 1 1 2 2-1 0 1 2 1 0 1 0 3 1 1 1 3 0 1 1 2 3 1 2 2 1 1 2 2 1 3 1 0 1 1 1 3 2 1 1 1 3 3 0 2 0 1 0 2 2 2-1 3 0 1-1 3 0 1 0 2-2 3-2 1-1 2-1 2 0 2-1 1-1 2 0 2-2 2-3 0-3 1-1 2-2 1-2-1-2-1 1-1-2 0-2 2-2-1-2 0-2 0-2-1-2-2-1-2 0-1-2-1-2-1 1-1-1-2-1 2-3 0 2-1 0-2 1-1 0-2-2 2-2 1-1 2-2-1 0-1-2-2-2-1-3-3-2 0-2-1-5 0-4 1-3 1Z", + "Austria": "M547 100l0 1-2 0 0 2-3 1-1 0-3 0-4-1-3 0-2 0-1 0-2-1 1-1 2 1 3-1 2 0 1 0 1 0 0-2 1 0 1-1 2 0 1-1 4 1 1 0 1 0 0 1Z", + "Bangladesh": "M757 172l0 2 0 2-1-2 0-1-1-1-1-2-3 0 1 1-1 2-1-1-2 0-1 0 0-2-1-2 0-2-1-1 0-1 2-1-2-1 1-2 2 1 2 0 0 2 2 1 3-1 2 1-2 2-1 0-1 2 2 1 0-1 1 0Z", + "Belarus": "M578 77l3 1 2 0 3 1 0 1 1 3 1 0 2 1 0 2-3 0 1 1 0 2-2 0-1 2-1 0-3 0-2-1-1 0-2 0-3-1-3 0-2 0-1 1-2 0 0-1-1-1 2-1 0-1-1-1 0-1 3 0 3-1 1-2 2-1 0-1 1-1Z", + "Belgium": "M517 92l0 2-1 2-3-2-1 1-2-2-1-1-2 0 2-1 2 0 3-1 2 2Z", + "Brazil": "M352 327l-1-1 1-2-1-2-3-1-2-2-1 0-3-2-2 0 4-4 3-2 2-1 2-2 0-2-1-2-2 1 1-2 0-1 0-2-1 0-1 0-1 0 0-1 0-3-3-1-1 0-3 0 0-4-1-2 1-2 1-1 0-2 0-2-2-1 0-1 0-1-5-1-1-3 1-1-1-2-2-1-1 0-2-1-1-1-2-1-3 0-3-3 1-1-1-1 1-3-4 1-2 1-2 1-2 1-2 0-2 0-1 0 0-4-2 1-3 0-1-1-1 0 0-2-1-1-2-3 1-1 2-1 0-2 1-2 3-2 2 0 3-1 1-7 0-2 0-1-1-1 0-2 1-1-1-1 0-1 5 0 2 0 1 1 2 1 2 0 3-1 1-1 0-1 2-1-3-1 0-1 0-2-1-1 3 0 2 1 3-1 3-1 1-1 1-1 0 2 2 1-2 4 1 1 0 1 2 2 1 0 2-1 1 0 2-1 2 0 0-1 2 0 1-1 2 1 1 0 1 0 2 0 1-1 1-2 2-2 1 1 2 5 1 1 0 1-2 3 6 1 0 3 2-2 4 1 4 2 2 1-1 2 3-1 6 1 4 0 4 3 4 3 2 1 2 0 1 1 1 3 1 2-2 4-1 2-4 4-2 3-2 3-1 2 0 5-1 4 0 2-1 1 0 4-3 3-1 3-2 1-1 2-3 0-4 1-2 1-3 1-3 2-3 3 0 2 0 2 0 3-1 1-2 2-3 4-2 3-2 1-1 3Z", + "Canada": "M159 97l-6-3-2-1-5-1-2-2 1-2-4-1 0-2-4-2 0-2 2-1 0-2-5-2-3-3-1-2-3-1-2-1-1-2-3 1-3 2-2-2-2-1-3-1-3 0 0-16 0-10 6 0 4 2 3 0 2-1 4-1 4 0 4-1 4-1 2 2 2-1 1-1 2 0 5 2 3-2 1 3 3-1 1-1 4 0 4 2 6 1 4 0 3 0 4 1-4 2 5 0 7 0 3-1 3 2 3-1-3-1 2-1 3 0 2-1 2 1 3 1 3 0 5 1 4 0 5 0-1-2 3 0 4 1 0 2 2-2 2 0 1-3-3-1-3-1 0-3 4-2 3 0 3 1 4 3-3 2 5 0 0 3 4-2 4 2-1 2 2 2 3-2 2-3 0-3 4 0 4 1 4 1 0 2-2 1 2 2 0 1-6 2-3 0-3-1-1 2-3 2-1 1-3 2-4 0-2 2 0 1-3 1-4 2-3 3-1 2 0 4 4 0 1 3 2 2 4-1 5 1 2 1 2 2 4 1 3 1 4 0 3 0 0 3 1 2 2 3 4 3 2-1 2-3-2-4-2-2 5-1 3-2 1-2 0-2-2-2-3-2 3-3-1-2-1-5 2 0 5 0 3 1 2-1 2 1 4 1 1 2 5 0 0 2 1 4 2 0 2 2 4-2 3-3 2-1 2 2 3 4 3 3-1 2 4 1 3 2 4 1 2 1 1 2 2 0 1 1 0 3-2 2-2 0-4 1-4 3-4 0-6 0-5 0-2 0-3 2-3 1-4 4-4 2 3 0 4-4 6-2 4 0 3 1-3 2 1 3 1 2 4 1 4 0 3-3 0 2 2 1-4 2-6 1-2 1-4 2-2 0 0-2 5-2-4 0-3 0-2-2 0-3-2-1-1 0-3 2-1 2-1 1-1 0-2 1-5 0-4 0-1 1-3 2-1 1-3 0-3 0-1 1 1 1-4 2-3 0-3 2-2-1 1-2 1-1 1-2-1-2 0-3-3-1-2-1-1-1-1-1-2-1-2-1-3-1-2-1-3 1-4-1-3 1-2-1-3-1-2 0-1-2-1 1-6 0-10 0-9 0-8 0-9 0-8 0-8 0-3 0-8 0ZM267 60l2-1 4 0-4 2-2 0ZM278 31l-3-1 0-1 2-1 6 1 5 1-3 1-3 0-3 0ZM277 61l1-1 1 0 0 2-1 0-1-1ZM240 25l-2 1-4 0-3-1 2-1 4-1 2 1ZM239 18l-1 0-5 0 5-1 2 1ZM231 15l4 1-1 1-4 0-3-1-1-1 0-1 4 0ZM255 27l-4-1-8-1-1-1 0-1-3-2-6 0-3-1 1-1 6 0 3 1 6 0 2 1-1 1 3 1 2 0 4 0 4 1 4-1 6 0 5 0 3 1 0 1-1 1-5 1-3-1-8 1ZM191 16l4 1-6 1-4-1 2 0ZM192 14l3 1-3 1-5 0 3-2ZM346 91l-2 2-2 2 2-1 2 1 1 1 2 0 2 1 0 2 2-1 0 2 1 1-1 3-2 0-1-1 0-2-4 2-2 0 2-1-2-1-3 0-6 0 2-2-2 0 3-2 3-5 1-1 3-1 1 0ZM267 52l3 1 3 1 0 2 3-1 1 1-2 1-4 0-2-2-3 2-4 1-1-1-3 0 2-2 0-2 1-2 2 0 1 1 1 0ZM281 32l3-1 6 2 4 1 0 1 5 0 3 1 7 1 2 2 3 2-5 1 6 2 5 1 4 2 4 1-1 2-5 3-3-1-4-3-4 0 0 2 3 1 3 2 2 0 1 3-1 2-3-1-7-2 4 3 3 1-7 0-6-1-4-2-3-2-4-1-8 1-2-1 2-2 5 0 6 0 0-3 3-3-1-1-1-1-4-1-6-1 2-1-3-1-2-1-2-1-2 1-5 1-10-1-6-1-4 0-3-1 3-1-4-1-1-2 3-3 2-1 8-1-2 2 2 2 2-2 7-2 5 3 0 2ZM237 27l6 0 6 1-5 3-3 0-3 2-3 0-2-2 0-2 2-1ZM159 22l4-2 6-2 4 0 4 0 0 2-2 1-3 0-5 1-4 1ZM131 83l3 0-1 3 3 2-1 0-2-1-1-1-2-1 0-1 0-1ZM207 13l5 0 8 1 2 2 1 1-4 0-5-1-6 0 3-1-4-1ZM157 99l-1 0-5-1-3-2-4-2-1-1 3 0 2 0 3 1 2 2 3 1ZM162 27l4 0 7 0 3 1 3 1-3 1-7 2-4 2 0 1-7 1-1-1-7-1 1-1 2-3 3-1-3-2ZM201 23l2-1 3 0 0 2-1 1-10 0-7 2-4 0 5-2-12 0-4-1 4-2 2-1 8 1 5 1 5 1-4-3 3-1 2 0 1 2ZM204 30l3 1 2 3 1 2 4 2 6 1-1 1-4 0 1 1-1 1-5 0-4-1-4 0-5 1-7 1-5 0-1-1-4-1-3 0-3-2 2 0 4-1 4 0 4 0-6-1-6 0-4 0-1-1 6-1-4 0-5 0 3-3 2-1 7-1 3 0-2 2 7-1 4 1 3-1 2 1 2 2 2-1-2-3 2 0ZM221 31l-3-1 3-2 4 1 5-1-2 3 4 1-1 2-4 1-3 0-2-1-7-2 0-1ZM204 29l4 0 2 0-3 2-4-2ZM226 20l3 2 0 1-2 2-4 0-3 0 0-2-5 0 0-2 3 0 4-1 4 1ZM233 9l2 0 3-1-1 0 6 0 4 1 5 1 4 0 2 2 4 1-4 1-5 2-5 0-6 0-3-1 0-1 2-1-5 0-3-1-2-1ZM246 6l4-1 3 0 5 0 5-1 3 0 3 1 2-2 4 0 5-1 8 0 2 1 8-1 6 0 6 0 7 1 6 0 5 1-7 2-6 0-3 1 6 0-6 2-5 0-5 3-5 0-2 1-9 0 4 0-2 1 3 1-3 1-4 1-2 1-4 1 6 0-8 3-7-1-8 0-4 0-6 0 0-1 5-1-1-2 2 0 7 1-4-2-4 0 2-1 5-1-3-2-1-2 7 1 2 0 5-1-7 0-9 0-5-1-3-1-3-1ZM291 46l-2 1-3 0-1-1 2-2 2 0 2 0 0 2ZM233 40l1 1-1 1-4-1-2 1-4-1 2-1 2-2 3 1 2 1ZM321 95l4 0 3 1-1 1-4-1ZM322 103l1 1 2 0 3 0-2 1-1 1-3-2-1 0Z", + "Chile": "M309 380l0 6 3 0 2 0-1 1-2 1-2 0-1-1-2 0-3-1-4-1-3-2-3-3 2 1 4 2 3 1 2-2 1-2 2-1ZM307 282l1 2 0 2 2 1-1 3 2 3 1 4 2-1-1 4-3 1 0 5 0 2-2 2-1 2-1 3 0 3-2 2 1 5 1 3-2 3 1 2-3 2 0 3 1 2-1 1-1 3-1 3 1 3-1 1 0 3 1 1 0 1 1 1 0 1-1 0 0 2-1 4-1 2 0 1-1 2-2 1 0 3 1 1 2 0 0 2 1 2 7 0 3 1-3 0-1 0-3 1 0 3-1 0-4-1-3-2-3-1-1-2 1-2-2-2 0-4 1-3 3-2-4-1 3-3 0-4 4 1 1-6-2-1-1 4-1-1 0-4 1-5 2-2-1-2 0-3 1-1 1-4 2-4 2-5-1-4 1-2-1-3 2-4 1-5 0-6 1-6 0-5 0-4 1 0Z", + "China": "M804 183l-2-1 0-2 1-2 3-1 2 1 0 1-1 1-1 1ZM723 116l0-2 2-1-3-4 6-2 1 0 2-5 6 1 1-1 0-3 3-1 2-1 1-1 0 2 3 2 4 1 2 2-1 3 1 2 3 0 4 0 3 2 2 0 1 3 2 2 3 0 5 0 4 0 3 0 4 2 3 0 2 1 3-2 4-1 4 0 4-1 2-1 2-1-2-2 2-2 1 0 3 1 3-2 4-1 2-2 2 0 4-1 2 1-2-3-2-1-2 1-3-1-1 1-1-1 2-3 1-2 3 1 4-2 0-1 3-3 1-1 0-2-1 0 2-2 4 0 3 0 4 1 3 1 2 2 1 2 1 1 1 3 4 1 4 2 1 3 4 0 2-2 5 0-1 2-1 1-1 3-2 3-4-1-2 1 1 3-1 3-1 0 0 2-2-2-1 1-4 2 0 1-2 0-2-1-1 2-4 2-2 1-4 1-2 2-3 0 2-1-1-1 2-2-1-1-3 1-3 2-1 1-3 0-2 2 2 2 2 0 0 1 3 1 3-2 2 1 2 0 0 2-4 1-1 1-3 1-1 2 3 2 1 3 2 2 2 2 0 2-2 1 1 2 1 0 0 3-1 2-2 0-2 3-2 4-2 3-4 3-4 2-3 0-2 2-2 0-4 1-3 1-1 3-2 0 0-2 0-1-4-1-1 0-3 0-1-1 0-2-2 0-1-2-3 2-2 0-3 0-1 1-2 0 1 4-2-1 0-1-2 0-1 0-2-1 0-2-1-1-1-3-3 1 0-3 3-3 0-2 0-2-1-1-1-1-2 0-3-1 1-1-1-1-2 1-2-1-4 2-2 2-2 0-2-1-1 0-2 0-1 0-2 3-1-3-1 1-3 0-4-1-2-1-2-1-1-1-2-1-2-1-3-1-1 0-4-1-2-2-1-3 2 0 0-1-1-2 0-2-3-3-4-1-1-3-2-1-1-2 0-1-2-1-1-2 0-1 3-2 2 0 3 0 1-2 3 0 1-1 5-2Z", + "Colombia": "M314 230l-1-1-2 0-5 0 0 1 2 1 0 1-2 0 0 2 1 1 0 1 0 2-1 7-2-1 1-3-2-1-1 0-1-1-2 1-2 0-2-3-1-1-1-1-2-2-2 0-1-1-3 0-1-1-3-2 1-1-1-1 1-1 2 0 1-2 1-1-1-1 0-2 0-2 0-3-1-2 0-1 1-1 0-1 2-1 2-2 1 0 0-3 2-1 2-1 2 0 2-1 1-1 2-1 1 1-2 1-1 1-1 1-1 1 0 2-1 2 2 0 0 1 1 2 0 1 1 2 4 0 1 0 2 2 1 0 2 0 2 0 0 2 0 3 0 2 1 1-1 1 1 1 0 1Z", + "Cyprus": "M591 136l1 0 2 0-2 1-2 0 0-1Z", + "Czechia": "M542 91l1 1 2 1 1 1 3-1 2 1 1 2-1 0-1 1-1 1-1 0-2 0-1 0-3-1-2 1-2 0-2-2-1 0-1-1 2-2 1-1 2 0 1 0Z", + "Denmark": "M528 81l-2 0-2 0-1-2-1-3 2-1 2 0 3-2 0 1 0 2 1 1 0 1-3 1ZM534 77l1 2-1 2-3-1-1-2Z", + "Egypt": "M602 172l-11 0-10 0-12 0 0-10 0-10 0-2 0-2 0-1 1-1 4 0 2 0 3 1 1 1 2-1 2-1 2 0 2 0 1 1 3 0 2 0 1 0 2 4-1 2 0 2-1 1-2 0-1-2-2-4 1 4 2 2 2 5 1 1 1 2 2 3 0 2 3 3Z", + "Finland": "M579 41l0 2 4 2-2 2 3 4-2 2 3 2-2 2 5 2-2 1-2 2-6 3-5 0-5 2-4 0-2-1-3-1 1-3-2-3 2-1 2-2 7-3 2-1-1-1-4-1-1-1 0-4-4-2-4-2 2 0 3 1 4 0 3 1 2-2 2-2 4-1 4 2Z", + "France": "M357 222l-2 2-1 2-1 1-1 1-1-1-2 0 0-1 0-2 1-1-1-1 0-2 1-3 3 1 3 3ZM517 96l1 1 4 0-1 2 0 2-2 0-2 3 0 1 1-1 1 2 1 1-1 3 2 1 0 1-3 2-5-1-4 1-1 1-3 1-3-2-1 1-5-1-1-1 1-2 1-5-3-3-2-2-4-1-1-2 4-1 5 1-1-3 2 1 7-2 1-2 2-1 2 1 1 1 2 2 1-1 3 2ZM524 115l2-1 1 2-1 2-2 0 0-2Z", + "Germany": "M539 84l1 1 0 2 1 2 1 2-2 1-1 0-2 0-1 1-2 1 1 2 1 0 2 2-1 1-1 0 0 2-1 0-1 0-2 0-3 0-2 0-3-1-3 1 0-2 1-2-4 0-1-1 0-1 0-3 0-3 1 0 1-1 1-2 0-2 2 0 2-1 0-1 0-1 2 0 2 0 0 1 2 0 3 1 2-1 3 1Z", + "Greece": "M573 135l-4 1-4-1 1-1 1 1 3 0 2 0 0 1ZM564 119l2 0 2-1 2 1 3 0 0-2 1 1-1 2-2 0-2 0-3 0 2 2-2 0-1 0-2-1 0 2 2 1-1 1 1 1 2 1 0 2-3-1 1 1-2 1 1 2-2 0-2-1-1-2 0-2-1-1-2-2 1-2 1-1 2-1 1-1 2 0Z", + "Hungary": "M561 99l2 1-2 1-1 2-2 2-2 0-2 0-2 1-1 0-2 0-2-2-2-1 1-2 1 0 0-1 2 0 1 1 2-1 1 0 2-1 1 0 2-1 3 1Z", + "India": "M770 155l1 1-1 2-2 0-4 2 0 1-1 2 0 2-2 2-2-1 0 3 0 2-2 1-1-4-1 1-2-1 1-2 1 0 2-2-2-1-3 1-2-1 0-2-2 0-2-1-1 2 2 1-2 1 0 1 1 1 0 2 1 2 0 2 0 1-2 0-3 1 0 2-2 1-4 2-3 3-2 2-3 2 0 1-1 1-3 1-1 0-1 2 1 4 0 2-1 3 0 5-2 0-1 2-2 2 0 1-2 1-2-2-2-4-1-3-1-1-1-3-1-4 0-1-3-4-1-5-1-4 0-3 0-3-4 2-2-1-4-3 1-1 0-1-4-2 2-2 6 0 0-3-2-1 0-2-2-2 3-2 3 0 3-3 2-3 3-3 0-2 2-1-2-2-1-2-1-2 1-1 4 0 4 0 2-2 3 3 0 2 1 2 0 1-2 0 1 3 2 2 4 1-1 2-2 2 3 1 3 2 3 1 4 1 2 1 2 0 3 1 3 0 0-1 0-2 0-1 1-1 1 3 2 1 2 0 2 0 3 0 0-2-1-1 2 0 2-2 4-2 2 1 2-1 1 1-1 1Z", + "Indonesia": "M892 241l0 9 0 9-3-3-3 0-4 1 1-3 2 0-1-3-1-3-5-2-3 0-4-3-1 2-1 0 0-1 0-1-2-2 3-1 2 0-5-1-1-1-2-1-1-1 3-1 2-1 4 1 1 2 0 4 3 2 3-3 3-2 2 0 3 1 2 1ZM847 258l0 1-1 2-3 1 0-1 1-2ZM873 252l0-2 1-2 0 3ZM827 222l-1 2 2 3-1 1 4 3-4 0-1 2 0 3-2 1 0 3-1 4-4 1-1-2-2 0-1-1-4 1-1-1-2 0-2-1 0-3-2-1-1-2 0-3 0-2 2-2 0 2 2 1 2 0 2 0 1-2 1 0 3 1 2-1 2-4 1 0 1-4 3 0ZM859 241l3 1 1 2-2-1-2 0-2 0-2 0 1-2ZM852 244l-1-1-1-1 3 0 0 1ZM855 227l1 2 1 0 0 1 0 3-1-1-1 2 2 1-2-1-1-3 0-3ZM841 231l4 0 2-2-1 3-2 1-3-1-5 0-2 1-1 2 3 2 1-1 6-1-1 1-1 0-1 2-2 1 2 3 2 4 0 2-1 1-1-1 1-2-3 1 0-2-2-2 0-2-2 0 0 4 1 4-2 0-1-1 0-2 0-3-1 0-1-2 1-2 0-2 2-4 0-1 3-2 2 1ZM834 262l-4-2 3-1 2 1 0 2ZM837 257l2 0 2-1 0 1-4 1-4 0 0-1 2-1ZM828 257l2-1 1 2-3 0-2 0-2 0 1-1 2 0 0-1ZM801 251l6 1 1-1 5 2 1 1 4 1 3 2-3 1-3-1-2 0-3-1-3 0-3-1-2 0-1 0-5-1-1-1-2-1 2-2 3 0 2 1ZM790 236l0 2 1 2 2 0 2 2-1 3 0 5-3 0-2-3-4-2-1-2-2-2-2-2-2-4-2-2-1-3-1-2-3-1-1-3-2-1-3-3 0-2 1 0 5 1 2 2 2 2 2 2 3 3 2 0 3 1 1 3 2 1-1 2 2 1Z", + "Ireland": "M483 84l0 2-2 2-5 2-4-1 3-3-2-2 4-3 2-1 1 2-1 1 2 0Z", + "Israel": "M599 142l-1 1-1 2 0 1 1 0 0 1-1 4-2-4 1-1 1-2 0-2 2-1 1 0 0 1-1 0Z", + "Italy": "M529 103l2 0 3-1 4 2 0 2 1 1-2-1-3 1 0 2 1 2 3 1 1 3 3 2 2 0 0 1 3 1 2 1 2 1 0 2-2-2-2 0-1 2 2 1-1 1-1 0-1 3-1 0 0-2 0-3-1-1-1 0-1-1-2-1-1-1-2 0-2-2-3-1-2-2-1-3-1 0-2-1-2 0-1 2-1 0 0-1-2-1 0-2 0-2 1 0 2 0 1-1 2 0 1-1 2 1 1-1ZM541 127l2 0-1 2 0 3-2-1-2-1-3-1 0-2 3 1ZM524 120l2-1 1 2 0 4-1-1-2 1-1 0 0-4 0-1Z", + "Japan": "M894 124l-2 3 0 3-1 2 0 2-1 2-4 1-5 0-4 3-2-1 0-2-5 1-3 1-3 0 3 2-2 5-2 1-1-1 0-2-1-1-2-2 3-1 2-2 2-1 2-2 6-1 3 1 3-5 2 1 4-3 1-1 2-3 0-3 1-2 3-1 1 4ZM902 111l2-1 0 3-4 1-2 3-5-2-1 3-3 0-1-3 2-2 3 0 1-4 0-2 4 3 2 1ZM868 140l1-1 2 0 1-1 2 0-1 3-1-1-2 1-1 1-1 0Z", + "Kenya": "M609 246l-4-2 0-2-10-6-1-3 1-1 1-2 1-2-1-3 0-2-2-1 2-2 2-2 1 1 0 1 3 1 4 2 1 0 3 1 1-1 2-1 1 0 2 0-2 4 0 10 2 2-2 1-1 1-1 0-1 2 0 2-1 1Z", + "Malaysia": "M778 215l3 1 0 2 2-1 1-1 2 2 1 2 0 2 0 1 1 3 2 3-2 1-3-2-3-2-1-2-1-2-1-2-1-1 1-2ZM827 222l-2-1-3 0-1 4-1 0-2 4-2 1-3-1-1 0-1 2-2 0-2 0-2-1 0-2 2 1 2-1 0-2 2-1 3 0 2-2 1-2 1 1 2-1 1-1 0-2 2-2 1-2 1 0 2 1 0 2 2 0 2 1 0 1-2 1 0 1Z", + "Mexico": "M175 143l3 0 3-1 4 2 7 2 5 0 2 0 0-1 5 0 1 1 2 1 1 1 1 2 1 1 1 1 3 1 1-2 3 0 2 1 1 2 1 1 2 2 0 2 1 1 2 1 2 1 1-1-1 3 0 2-1 4 0 1 1 2 0 1 1 2 2 2 1 2 1 1 3 1 1 1 2-1 2 0 2-1 2 0 2-1 1-2 0-2 2-2 3 0 3 0 1-1 1 2-2 2 0 2 0 1-1 3-1-1-2 2-3 0-3 0 0 1-1 0 1 1 1 1 1 2-4 0-1 2 0 1-3-2-2-1-2-1-2 1-2 1-1 0-2-1-2 0-3-2-2 0-3-1-2-2-3-1-2 0-2-2-3-1-1-2-1-1 1-1 1-2-1-1-1-2-1-1-2-3-3-2-1-2-2-1-1-2-1-1-2-1 0-2-2 0-1-2-2-1-1-3-1-2 0-1-2-1-3-1 0 1 0 1 0 3 1 1 2 2 2 1 1 2 1 2 2 1 1 3 1 1 0 1 1 2 1 0 1 1 1 1-1 2-1-2-2-1-2-2-2 0 0-2 0-2-1-1-2-1-1 0-2-1-2-2 2 0 1-1 0-1-2-2-2-1-1-2-1-1-1-3Z", + "Morocco": "M494 136l1 1 0 2 1 3 0 2-3 0-2 1-1 0 0 3-3 1-2 1-2 1-3 0-4 2 0 4 0 1-2 0-2 1-1-1-3 1-1 2-2 4-4 3-1 3-1 2 0 1-6 0 0-1 1-1 1-2 1-3 1-1 2-2 0-2 1-2 2-1 2-2 1-2 3 0 2-2 1 0 2-3 0-3 1-3 0-1 2-2 3-1 2-1 2-3 1-2 2 0 1 1 3 0 3 1Z", + "Netherlands": "M519 85l0 3-1 1-1 0 0 3-1 0-2-2-3 1-2 0 2-1 2-4 4-1Z", + "New Zealand": "M991 345l-1 1-1 2-2 1-2-1 2-2-1-2-3-1 0-1 2-1 0-2 0-2-1-2-1-1-2-3-1-2 1 0 1 2 2 0 1 3 2 3 0-2 1 1 1 2 2 1 2 0 1-1 2 0-1 3-1 1-2 0 0 2ZM971 354l3-1 1-2 2-2 1 0 0-2 2-1 1 1 0 1 2-1 1 1 0 1-1 2-2 2-1 1 1 1-2 0-3 1 0 2-2 3-2 1-2 1-2 0-2-1-3 0 0-1 1-2 4-3 1-1Z", + "Nigeria": "M507 216l1-5 0-1 0-2 2-3 1-1-1-2 0-4 1-1 0-1 4-1 3 1 1 1 1 0 2-1 3 2 1 0 2-2 2 1 2-1 2 1 2-1 3 3 1 1 0 1-2 2-1 2 0 2-2 2-1 1 0 2-1 1 0 1-2 1-2-1-1 0-2 1-2 3 0 2-3 1-1 0-1 1-3-1-1-1-1-2-2-2-2 0Z", + "Norway": "M542 12l1-1 4 0 4 1 9 2-7 1-2 2-2 1-1 2-4 0-6-2 3-1-4 0-6-3-2-2 8-1 1 1ZM586 40l-4 1-3 0 2-1-4-2-4 1-2 2-2 2-3-1-4 0-3-1-2 0-1 0-1 2-5 0-1 1-2 0-2 2-3 3-4 4 1 1-1 1-3 0-2 3 0 4 2 1-1 3-2 2-1 2-2-2-6 3-3 1-4-1-1-3-1-7 2-2 8-2 5-3 5-4 7-5 5-2 7-4 6-1 5 0 4-2 5 0 5 0 9 2-4 0ZM576 11l-4 1-8 1-8-1-5-1-3-1 9-1 4 1 3-1 7 1ZM569 17l-7 1-4 0 1-1-1-1 6-1 1 1Z", + "Pakistan": "M716 135l-2 2-4 0-4 0-1 1 1 2 1 2 2 2-2 1 0 2-3 3-2 3-3 3-3 0-3 2 2 2 0 2 2 1 0 3-6 0-2 2-2-1 0-2-3-2-5 0-4 0-4 1 1-4 4-1 0-1-2-1 0-2-2-1-2-2-1-2 5 2 3-1 1 1 3-1 3-1 0-2 2-2 2 0 2-1 1 0 2 0-1-2 2-2 1 0-1-2 3 0 1-1 0-1 1-1 0-2-1-1 2-1 3-1 3 0 1-1 2 0 2 1 1 3Z", + "Peru": "M306 245l-3 0-2 1-3 2-1 1 0 3-2 1 0 1 1 3 1 1 0 2 1 0 1 1 3 0 2-1 0 4 1 0 2 0 2 4 0 3-1 2-1 2 1 1 0 3-1 3-1 2-1 0-3-1-1-1-5-3-5-3-2-2-1-2-2-5-3-5-3-5-1-2-1-2-2-1-2-2 1-1-1-3 1-1 2-2 0 1 0 2 1-1 1 1 1 1 2-1 0-2 2-2 3-1 3-3 1-2 0-2 2 1 1 1 1 1 2 3 2 0 2-1 1 1 1 0 2 1-1 3Z", + "Poland": "M565 84l0 1 1 1 0 1-2 1 1 1 0 1 2 2-2 2-2 2 0 1-3-1-2 0-1 0-2 1-1-1-1 0-2-2-2 0-2-1-2 0-2-1-1-1-1-1-1-3-1-1 0-2 2-1 4-1 4-1 3 0 3 1 3 0 5 0 2 1Z", + "Portugal": "M475 117l1-1 1 0 1 1 1 0 2 0 1 1-1 1 0 2-1 2-1 0 1 2 0 2 0 1-1 1-1 2-1 0-2 0 1-2-1-2-1 0 0-1 0-2 1-1 0-1 1-2 0-1-1-1Z", + "Romania": "M578 107l2 0 1 0 1 1-1 1-2 3-1 0-2-1-4 0-1 1-4 0-2-1-1 1-1-2-1-1-2 0 0-1-2-1-2-2 2 0 2-2 1-2 2-1 1 0 2 0 2 0 1 1 3-1 2-1 2 1 1 2 1 1 0 2 0 1Z", + "Russia": "M760 8l6 0 6 1 6 3 0 2-6 1-8-1-5-1-2-2-4-1ZM786 13l7 2-1 1-16 1 5-4 3 0ZM886 22l7 0 10 1-2 2-10 0-5 1-6-2 2-2ZM912 24l7 1-4 1-4 0-5-1 1-1ZM889 30l2-2 4 0 4 1-4 1-6 0ZM625 9l5 0 4 0 2 0 3 0 4 0-1 1-4 0-2 0-4 1-3-1 2 0ZM563 82l-5 0-3 0 0-1 4-1 3 1 1 0 0 1ZM649 28l6-2 0-1 6-2 9-1 9-1 5-1 5 0 2 1-2 1-9 1-9 1-9 3-4 3-4 2 1 3 5 2-2 0-9 0-1-1-5-1 0-2 3 0 0-2 5-2ZM897 84l1 3 0 3 1 2 3 5-4-1-2 4 3 3 0 2-2-2-2 3-1-3 0-2 0-3 1-2 0-4-2-3 1-4 2-1-1-1 1-1ZM863 116l0-2 1 0 1-3-1-3 2-1 4 1 2-3 1-3 1-1 1-2-5 0-2 2-4 0-1-3-4-2-4-1-1-3-1-1-1-2-2-2-3-1-4-1-3 0-4 0-2 2 1 0 0 2-1 1-3 3 0 1-4 2-3-1-3 0-2-1-1 0-4 2-4 0-3 1-3-1-3 0-1-1-3-1-3-1-3 1-3 0-4-1 0-2-4-1-2 0-3-1-3 3 1 1-3 2-4 0-3-1-1-1-3 0-3-1-4 2-5 2-3 0-1 1-2-2-3 0-2-1-2 0-1-2-1-1-4 1-4-1-2 1-6-7-3-2 1-1-7 3-3 0 0-2-3-1-3 1-1-3-5-1-3 2-7 1-1 0-10 1-2 1 2 2-2 1-2 2 4 2-1 1-4 0-4-1-4 1-3 1-4-1-6-2-4 0-6 3 0 2-3-2-2 3-1 3 2 2 2 0 2 2 0 1 1 0-1 2-3 1-2 2 2 3 0 2 3 3-2 1-1 1-2-2-3-1-1-1-2 0-2 0-4-2-4 0-3-1-4-2-3-1-2-1 2-1 2-2-1-1 4-1-3-1 0-1 2-1 2 0 0-2 1-2-4-1-1 0-2-2-2 1-4-1-1-2-2 0 1-1-2-2-3 0-2 1-1 0 0-2-1-1 3 0 1-1-3-1-1-1-2-2 1-2-3-1-1 1-4-2-1-1 0-2-1 0 1-1-1-3 2-1 3-2-3-2 6-3 2-2 2-1-5-2 2-2-3-2 2-2-3-4 2-2-4-2 0-2 3 0 4-1 3-1 5 2 7 0 11 4 2 1 0 2-3 1-4 1-13-2-2 0 5 2 0 2 0 2 4 1 2 1 0-1-2-2 2-1 7 2 2-1-2-2 7-2 2 0 3 1 2-2-3-2 2-1-2-2 7 1 2 1-3 1 0 1 2 1 4 0 1-2 6-2 9-2 2 0-2 2 3 0 2-1 5 0 4-1 4 2 3-2-3-2 1-1 8 1 4 1 10 3 2-1-3-2-3-1 1-1-2-2 5-3 2-3 2-1 8 1 0 2-2 2 1 1 1 2 0 4 3 1-2 2-5 4 3 1 1-1 3-1 1-2 2-1-1-1 1-2-3-1-1-1 3-3-4-2 5-2-1-2 2 0 1 2-1 2 3 1-1-2 4-1 6-1 5 2-2-2 0-3 4-1 7 0 6 0-2-1 3-2 3 0 6-2 7 0 8-1 3 0 6-1 5 0 1-1 2-1 7-1 5 0-4 1 6 1 1 1 2-1 9 0 6 2 2 1-1 1-3 1-7 1-2 1 3 1 4 0 3 0 1 2 2-1 4-1 9 1 1 1 11 1 0-2 6 0 5 0 4 1 1 2-1 1 3 3 5 1 2-3 5 1 5-1 5 1 2-1 5 1-2-3 3-1 25 2 3 1 7 3 11-1 6 0 2 2 0 2 3 0 4 0 5 0 5 0 5 0 5 3 3-1-2-2 1-1 9 0 6 0 8 2 4 1 0 11-4 1-3 0 2 1 2 3 1 0 1 2-6 0-8 2-3 0-4 2-4 2-1 1-4-2-7 2-1-1-3 1-4 0-1 2-3 3 0 1 3 0 0 4-3 0-1 3 1 1-5 1 0 4-5 0 0 3-4 3-2-2-1-4-1-7 1-3 2-2 1-1 4-1 5-3 5-3 5-3 2-3-4 0-1 2-7 3-2-3-8 1-7 4 3 2-7 1-4 0 0-2-4 0-3 1-9 0-9 0-9 6-11 6 5 1 1 1 3 1 2-1 3 0 4 3 0 2-2 3-1 3-1 5-4 4-1 2-4 3-4 3-1 2-4 1-2 0-2-1-3 2ZM0 42l7 2 7 3 0 1 2 1-1-2 8 0 5 3-3 1-4 1 0 2-1 1-3 0-2-1-4-1 0-1-3-1-3 1-2-1 1-1-4 0 2 2-2 1ZM0 35l3 0 4 0-3 1-4 1 0-2ZM1000 35l0 2-3 0-1-1ZM593 106l3 0 1 0 2 1 2 0-3 2-4 1-1 0 0-2-3-1 3-1Z", + "South Africa": "M545 313l2-2 1 1 0 1 2 0 1 1 2 0 2-2 0-10 3 3-1 2 3 1 1-2 2 0 0-2 2 0 1 0 1 1 3 0 1-1 1-2 2 0 0-1 1-2 3-2 4-2 1 0 1 0 3 0 1 4 1 2-1 3-1 1-1 1-1 1 2 2 2 0 0-1 2 0-1 2 0 2-1 1-1 2-2 1-1 2-2 2-3 3-2 1-2 2-3 1-1 0-2 0-1 1-3-1-2 1-1-1-3 1-3 1-1 1-2 0-1-1-2-1 0-2-1-2 1-3-2-2-2-3ZM580 314l-1-1-1 0-2 2-1 1 2 2 2-1 1 0 1-2Z", + "South Korea": "M850 128l2 0 1-1 2 0 1 0 3 2 1 2 0 3-1 2-3 0-2 2-3 0 0-2 1-2-2-3 2 0Z", + "Spain": "M479 130l1-2 0-1 0-2-1-2 1 0 0-1 1-3 1-1-1-1-1 0-2 0-1-1-1 0-1 1 0-2-1-1 4-2 3 0 4 0 3 1 2 0 5 0 1 1 5 1 1-1 3 2 3-1 0 2-2 2-4 0-2 3-1 2 1 2-1 1-1 2-2 0-2 2-3 1-3-1-2 1-1 1-1 0-1-1-1-1Z", + "Sweden": "M531 70l1-2 2-2 1-3-2-1 0-4 2-3 3 0 1-1-1-1 4-4 3-3 2-2 2 0 1-1 5 0 1-2 1 0 4 2 4 2 0 4 1 1-4 1-3 2 0 1-4 3-5 2-2 4 2 2 2 1-2 4-3 0-1 5-2 2-3 0-2 2-3 1-1-3-2-3Z", + "Switzerland": "M527 101l-1 1 2 1 1 0 0 1-1 1-2-1-1 1-1 1-2 0-2 0-1 0-1-2-1 1 0-1 2-2 1 0 3-1 1-1Z", + "Taiwan": "M838 166l-1 4-2 2-1-2 0-2 1-3 2-2 2 1Z", + "Thailand": "M792 194l-2-1-4 1-2 2 1 3-3-1-2 0 0-2-2 0 0 3-2 4-1 3 1 2 1 0 2 2 0 3 2 1 1 1 2 1-1 1-2 1 0-2-3-1-1-1-1-1-1-2-1-1-1 2 0-2 0-2 1-2 1-3 2-3-1-2 0-2-1-1-2-2 0-2 2-3-1-1-2-2-2-3 2 0 1-3 2-1 2-1 1 0 1 0 0 2 2 0 0 3 0 3 3-2 2 0 3 0 2 2 0 3 2 2 0 2Z", + "Turkey": "M624 130l-1 1-4-2-1 1-4 0-1 0-3 1-3 0-1 0-3 1-1 1-2 1-1-1 1-1-1 0-3-1-1 2-5 0-2-1-3-1-1 2-2 0-2-2-3 1-2-3-2-2 1-2-1-1 3-3 4 0 1-2 6 0 3-2 3 0 5 0 5 2 4 1 3-1 2 0 3-1 3 0 3 1 0 3 2 0 1 1-1 1 0 3 0 1ZM573 117l2-1 3 1 0 1 3 1-4 0-1 1-3 2-1-1 2-3Z", + "Ukraine": "M588 89l1 0 2-1 3 0 2 2-1 1 2 0 1 1 4 2 2-1 2 2 1 0 4 1-1 2 1 1-3 1-2 1 0 1-2 1-2 1-2 0-3 1 0 1-1 0-2-1-2 0-4 0 0-1-3 0-1 1-2 3-1-1-1 0-2 0 2-1 0-2 3 1-1-2 0-1-1-1-1-1-2 0-2-1-1 0-2 0-3 1-2 0-2 0-2 0-1 0-2-1 1-1 1-1 0-1 2-2 1-1-1-3 2 0 1-1 2 0 3 0 3 1 2 0 1 0 2 0 3 1 1 0 0-2 1 0Z", + "United Arab Emirates": "M643 166l3 0 2 0 2 0 2-2 2-1 2-2 1 3-2 0 0 2-1 2-1 1 0 1-9-1-1-2Z", + "United Kingdom": "M483 84l-2-1-2 0 1-1-1-2 2 0 3 2ZM491 85l1-2-2-1-3-1 0-2-3 1 0-3-1-1 1-3 2-3 2 1 4-1-3 3 3 0 3 0-1 2-3 3 3 0 3 4 2 0 2 3 0 1 4 1-1 2-1 0 1 2-2 1-4 0-5 1-1 0-2 1-3-1-2 2-1-1 4-3 3-1-5 0-1-1 3-1-1-1 0-2Z", + "United States": "M159 97l8 0 8 0 3 0 8 0 8 0 9 0 8 0 9 0 10 0 6 0 0-1 1 2 3 0 3 1 2 1 3-1 3 1 4-1 2 1 3 1 2 1 2 1 1 1 1 0 1 1 1 1 3 1 0 3 1 2-1 2-1 1-1 2 1 1 4-2 3 0 4-2 0-1 3-1 3 0 1-1 3-2 1-1 4 0 5 0 2-1 1 0 1-1 1-2 2-2 2 0 2 1 0 3 2 2-3 2-3 1-3 1-1 2-1 2 1 1 1 0 0-1 1 2-2 0-2 0-2 1-1 0-1 0-3 1 4-1-3 1-2 0 0 3-2 2-2-1 1 1 1 2-1 1-2 2 1-2-2-1 0-2-1 1 1 2-2-1 2 1 0 3 1 1 1 3-2 2-3 0-2 2-1 0-2 1-3 3-2 1-1 2 0 2 0 2 1 2 1 2 0 1 2 4 0 2-1 1 0 1-2 0-1-1-1-1-1-2-2-2 0-1 0-2 0-2-3-2-1 0-2 1-2-1-2-1-3 0-3 0-2 0-1 1 1 1-2 1-2 0-3-1-2 0-2 0-2 0-2 0-3 3-2 1-2 1 0 1 0 2 0 2-1 1-2-1-2-1-1-1 0-2-2-2-1-1-1-2-2-1-3 0-1 2-3-1-1-1-1-1-1-2-1-1-2-1-1-1-5 0 0 1-2 0-5 0-7-2-4-1-3 0-3 0-1-1-2-2-1 0-2-1-1-1-2 0-1-2-3-3-2-4-2-2-2-2 0-2-2-2 1-2 0-2-1-2 1-3 1-3 0-2-1-4-1-2 0-2 4 1 1 3 1-3ZM68 178l2 1-1 1-1 1-1-1-1-1 1-2ZM67 176l-1 0-1-1 1 0ZM65 175l-2 0 0-1ZM61 173l1 1-1 0-1-1ZM57 172l-1 0 1 0ZM38 66l2 0 0 1-2 0-1 0-2-1ZM74 72l2 0 1 1-2 2-3 1-1-1-1-1 3-1ZM108 40l0 10 0 16 3 0 3 1 2 1 2 2 3-2 3-1 1 2 2 1 3 1 1 2 3 3 5 2 0 2-2 1-1-1-3-1 0-2-4-2-1-3-3 0-5 0-3-1-6-3-2-1-5-1-4 1-5-2-4-1-3 1 1 2-2 0-3 0-2 1-3 1-1-2 1-2 3-1-4 1-2 1-4 2 2 2-2 2-3 1-3 1-1 1-4 1-1 2-3 1-2 0-3 1-3 0-2 1-5 1 3-2 3 0 3-2 3 0 1-1 4-2 3-2 0-2 2-1-3 1-3 0-2-1-1-1-3 1-2 0 0-1 0-1-1-1-4 0-2-1-2-1 0-1-2-1 1-2 2-2 1-1 2 0 2 0 2-1 2 0 2-1 0-1-2-1 2-1-1 0-3 1-3 0-4 0-4 0-1-1-4-2 4-1 6-2 2 0 0 2 6 0-2-2-4-1-2-1-2-2-4-1 1-1 5 0 4-1 0-2 3-1 3 0 5-2 3 1 4-2 4 1 2 1 2-1 4 1 4 1 3-1 6 1 5 1 2 0 4-1 4 1ZM23 56l2 1 1-1 3 1 2 1-2 0-2 0-1-1-3 0Z", + "Vietnam": "M790 204l2-1 3 0-1-2 5-2 0-3-1-2 1-3-1-2-2-2-2-2-2-4-3-1 2-2-1-2-3 0-2-3-1-2 1-1 3 0 2 0 3-2 1 2 2 0 0 2 1 1 3 0-4 3-2 2 0 2 2 3 2 4 3 2 1 2 2 5-1 5-2 2-3 2-2 2-4 2-1-1 1-2Z" + } } diff --git a/hugo_stats.json b/hugo_stats.json index 93df9d8..796a58d 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -183,6 +183,7 @@ "worldmap-city-head", "worldmap-city-login", "worldmap-city-people", + "worldmap-country", "worldmap-dot", "worldmap-hit", "worldmap-hit-left", diff --git a/layouts/_default/home.html b/layouts/_default/home.html index 5bc5158..64ec17d 100644 --- a/layouts/_default/home.html +++ b/layouts/_default/home.html @@ -45,6 +45,44 @@

Contributors

+ {{/* Every country anybody on the map is in, shaded whole. Two things + are being said at once and they are different claims: the shade + is the country somebody is in, the dot is the city they are in. + A country nobody named a city in — "United States" is 39.5N + 98.35W, a field in Kansas — gets only the shade, which is exactly + what was given: somewhere in here. Drawing that as a dot claimed + the precision of San Francisco 500 units away. + + One shade per country, not one per mark: China has four cities on + the map and the United States six marks, and painting the same + path four times would have stacked four alphas into a country + brighter than its own dots. + + Drawn before the marks and outside their groups, so every dot and + every label sits on top of every shade. Paths rather than a + wrapping `` for the same reason the pulse rhythms are keyed to + `worldmap-place`: `nth-of-type` counts elements of one type among + their siblings, and a group here would have shifted every mark's + bucket by one. + + Only the countries tools/worldmap.mjs has an outline for. It has + one for every country tools/locations.json can place anybody in + except Hong Kong and Singapore, which are smaller than the + dataset resolves — they are on the map as city dots, and the + `with` below is what lets them be. */}} + {{ $countries := slice -}} + {{ $withCities := slice -}} + {{ range .places -}} + {{ $countries = $countries | append .country -}} + {{ if not .countryOnly -}} + {{ $withCities = $withCities | append .country -}} + {{ end -}} + {{ end -}} + {{ range uniq $countries -}} + {{ with index $map.countries . -}} + + {{ end -}} + {{ end -}} {{ range .places -}} {{/* Position and size, from the one place that computes them: the stylesheet generated in head/resource-hints.html has to put each @@ -81,6 +119,16 @@

Contributors

{{ $x := $pos.x -}} {{ $y := $pos.y -}} {{ $r := $pos.r -}} + {{/* A country-level mark is the shade above and nothing else: its + coordinates are a national centroid, and a dot there is a city + that does not exist. City marks keep their dots on top of the + shade — that pairing is the whole point of it. + + Conditional on the outline actually being there, so a country + without one still gets a dot rather than nothing at all: today + that is Hong Kong and Singapore, whose only marks are cities + anyway. */}} + {{ $shaded := and .countryOnly (index $map.countries .country) -}} {{/* No `` here any more. It was a native tooltip naming the city and its count, and the card in the layer below now sits @@ -110,21 +158,46 @@ <h2 class="feature-grid-title">Contributors</h2> Not the hover target, which is in the html layer below the svg — scaling that would have made the hit area breathe under the cursor. Decoration only, and it is nowhere in the - accessibility tree: the svg is a single `role="img"`. */}} + accessibility tree: the svg is a single `role="img"`. + + Nothing pulses on a shaded country. The ring says a mark is + live, and it can only leave a dot: grown from the centroid of + a country it would have been a ring in open country with no + mark at its centre, which reads as a stray artefact rather + than as the country breathing. */}} + {{ if not $shaded -}} <circle class="worldmap-pulse" cx="{{ printf "%.1f" $x }}" cy="{{ printf "%.1f" $y }}" r="{{ printf "%.1f" (div (add $r 13.0) 2.0) }}" /> <circle class="worldmap-dot" cx="{{ printf "%.1f" $x }}" cy="{{ printf "%.1f" $y }}" r="{{ printf "%.1f" $r }}" /> + {{ end -}} {{/* Offsets come from tools/locations.json, hand-placed per city — see the note there on why no automatic rule works once two of them are 2.6 units apart. Hidden on narrow screens by the stylesheet, where the map is too small to - carry text and the list below says the same thing. */}} + carry text and the list below says the same thing. + + A country's own name is only written where the country is the + only thing there. Where it has cities too, the shade already + names nothing and the cities name themselves: "United States" + beside San Francisco, Palo Alto, Los Angeles, Chicago, + Cleveland and Tampa was a label for the same shape those six + dots are standing on, and the six of them are the country + being on the map. It is the crowded corners of the map that + this buys room in — the American west and the Indian south are + two of the three tightest clusters of names there are. + + The mark itself stays: those contributors gave a country and + no city, so their card is the only place their names appear, + and it opens from the country's centre either way. */}} {{ $short := .short -}} + {{ $named := not (and .countryOnly (in $withCities .country)) -}} {{ with .offset -}} + {{ if $named -}} <text class="worldmap-label" x="{{ printf "%.1f" (add $x (float .dx)) }}" y="{{ printf "%.1f" (add $y (float .dy)) }}" text-anchor="{{ .anchor }}">{{ $short }}</text> {{ end -}} + {{ end -}} </g> {{ end -}} </svg> diff --git a/tools/worldmap.mjs b/tools/worldmap.mjs index 119b8af..ac35c51 100644 --- a/tools/worldmap.mjs +++ b/tools/worldmap.mjs @@ -1,18 +1,26 @@ // Regenerates data/worldmap.json — the land silhouette the contributor map is -// drawn on. Run by `make worldmap`; the output is committed, so a normal build -// needs neither this script nor network access. +// drawn on, and the outline of each country a contributor can be placed in +// without a city. Run by `make worldmap`; the output is committed, so a normal +// build needs neither this script nor network access. // -// The source is Natural Earth's 110m land layer, as published in TopoJSON by +// The source is Natural Earth's 110m admin-0 layer, as published in TopoJSON by // world-atlas. TopoJSON is decoded here by hand rather than with topojson-client // + d3-geo, to keep tools/ dependency-free the way tools/og-shot.mjs is: the // whole job is a delta-decode and an equirectangular projection, which is less // code than the install would be. // -// Usage: node tools/worldmap.mjs [land-110m.json] +// countries-110m.json rather than land-110m.json, which is where this started and +// which draws the same coastline: this file carries a `land` object of its own +// *and* the per-country geometry, built from one shared set of arcs. Two files +// would have given the highlight a coastline simplified independently of the +// silhouette it is painted on, and disagreements of a pixel would show as a +// fringe of unshaded land along the coast of every highlighted country. +// +// Usage: node tools/worldmap.mjs [countries-110m.json] import { writeFile, readFile } from "node:fs/promises"; -const SOURCE = "https://cdn.jsdelivr.net/npm/world-atlas@2/land-110m.json"; +const SOURCE = "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json"; // Equirectangular, because the projection has to be reproduced in a Hugo // template to place the dots (see layouts/_default/home.html) and this is the @@ -185,8 +193,10 @@ function encode(points) { return `${out}Z`; } -const rings = []; -for (const geometry of topology.objects.land.geometries) { +// One geometry — a whole landmass, or one country — as the rings of it that fall +// on the map, projected and thinned. +function outline(geometry) { + const rings = []; const polygons = geometry.type === "MultiPolygon" ? geometry.arcs : [geometry.arcs]; for (const polygon of polygons) { @@ -226,11 +236,58 @@ for (const geometry of topology.objects.land.geometries) { } } } + return rings; } // One path for all of the land: it is filled with a single flat colour, so // splitting it per landmass would only add markup. -const d = rings.map(encode).join(""); +const land = topology.objects.land.geometries.flatMap(outline); +const d = land.map(encode).join(""); + +// Which countries get an outline: every country tools/locations.json can place +// anybody in, city or not, because the map shades the country a contributor is in +// as well as dotting the city (see layouts/_default/home.html). +// +// That list, and not the countries actually on the map today: +// data/contributors.json is regenerated on every contributor change, and this file +// would then have to be rebuilt over the network to catch up with it. Fifty-five +// outlines is some 20 KB of path data in a file nothing downloads; the page carries +// only the twenty of them that are used. +const places = JSON.parse( + await readFile("tools/locations.json", "utf8"), +).places; +const wanted = new Set( + Object.values(places) + .filter((place) => place && place.country) + .map((place) => place.country), +); + +// Natural Earth's own names, where they differ from the ones the site uses. Only +// one does today; the rest of the fifty-five match on the nose. +const NE_NAMES = { "United States": "United States of America" }; + +// The city-states, which 110m has no polygon for at all — one is 50 km across and +// the other 25, well under the pixel this dataset resolves. Listed rather than +// warned about: their contributors are on the map as city dots, a shade of a +// country that small would be invisible under its own dot, and a warning on every +// run for something that cannot be fixed is a warning nobody reads. +const NO_OUTLINE = new Set(["Hong Kong", "Singapore"]); + +const countries = {}; +for (const name of [...wanted].sort()) { + if (NO_OUTLINE.has(name)) continue; + const geometry = topology.objects.countries.geometries.find( + (candidate) => candidate.properties.name === (NE_NAMES[name] ?? name), + ); + if (!geometry) { + // Not fatal: the country goes unshaded and its cities are still dotted, so the + // map stays complete. A missing name is a typo in tools/locations.json or a + // rename upstream, and both want a person rather than a failed build. + console.error(`warning: no country named ${name} in the source`); + continue; + } + countries[name] = outline(geometry).map(encode).join(""); +} const output = { // Provenance, since the geometry is generated rather than authored. @@ -242,11 +299,14 @@ const output = { k: K, latTop: LAT_TOP, land: d, + countries, }; await writeFile("data/worldmap.json", JSON.stringify(output) + "\n"); +const shapes = Object.values(countries).join(""); console.log( - `data/worldmap.json: ${rings.length} rings, ${(d.length / 1024).toFixed(1)} KB of path data`, + `data/worldmap.json: ${land.length} rings, ${(d.length / 1024).toFixed(1)} KB of path data` + + `, plus ${Object.keys(countries).length} countries in ${(shapes.length / 1024).toFixed(1)} KB`, ); async function fetchOrDie(url) { From cc6a21c78c86e86971ad833193ce25051773cc94 Mon Sep 17 00:00:00 2001 From: kerthcet <kerthcet@gmail.com> Date: Fri, 11 Sep 2026 16:08:14 +0100 Subject: [PATCH 6/6] highlight the map once it has contributor Signed-off-by: kerthcet <kerthcet@gmail.com> --- assets/scss/common/_custom.scss | 38 ++++++++++---- layouts/_default/home.html | 62 +++++++---------------- layouts/partials/head/resource-hints.html | 26 ++++++++-- layouts/partials/worldmap-marks.html | 47 +++++++++++++++++ 4 files changed, 116 insertions(+), 57 deletions(-) create mode 100644 layouts/partials/worldmap-marks.html diff --git a/assets/scss/common/_custom.scss b/assets/scss/common/_custom.scss index 4f43463..3be82c1 100644 --- a/assets/scss/common/_custom.scss +++ b/assets/scss/common/_custom.scss @@ -341,7 +341,7 @@ $feature-gap: 1.25rem; // smudges in the land. // // The cost is contrast, and it is worth stating plainly: 2.2:1 against the white -// keyline that rings each dot, 1.9:1 against the land — 1.5:1 where the dot stands +// keyline that rings each dot, 1.9:1 against the land — 1.1:1 where the dot stands // on a shaded country, which is where most of them stand. WCAG 2.1 asks 3:1 of a // graphic needed to understand the content, and this no longer meets it. What makes // that a considered trade rather than an oversight is that nothing here is only on @@ -350,7 +350,8 @@ $feature-gap: 1.25rem; // back to 72% (#E47351) restores 3.1:1 on the keyline. // // The keyline is what keeps the dots countable over the shade, and it gains what the -// fill loses: white on a shaded country is 1.5:1 against 1.17:1 on bare land. +// fill loses: white on a shaded country is 1.97:1 against 1.18:1 on bare land. That +// trade is why the shade can be as dark as it is — see $map-region. $map-land: #e9edf2; // The countries somebody is in, filled whole. The land's own colour a step darker // rather than a colour of its own, which is what this started as — the mark's hue at @@ -361,18 +362,33 @@ $map-land: #e9edf2; // are. A darker neutral says which countries without spending the accent on them, so // the dots stay the only colour the map has. // -// #c8d4e3 is the land's own hue at 84% lightness against its 93%, a little more -// saturated. 86% was the first attempt and it is the number to know about: at that -// lightness the United States, China and Brazil read fine and Romania, Italy, -// Ireland and Cyprus — the countries that are a few pixels of coastline at this -// scale — did not read at all, which loses the shade exactly where it is the only -// thing saying anybody is there. Darker than this and the shades stop being marks on -// one landmass and become a second silhouette over it. +// #b0bac6 is a slate: the land's own hue (213) at 16% saturation against its 26%, and +// 73% lightness against its 93%. Every number here was set by rendering the +// alternatives and looking at them, and the two dead ends are worth keeping: +// +// #c8d4e3 — the land's hue and saturation at 84% — was too blue and too close. Blue at +// full saturation is the one hue a map already spends on something else, so the shaded +// countries read as a second sea rather than as land with somebody in it, and at 84% +// they were a 1.28:1 step off the land besides. +// +// #bcc0c4 — a near-neutral grey at 6% saturation — went too far the other way. A grey +// that has no hue at all sits over a land that has one, and the shapes read as a +// desaturated patch of the map rather than as a tone of it: muddy, and nothing to do +// with contrast, which was fine. +// +// So: keep the land's hue, drop most of its saturation, and take the lightness down +// until the shade is unmistakable — a 1.67:1 step against the land. The floor is the +// small countries, because Romania, Italy, Ireland and Cyprus are a few pixels of +// coastline at this width and at 86% they did not read at all, which loses the shade +// exactly where it is the only thing saying anybody is there. The ceiling is where the +// shades stop being marks on one landmass and become a silhouette of their own over +// it — around 70%, where the United States and Brazil start to outweigh the dots +// standing on them. // // Flat and opaque rather than a tint: the shades are one per country and never // overlap, so there is nothing for an alpha to buy — and an alpha over the land is // this colour anyway, computed at render time instead of being chosen. -$map-region: #c8d4e3; +$map-region: #b0bac6; $map-mark: mix($primary, #fff, 52%); // City labels and the legend's figures. Lighter than the body ink, which made both // heavier than the map they annotate. Safe at this lightness only because each is @@ -568,7 +584,7 @@ $map-pulse-alpha: 0.18; // // The stroke straddles the outline, so half of it lands outside: on the sea, where // it is the same white and invisible, and on an unshaded neighbour, where it is a - // barely-there 1.17:1 lightening of the land. Only where two shades meet do both + // barely-there 1.18:1 lightening of the land. Only where two shades meet do both // halves paint, and that is the one place a seam is wanted. // // 0.8 units against the dots' 2.5. This is a border and not a keyline: at 2.5 the diff --git a/layouts/_default/home.html b/layouts/_default/home.html index 64ec17d..58a6261 100644 --- a/layouts/_default/home.html +++ b/layouts/_default/home.html @@ -71,19 +71,22 @@ <h2 class="feature-grid-title">Contributors</h2> dataset resolves — they are on the map as city dots, and the `with` below is what lets them be. */}} {{ $countries := slice -}} - {{ $withCities := slice -}} {{ range .places -}} {{ $countries = $countries | append .country -}} - {{ if not .countryOnly -}} - {{ $withCities = $withCities | append .country -}} - {{ end -}} {{ end -}} {{ range uniq $countries -}} {{ with index $map.countries . -}} <path class="worldmap-country" d="{{ . }}" /> {{ end -}} {{ end -}} - {{ range .places -}} + {{/* Every place except the country-level ones the shade above has + already accounted for — see partials/worldmap-marks.html for which + those are and what it costs. Ranged from the same partial as the + hover targets below and as the stylesheet that positions them, + because those rules are `nth-child` and the three lists have to + agree element for element. */}} + {{ $marks := partial "worldmap-marks.html" (dict "data" . "map" $map) -}} + {{ range $marks -}} {{/* Position and size, from the one place that computes them: the stylesheet generated in head/resource-hints.html has to put each card's hover target exactly on top of the dot drawn here, and @@ -119,16 +122,6 @@ <h2 class="feature-grid-title">Contributors</h2> {{ $x := $pos.x -}} {{ $y := $pos.y -}} {{ $r := $pos.r -}} - {{/* A country-level mark is the shade above and nothing else: its - coordinates are a national centroid, and a dot there is a city - that does not exist. City marks keep their dots on top of the - shade — that pairing is the whole point of it. - - Conditional on the outline actually being there, so a country - without one still gets a dot rather than nothing at all: today - that is Hong Kong and Singapore, whose only marks are cities - anyway. */}} - {{ $shaded := and .countryOnly (index $map.countries .country) -}} <g class="worldmap-place"> {{/* No `<title>` here any more. It was a native tooltip naming the city and its count, and the card in the layer below now sits @@ -158,46 +151,29 @@ <h2 class="feature-grid-title">Contributors</h2> Not the hover target, which is in the html layer below the svg — scaling that would have made the hit area breathe under the cursor. Decoration only, and it is nowhere in the - accessibility tree: the svg is a single `role="img"`. - - Nothing pulses on a shaded country. The ring says a mark is - live, and it can only leave a dot: grown from the centroid of - a country it would have been a ring in open country with no - mark at its centre, which reads as a stray artefact rather - than as the country breathing. */}} - {{ if not $shaded -}} + accessibility tree: the svg is a single `role="img"`. */}} <circle class="worldmap-pulse" cx="{{ printf "%.1f" $x }}" cy="{{ printf "%.1f" $y }}" r="{{ printf "%.1f" (div (add $r 13.0) 2.0) }}" /> <circle class="worldmap-dot" cx="{{ printf "%.1f" $x }}" cy="{{ printf "%.1f" $y }}" r="{{ printf "%.1f" $r }}" /> - {{ end -}} {{/* Offsets come from tools/locations.json, hand-placed per city — see the note there on why no automatic rule works once two of them are 2.6 units apart. Hidden on narrow screens by the stylesheet, where the map is too small to carry text and the list below says the same thing. - A country's own name is only written where the country is the - only thing there. Where it has cities too, the shade already - names nothing and the cities name themselves: "United States" - beside San Francisco, Palo Alto, Los Angeles, Chicago, - Cleveland and Tampa was a label for the same shape those six - dots are standing on, and the six of them are the country - being on the map. It is the crowded corners of the map that - this buys room in — the American west and the Indian south are - two of the three tightest clusters of names there are. - - The mark itself stays: those contributors gave a country and - no city, so their card is the only place their names appear, - and it opens from the country's centre either way. */}} + Every mark drawn here is named, country-level ones included: + the countries that would have wanted their name left off, + because their cities were already naming the same shape, are + not marks any more (partials/worldmap-marks.html). What is + left of a country mark is South Africa, Romania, Italy and New + Zealand — the countries whose name is the only name on the map + for them. */}} {{ $short := .short -}} - {{ $named := not (and .countryOnly (in $withCities .country)) -}} {{ with .offset -}} - {{ if $named -}} <text class="worldmap-label" x="{{ printf "%.1f" (add $x (float .dx)) }}" y="{{ printf "%.1f" (add $y (float .dy)) }}" text-anchor="{{ .anchor }}">{{ $short }}</text> {{ end -}} - {{ end -}} </g> {{ end -}} </svg> @@ -213,8 +189,8 @@ <h2 class="feature-grid-title">Contributors</h2> head/resource-hints.html, not in a `style` attribute here, which the site's own `style-src 'self'` refuses. That is also why the order of these divs matters: the generated rules address them by - `nth-child`, so this must stay a flat list in the order of - `.places`. + `nth-child`, so this must stay a flat list of exactly the marks + partials/worldmap-marks.html returns, in that order. This layer takes the pointer for the whole map, so it is also what replaced the dots' `<title>` tooltips — the card names the city, @@ -230,7 +206,7 @@ <h2 class="feature-grid-title">Contributors</h2> hidden` until something in it has focus, and a hidden link is not focusable, so without a stop on the mark itself there is no way in. */}} <div class="worldmap-hits"> - {{ range .places -}} + {{ range $marks -}} {{ $pos := partial "worldmap-place.html" (dict "map" $map "place" .) -}} {{/* Which side the card opens on, decided here because no script measures it at runtime: everything east of the middle opens to diff --git a/layouts/partials/head/resource-hints.html b/layouts/partials/head/resource-hints.html index 5e97571..3c4f62e 100644 --- a/layouts/partials/head/resource-hints.html +++ b/layouts/partials/head/resource-hints.html @@ -24,10 +24,14 @@ assets/scss/common/_custom.scss. Positions and sizes come out of partials/worldmap-place.html, the same partial - the svg's dots are drawn from, because these two have to agree to the pixel. */}} + the svg's dots are drawn from, because these two have to agree to the pixel. The + list itself comes out of partials/worldmap-marks.html for the same reason, and it + is the stricter of the two requirements: these rules are `nth-child`, so this has + to be the same marks in the same order as the divs on the page, not merely the + same arithmetic. */}} {{ $map := site.Data.worldmap -}} {{ $rules := slice -}} -{{ range $i, $place := site.Data.contributors.places -}} +{{ range $i, $place := partial "worldmap-marks.html" (dict "data" site.Data.contributors "map" $map) -}} {{ $pos := partial "worldmap-place.html" (dict "map" $map "place" $place) -}} {{/* Percentages of the layer, which is the svg's own box — so the targets track the map through every width it renders at, exactly as the dots do. @@ -53,6 +57,22 @@ {{ $room := cond (gt $pos.x (mul 0.5 $map.width)) (sub $left (div $size 2.0)) (sub (sub 100.0 $left) (div $size 2.0)) -}} {{ $rules = $rules | append (printf ".worldmap-hit:nth-child(%d){left:%.3f%%;top:%.3f%%;width:%.3f%%;--map-room:%.3f}" (add $i 1) $left $top $size $room) -}} {{ end -}} -{{ $css := resources.FromString "css/worldmap-hits.css" (delimit $rules "\n") | resources.Minify | fingerprint -}} +{{/* The target path carries a hash of the rules, which is not decoration: it is what + keeps this stylesheet from going stale against the page it positions. + + `resources.FromString` is keyed on the path it is given, and Hugo caches generated + resources under `resources/_gen` between builds. With a fixed name, a build whose + rules had changed — a contributor added, a city resolved, a mark reordered — could + be handed the bytes of an earlier one, and the failure is silent and total: the + rules are `nth-child`, so a stylesheet written for a different number of marks, or + the same marks in a different order, positions every card on somebody else's dot. + London's dot opened Bengaluru's card, which is exactly how this was found. A path + that changes with the content cannot be answered from the cache at all. + + The `fingerprint` below still earns its place — it is what produces the integrity + hash the `style-src 'self'` policy is paired with, and the immutable URL — but it + runs after the fact and cannot fix a resource that was already the wrong one. */}} +{{ $body := delimit $rules "\n" -}} +{{ $css := resources.FromString (printf "css/worldmap-hits.%s.css" (hash.XxHash $body)) $body | resources.Minify | fingerprint -}} <link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}"> {{ end -}} diff --git a/layouts/partials/worldmap-marks.html b/layouts/partials/worldmap-marks.html new file mode 100644 index 0000000..f06e7d9 --- /dev/null +++ b/layouts/partials/worldmap-marks.html @@ -0,0 +1,47 @@ +{{/* The marks the contributor map draws, which is not every place in + data/contributors.json. + + A country-level place — contributors who said "India" and no city — is dropped + where that country has cities on the map as well and an outline to shade. The + shade over India is already the claim those contributors support: somebody is + somewhere in this country. Drawing a second mark for it put a card on a field in + central India that opened next to Bengaluru's, saying "India" where the map had + just said Bengaluru, and there was nothing to point at it with in the first + place — a shade is not a hover target, so the mark was a card that only appeared + when the pointer crossed empty land. + + Stated plainly, because it is a real loss: those contributors are then nowhere on + the map by name. Four people today — two who gave "USA" and two who gave "India" — + are counted in the summary above it and in the region shares beside it, and + represented on the map by their country's shade and nothing else. What buys that + is that a centroid is not where they are: the alternative was a mark claiming a + place none of them named. + + Where the country has no cities — South Africa, Romania, Italy, New Zealand — the + place is kept and drawn like any other mark, dot and name both. And where it has + no outline to be shaded by, it is kept whatever else is true, so a country the + 110m dataset cannot draw still puts its people on the map: today that is Hong Kong + and Singapore, which only ever come through as cities anyway. + + One partial rather than the same test in three templates, and that is the point of + it. The svg's marks, the hover targets over them and the generated stylesheet that + positions those targets are three lists that have to be the same list in the same + order — the rules are `nth-child`, so a template that filtered differently from the + others would silently put every card on somebody else's dot. + + Called with `data` (site.Data.contributors) and `map` (site.Data.worldmap). */}} +{{ $data := .data -}} +{{ $map := .map -}} +{{ $withCities := slice -}} +{{ range $data.places -}} +{{ if not .countryOnly -}} +{{ $withCities = $withCities | append .country -}} +{{ end -}} +{{ end -}} +{{ $marks := slice -}} +{{ range $data.places -}} +{{ if not (and .countryOnly (and (in $withCities .country) (index $map.countries .country))) -}} +{{ $marks = $marks | append . -}} +{{ end -}} +{{ end -}} +{{ return $marks -}}