rspace-online/modules/rcal/geo-hierarchy.ts

150 lines
8.7 KiB
TypeScript

/**
* Static geographic hierarchy lookup — no API calls, works offline.
*
* Provides continent/country bounding-box lookups and breadcrumb generation
* for semantic zoom on the map and calendar views.
*/
// ── Continent definitions ──
interface BoundingBox {
name: string;
latMin: number;
latMax: number;
lngMin: number;
lngMax: number;
}
interface CountryDef extends BoundingBox {
continent: string;
area: number; // approx sq degrees for specificity sorting
}
export const CONTINENTS: BoundingBox[] = [
{ name: "Europe", latMin: 35, latMax: 72, lngMin: -25, lngMax: 45 },
{ name: "Asia", latMin: -10, latMax: 75, lngMin: 45, lngMax: 180 },
{ name: "Africa", latMin: -35, latMax: 37, lngMin: -20, lngMax: 55 },
{ name: "North America", latMin: 15, latMax: 84, lngMin: -170, lngMax: -50 },
{ name: "South America", latMin: -56, latMax: 15, lngMin: -82, lngMax: -34 },
{ name: "Oceania", latMin: -48, latMax: 0, lngMin: 110, lngMax: 180 },
{ name: "Antarctica", latMin: -90, latMax: -60, lngMin: -180, lngMax: 180 },
];
export const CONTINENT_CENTROIDS: Record<string, [number, number]> = {
"Europe": [50.0, 10.0],
"Asia": [35.0, 90.0],
"Africa": [5.0, 25.0],
"North America": [45.0, -100.0],
"South America": [-15.0, -60.0],
"Oceania": [-25.0, 140.0],
"Antarctica": [-82.0, 0.0],
};
// ~50 major countries sorted by area (ascending) for specificity-first matching
const COUNTRIES: CountryDef[] = [
// Europe
{ name: "Luxembourg", continent: "Europe", latMin: 49.4, latMax: 50.2, lngMin: 5.7, lngMax: 6.5, area: 1 },
{ name: "Slovenia", continent: "Europe", latMin: 45.4, latMax: 46.9, lngMin: 13.4, lngMax: 16.6, area: 5 },
{ name: "Belgium", continent: "Europe", latMin: 49.5, latMax: 51.5, lngMin: 2.5, lngMax: 6.4, area: 8 },
{ name: "Netherlands", continent: "Europe", latMin: 50.7, latMax: 53.6, lngMin: 3.4, lngMax: 7.2, area: 11 },
{ name: "Switzerland", continent: "Europe", latMin: 45.8, latMax: 47.8, lngMin: 5.9, lngMax: 10.5, area: 9 },
{ name: "Denmark", continent: "Europe", latMin: 54.5, latMax: 57.8, lngMin: 8.0, lngMax: 15.2, area: 24 },
{ name: "Austria", continent: "Europe", latMin: 46.4, latMax: 49.0, lngMin: 9.5, lngMax: 17.2, area: 20 },
{ name: "Czech Republic",continent: "Europe", latMin: 48.5, latMax: 51.1, lngMin: 12.1, lngMax: 18.9, area: 18 },
{ name: "Ireland", continent: "Europe", latMin: 51.4, latMax: 55.4, lngMin: -10.5, lngMax: -5.5, area: 20 },
{ name: "Portugal", continent: "Europe", latMin: 36.9, latMax: 42.2, lngMin: -9.5, lngMax: -6.2, area: 18 },
{ name: "Hungary", continent: "Europe", latMin: 45.7, latMax: 48.6, lngMin: 16.1, lngMax: 22.9, area: 20 },
{ name: "Croatia", continent: "Europe", latMin: 42.4, latMax: 46.6, lngMin: 13.5, lngMax: 19.4, area: 25 },
{ name: "Greece", continent: "Europe", latMin: 34.8, latMax: 41.8, lngMin: 19.3, lngMax: 29.6, area: 72 },
{ name: "Italy", continent: "Europe", latMin: 36.6, latMax: 47.1, lngMin: 6.6, lngMax: 18.5, area: 125 },
{ name: "United Kingdom",continent: "Europe", latMin: 49.9, latMax: 58.7, lngMin: -8.2, lngMax: 1.8, area: 88 },
{ name: "Romania", continent: "Europe", latMin: 43.6, latMax: 48.3, lngMin: 20.3, lngMax: 29.7, area: 44 },
{ name: "Poland", continent: "Europe", latMin: 49.0, latMax: 54.8, lngMin: 14.1, lngMax: 24.2, area: 59 },
{ name: "Germany", continent: "Europe", latMin: 47.3, latMax: 55.1, lngMin: 5.9, lngMax: 15.0, area: 71 },
{ name: "Finland", continent: "Europe", latMin: 59.8, latMax: 70.1, lngMin: 20.6, lngMax: 31.6, area: 113 },
{ name: "Norway", continent: "Europe", latMin: 57.9, latMax: 71.2, lngMin: 4.6, lngMax: 31.1, area: 353 },
{ name: "Sweden", continent: "Europe", latMin: 55.3, latMax: 69.1, lngMin: 11.1, lngMax: 24.2, area: 181 },
{ name: "France", continent: "Europe", latMin: 41.3, latMax: 51.1, lngMin: -5.1, lngMax: 9.6, area: 144 },
{ name: "Spain", continent: "Europe", latMin: 36.0, latMax: 43.8, lngMin: -9.3, lngMax: 4.3, area: 106 },
{ name: "Ukraine", continent: "Europe", latMin: 44.4, latMax: 52.4, lngMin: 22.1, lngMax: 40.2, area: 145 },
{ name: "Turkey", continent: "Europe", latMin: 36.0, latMax: 42.1, lngMin: 26.0, lngMax: 44.8, area: 115 },
// Asia
{ name: "Israel", continent: "Asia", latMin: 29.5, latMax: 33.3, lngMin: 34.2, lngMax: 35.9, area: 6 },
{ name: "South Korea", continent: "Asia", latMin: 33.1, latMax: 38.6, lngMin: 124.6, lngMax: 131.9, area: 40 },
{ name: "Japan", continent: "Asia", latMin: 24.0, latMax: 45.5, lngMin: 123.0, lngMax: 146.0, area: 495 },
{ name: "Thailand", continent: "Asia", latMin: 5.6, latMax: 20.5, lngMin: 97.3, lngMax: 105.6, area: 124 },
{ name: "Vietnam", continent: "Asia", latMin: 8.4, latMax: 23.4, lngMin: 102.1, lngMax: 109.5, area: 111 },
{ name: "Indonesia", continent: "Asia", latMin: -11.0, latMax: 6.0, lngMin: 95.0, lngMax: 141.0, area: 782 },
{ name: "India", continent: "Asia", latMin: 6.7, latMax: 35.5, lngMin: 68.2, lngMax: 97.4, area: 841 },
{ name: "China", continent: "Asia", latMin: 18.2, latMax: 53.6, lngMin: 73.5, lngMax: 134.8, area: 2172 },
// Africa
{ name: "Tunisia", continent: "Africa", latMin: 30.2, latMax: 37.3, lngMin: 7.5, lngMax: 11.6, area: 29 },
{ name: "Morocco", continent: "Africa", latMin: 27.7, latMax: 35.9, lngMin: -13.2, lngMax: -1.0, area: 100 },
{ name: "Kenya", continent: "Africa", latMin: -4.7, latMax: 5.0, lngMin: 33.9, lngMax: 41.9, area: 78 },
{ name: "South Africa", continent: "Africa", latMin: -34.8, latMax: -22.1, lngMin: 16.5, lngMax: 32.9, area: 209 },
{ name: "Nigeria", continent: "Africa", latMin: 4.3, latMax: 13.9, lngMin: 2.7, lngMax: 14.7, area: 115 },
{ name: "Egypt", continent: "Africa", latMin: 22.0, latMax: 31.7, lngMin: 24.7, lngMax: 36.9, area: 118 },
{ name: "Ethiopia", continent: "Africa", latMin: 3.4, latMax: 14.9, lngMin: 33.0, lngMax: 48.0, area: 173 },
// North America
{ name: "Costa Rica", continent: "North America", latMin: 8.0, latMax: 11.2, lngMin: -85.9, lngMax: -82.6, area: 11 },
{ name: "Mexico", continent: "North America", latMin: 14.5, latMax: 32.7, lngMin: -118.4, lngMax: -86.7, area: 577 },
{ name: "United States", continent: "North America", latMin: 24.5, latMax: 49.4, lngMin: -125.0, lngMax: -66.9, area: 1449 },
{ name: "Canada", continent: "North America", latMin: 41.7, latMax: 83.1, lngMin: -141.0, lngMax: -52.6, area: 3652 },
// South America
{ name: "Colombia", continent: "South America", latMin: -4.2, latMax: 12.5, lngMin: -79.0, lngMax: -66.9, area: 202 },
{ name: "Argentina", continent: "South America", latMin: -55.1, latMax: -21.8, lngMin: -73.6, lngMax: -53.6, area: 666 },
{ name: "Brazil", continent: "South America", latMin: -33.8, latMax: 5.3, lngMin: -73.9, lngMax: -34.8, area: 1528 },
// Oceania
{ name: "New Zealand", continent: "Oceania", latMin: -47.3, latMax: -34.4, lngMin: 166.4, lngMax: 178.6, area: 157 },
{ name: "Australia", continent: "Oceania", latMin: -43.6, latMax: -10.7, lngMin: 113.2, lngMax: 153.6, area: 1331 },
].sort((a, b) => a.area - b.area); // smallest first for specificity
// ── Lookup functions ──
function inBox(lat: number, lng: number, box: BoundingBox): boolean {
return lat >= box.latMin && lat <= box.latMax && lng >= box.lngMin && lng <= box.lngMax;
}
export function lookupContinent(lat: number, lng: number): string | null {
for (const c of CONTINENTS) {
if (inBox(lat, lng, c)) return c.name;
}
return null;
}
export function lookupCountry(lat: number, lng: number): string | null {
// Countries sorted by area ascending → smallest match first (most specific)
for (const c of COUNTRIES) {
if (inBox(lat, lng, c)) return c.name;
}
return null;
}
/**
* Generate a geographic breadcrumb string from coordinates.
* Returns e.g. "Earth > Europe > Germany > Berlin"
*
* @param lat Latitude
* @param lng Longitude
* @param locationName Optional city/venue name to append as most specific level
*/
export function generateBreadcrumb(lat: number, lng: number, locationName?: string | null): string {
const parts = ["Earth"];
const continent = lookupContinent(lat, lng);
if (continent) parts.push(continent);
const country = lookupCountry(lat, lng);
if (country) parts.push(country);
if (locationName) {
// Strip common venue prefixes to get just the city/place
// e.g. "Factory Berlin" → "Berlin", "KICC, Nairobi" → "Nairobi"
const city = locationName.includes(",")
? locationName.split(",").pop()!.trim()
: locationName;
// Avoid duplicating country in breadcrumb
if (city && city !== country) {
parts.push(city);
}
}
return parts.join(" > ");
}