components/InteractiveGlobe/countryPolygons.jsjavascript
import { feature } from "topojson-client";
// 110m (not 50m) resolution - coastline detail beyond this isn't visible on
// a full-globe kiosk view, and it's 7x smaller to fetch/parse (108KB vs
// 756KB).
import worldTopology from "world-atlas/countries-110m.json";
import { countryMetaByCcn3, ALASKA_REGION_ID } from "./countryMeta";
// Ported from globe-proto/src/lib/countryPolygons.ts.
let cached = null;
/**
* Converts the world-atlas topology into GeoJSON country features (once,
* then cached in-module), decorated with the metadata (name/cca3/continent)
* countryMeta.js knows for each ccn3 - consumed by InteractiveGlobe's polygon
* layer. Also splits out French Guiana (from France's MultiPolygon) and
* Alaska (from the USA's) into their own features, see the inline comments
* below, so they can be colored independently via countryColors.js.
*
* @returns {Array<Object>} GeoJSON Feature objects, each with `properties:
* {ccn3, name, cca3, continent}`.
*/
export function loadCountryPolygons() {
if (cached) return cached;
const collection = feature(worldTopology, worldTopology.objects.countries);
cached = collection.features.flatMap((f) => {
// A handful of territories (Kosovo, Somaliland, N. Cyprus, ...) ship
// without an ISO numeric id in this topology; fall back to a name-based
// key so they don't all collapse onto the same "undefined" identity.
const ccn3 = f.id !== undefined ? String(f.id) : `unclassified-${f.properties.name}`;
// world-atlas bakes French Guiana into France's MultiPolygon as an extra
// ring (coordinates[0], with mainland France and Corsica following)
// instead of giving it its own topology feature - Natural Earth treats
// it as French territory, not a separate country. Left alone, its South
// American landmass would render and color as Europe/"France". Split it
// into its own feature so continent-based coloring is accurate.
if (ccn3 === "250" && f.geometry.type === "MultiPolygon") {
const [guianaRing, ...metropolitanRings] = f.geometry.coordinates;
const guianaMeta = countryMetaByCcn3("254");
const franceMeta = countryMetaByCcn3(ccn3);
return [
{
...f,
id: "254",
geometry: { type: "Polygon", coordinates: guianaRing },
properties: {
ccn3: "254",
name: guianaMeta?.commonName ?? "French Guiana",
cca3: guianaMeta?.cca3 ?? null,
continent: guianaMeta?.continent ?? null,
},
},
{
...f,
geometry: { type: "MultiPolygon", coordinates: metropolitanRings },
properties: {
ccn3,
name: franceMeta?.commonName ?? f.properties.name,
cca3: franceMeta?.cca3 ?? null,
continent: franceMeta?.continent ?? null,
},
},
];
}
// The USA MultiPolygon bundles its states into one set of coordinates
// with no per-state breakdown - rings 6/7/8/9 are (respectively) St.
// Lawrence Island, Kodiak Island, the Alaska mainland+Aleutians and a
// Seward Peninsula islet, identified by inspecting their bounding boxes
// (ring 0 is CONUS, rings 1-5 are Hawaii). Splitting these out under
// ALASKA_REGION_ID lets countryDisplay color Alaska independently of the
// rest of the country instead of it always following the US's color.
if (ccn3 === "840" && f.geometry.type === "MultiPolygon") {
const alaskaRingIndices = new Set([6, 7, 8, 9]);
const alaskaRings = f.geometry.coordinates.filter((_, i) => alaskaRingIndices.has(i));
const usRings = f.geometry.coordinates.filter((_, i) => !alaskaRingIndices.has(i));
const usMeta = countryMetaByCcn3(ccn3);
return [
{
...f,
id: ALASKA_REGION_ID,
geometry: { type: "MultiPolygon", coordinates: alaskaRings },
properties: {
ccn3: ALASKA_REGION_ID,
name: "Alaska",
cca3: null,
continent: usMeta?.continent ?? null,
},
},
{
...f,
geometry: { type: "MultiPolygon", coordinates: usRings },
properties: {
ccn3,
name: usMeta?.commonName ?? f.properties.name,
cca3: usMeta?.cca3 ?? null,
continent: usMeta?.continent ?? null,
},
},
];
}
const meta = countryMetaByCcn3(ccn3);
return [
{
...f,
properties: {
ccn3,
name: meta?.commonName ?? f.properties.name,
cca3: meta?.cca3 ?? null,
continent: meta?.continent ?? null,
},
},
];
});
return cached;
}