SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
2.7 KB · 71 lines tsx
Raw Blame History
1import { t } from '@/i18n';2import { formatValue } from '@/lib/format';3import { classIndex, isoLookupFromCountries, worldPaths } from '@/lib/map-geo';4import type { CountrySummary, MapResponse } from '@/lib/types';5import { ChoroplethView, type ChoroplethFeature } from './choropleth-view';67/**8 * Server component: joins the world geometry with `/indicators/{slug}/map` values and the country list9 * (for slugs and the ISO-numeric lookup), computes 5–7 quantile classes from the API legend, and hands10 * plain data to the interactive client view. Sequential single-hue ramp (light → dark = low → high).11 */12export function Choropleth({13  map,14  countries,15  height,16  compact = false,17  className,18  title,19}: {20  map: MapResponse;21  countries: CountrySummary[];22  height?: number;23  compact?: boolean;24  className?: string;25  title?: string;26}) {27  const { paths, sphere } = worldPaths();28  const byId = new Map(countries.map((c) => [c.id, c]));29  // Registry numeric → ISO3 lookup (when the API exposes iso_numeric on the summary); static table otherwise.30  const numeric = isoLookupFromCountries(countries as Array<{ id: string; iso_numeric?: string | null }>);31  const breaks = (map.legend?.breaks ?? []).slice(0, 6);32  const k = breaks.length + 1; // classes33  const features: ChoroplethFeature[] = paths.map((p) => {34    // Prefer registry numeric lookup when the static table has no code.35    const iso3 = p.iso3 ?? (p.numeric ? numeric.get(p.numeric) ?? null : null);36    const c = iso3 ? byId.get(iso3) : undefined;37    const v = iso3 ? map.values[iso3] : undefined;38    return {39      iso3,40      name: c?.name ?? p.name,41      slug: c?.slug ?? null,42      flag: c?.flag ?? null,43      d: p.d,44      value: typeof v === 'number' ? v : null,45      cls: typeof v === 'number' ? classIndex(v, breaks) : null,46    };47  });48  const spec = map.indicator;49  const legendItems = Array.from({ length: k }, (_, i) => {50    const lo = i === 0 ? map.legend.min : breaks[i - 1]!;51    const hi = i === k - 1 ? map.legend.max : breaks[i]!;52    return { cls: i, label: `${formatValue(lo, spec)} – ${formatValue(hi, spec)}` };53  });54  const year = map.year_used ?? map.year ?? '';55  const summary = t('chart.summary.map', { name: spec.name, year, n: map.n, min: formatValue(map.legend.min, spec), max: formatValue(map.legend.max, spec) });56  return (57    <ChoroplethView58      features={features}59      sphere={sphere}60      legend={legendItems}61      k={k}62      spec={{ format: spec.format, unit: spec.unit, unit_short: spec.unit_short, precision: spec.precision, name: spec.name }}63      summary={summary}64      title={title ?? t('chart.map.legend', { name: spec.name, year })}65      height={height}66      compact={compact}67      className={className}68    />69  );70}71