563 lines · 5 files · 34.4 kB
cases/108-country-choropleth/example.tsx98 lines · entry
cases/108-country-choropleth/example.tsx
import { Chart } from '@tanstack/charts/react/tooltip'
import { tooltip as exampleTooltip } from '@tanstack/charts/tooltip'
import { defineChart } from '@tanstack/charts'
import { geoShape } from '@tanstack/charts/geo'
import { geoEqualEarth } from 'd3-geo'
import { scaleQuantize } from 'd3-scale'
import {
previewWorldLand,
worldLand,
worldSphere,
} from '@tanstack/charts-data/country-atlas'
import {
learningPovertyCountries,
previewLearningPovertyCountries,
} from '@tanstack/charts-data/learning-poverty-geography'
const colorRanges = [
['#ecfeff', '#a5f3fc', '#67e8f9', '#06b6d4', '#0e7490', '#164e63'],
['#f0fdf4', '#bbf7d0', '#86efac', '#22c55e', '#15803d', '#14532d'],
]
const projection = {
type: geoEqualEarth,
fit: 'sphere' as const,
}
const previewProjection = {
type: () => geoEqualEarth().precision(2),
fit: 'sphere' as const,
}
export const createExampleChart = (input: ChartOptions) =>
defineChart(
{
marks: [
geoShape([input.preview ? previewWorldLand : worldLand], {
projection: input.preview ? previewProjection : projection,
fill: '#e2e8f0',
stroke: '#ffffff',
strokeWidth: 0.55,
}),
geoShape(
input.preview
? previewLearningPovertyCountries
: learningPovertyCountries,
{
projection: input.preview ? previewProjection : projection,
color: (country) => country.properties.density,
stroke: 'currentColor',
strokeOpacity: 0.34,
strokeWidth: 0.55,
},
),
geoShape([worldSphere], {
projection: input.preview ? previewProjection : projection,
fill: 'none',
stroke: 'currentColor',
strokeOpacity: 0.35,
strokeWidth: 0.75,
}),
],
scales: {
x: null,
y: null,
},
color: {
scale: scaleQuantize<string>,
range: colorRanges[input.revision % 2] ?? colorRanges[0],
},
margin: 12,
},
{
keyboard: true,
tooltip: {
use: exampleTooltip,
...{
format: ({ datum }) =>
'properties' in datum && 'density' in datum.properties
? `${datum.properties['Country Name']} · ${datum.properties.density} people/km²`
: 'World land',
},
},
},
)
export interface ChartOptions {
revision: number
preview?: boolean
}
export const exampleAriaLabel = 'World population-density choropleth'
export const chart = createExampleChart({
revision: 0,
preview: false,
})
export default function Example() {
return <Chart ariaLabel={exampleAriaLabel} definition={chart} height={480} />
}packages/charts-demo-data/src/country-atlas.ts135 lines · dependency
packages/charts-demo-data/src/country-atlas.ts
import countriesAtlasJson from 'world-atlas/countries-110m.json'
import landAtlasJson from 'world-atlas/land-110m.json'
import detailedLandAtlasJson from 'world-atlas/land-50m.json'
import { geoGraticule, geoGraticule10 } from 'd3-geo'
import { feature } from 'topojson-client'
import { simplifyPolygonGeometry } from './simplify-geo'
import type {
ExtendedFeature,
ExtendedFeatureCollection,
GeoGeometryObjects,
GeoSphere,
} from 'd3-geo'
type AtlasTopology = Parameters<typeof feature>[0]
export type CountryGeometry = Extract<
GeoGeometryObjects,
{ type: 'Polygon' | 'MultiPolygon' }
>
export interface CountryProperties {
name: string
}
export type CountryFeature = ExtendedFeature<CountryGeometry, CountryProperties>
export type LandFeature = ExtendedFeature<CountryGeometry, Record<never, never>>
export const worldSphere: GeoSphere = { type: 'Sphere' }
export const worldGraticule = geoGraticule10()
export const previewWorldGraticule = geoGraticule().step([30, 30])()
const countriesTopology = atlasTopology(
countriesAtlasJson,
'world-atlas countries-110m',
)
const countriesObject = countriesTopology.objects.countries
if (!countriesObject) {
throw new TypeError('world-atlas countries-110m is missing countries')
}
const convertedCountries = feature(countriesTopology, countriesObject)
if (convertedCountries.type !== 'FeatureCollection') {
throw new TypeError('world-atlas countries did not produce a collection')
}
export const worldCountries: readonly CountryFeature[] =
convertedCountries.features.flatMap<CountryFeature>((entry) => {
if (
!isCountryGeometry(entry.geometry) ||
!isRecord(entry.properties) ||
typeof entry.properties.name !== 'string'
) {
return []
}
return [
{
type: 'Feature',
id: entry.id === undefined ? entry.properties.name : String(entry.id),
geometry: entry.geometry,
properties: {
name: entry.properties.name,
},
},
]
})
if (worldCountries.length !== 177) {
throw new TypeError(
`Expected 177 world-atlas countries, got ${worldCountries.length}`,
)
}
export const worldCountryCollection: ExtendedFeatureCollection<CountryFeature> =
{
type: 'FeatureCollection',
features: [...worldCountries],
}
export const worldLand = convertLand(landAtlasJson, 'world-atlas land-110m')
export const previewWorldLand: LandFeature = {
...worldLand,
geometry: simplifyPolygonGeometry(worldLand.geometry, 2),
}
export const detailedWorldLand = convertLand(
detailedLandAtlasJson,
'world-atlas land-50m',
)
function atlasTopology(value: unknown, label: string): AtlasTopology {
if (!isAtlasTopology(value)) {
throw new TypeError(`${label} is not valid TopoJSON`)
}
return value
}
function convertLand(value: unknown, label: string): LandFeature {
const topology = atlasTopology(value, label)
const landObject = topology.objects.land
if (!landObject) {
throw new TypeError(`${label} is missing land`)
}
const converted = feature(topology, landObject)
const land =
converted.type === 'FeatureCollection' ? converted.features[0] : converted
if (!land || land.type !== 'Feature' || !isCountryGeometry(land.geometry)) {
throw new TypeError(`${label} did not produce polygon geometry`)
}
return {
type: 'Feature',
geometry: land.geometry,
properties: {},
}
}
function isCountryGeometry(
geometry: GeoGeometryObjects,
): geometry is CountryGeometry {
return geometry.type === 'Polygon' || geometry.type === 'MultiPolygon'
}
function isAtlasTopology(value: unknown): value is AtlasTopology {
return (
isRecord(value) &&
value.type === 'Topology' &&
Array.isArray(value.arcs) &&
isRecord(value.objects)
)
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null
}packages/charts-demo-data/src/learning-poverty-geography.ts96 lines · dependency
packages/charts-demo-data/src/learning-poverty-geography.ts
import { learningPoverty } from '@tanstack/charts-data/learning-poverty'
import { geoCentroid } from 'd3-geo'
import { worldCountries } from './country-atlas'
import { simplifyPolygonGeometry } from './simplify-geo'
import type { LearningPovertyRow } from '@tanstack/charts-data/learning-poverty'
import type { ExtendedFeature, GeoGeometryObjects } from 'd3-geo'
import type { CountryFeature, CountryGeometry } from './country-atlas'
type PointGeometry = Extract<GeoGeometryObjects, { type: 'Point' }>
export interface LearningPovertyProperties extends LearningPovertyRow {
name: string
}
export type LearningPovertyCountry = ExtendedFeature<
CountryGeometry,
LearningPovertyProperties
>
export type LearningPovertyPoint = ExtendedFeature<
PointGeometry,
LearningPovertyProperties
>
// The source uses World Bank names while world-atlas uses Natural Earth names.
// Tiny states absent from the 110m atlas remain unmatched.
const naturalEarthNameBySourceName: Readonly<Record<string, string>> = {
'Congo, Dem Rep': 'Dem. Rep. Congo',
'Congo, Rep': 'Congo',
'Cote d’Ivoire': "Côte d'Ivoire",
'Czech Republic': 'Czechia',
'Dominican Republic': 'Dominican Rep.',
'Egypt, Arab Rep': 'Egypt',
'Iran, Islamic Rep': 'Iran',
'Korea, Rep': 'South Korea',
'Kyrgyz Republic': 'Kyrgyzstan',
'Russian Federation': 'Russia',
'Slovak Republic': 'Slovakia',
'United States': 'United States of America',
'Yemen, Rep': 'Yemen',
}
const countryByName = new Map(
worldCountries.map((country) => [country.properties.name, country]),
)
export const learningPovertyCountries: readonly LearningPovertyCountry[] =
learningPoverty.flatMap((row) => {
const sourceName = row['Country Name']
const atlasName = naturalEarthNameBySourceName[sourceName] ?? sourceName
const country = countryByName.get(atlasName)
return country ? [joinCountry(country, row)] : []
})
export const previewLearningPovertyCountries: readonly LearningPovertyCountry[] =
learningPovertyCountries.map((country) => ({
...country,
geometry: simplifyPolygonGeometry(country.geometry, 2),
}))
if (learningPovertyCountries.length !== 95) {
throw new TypeError(
`Expected 95 learning-poverty countries in world-atlas, got ${learningPovertyCountries.length}`,
)
}
export const learningPovertyPoints: readonly LearningPovertyPoint[] =
learningPovertyCountries.map((country) => ({
type: 'Feature',
id: country.id,
geometry: {
type: 'Point',
coordinates: geoCentroid(country),
},
properties: country.properties,
}))
// Largest symbols render first so smaller countries remain selectable.
export const learningPovertyPointsByPopulation: readonly LearningPovertyPoint[] =
[...learningPovertyPoints].sort(
(left, right) => right.properties.population - left.properties.population,
)
function joinCountry(
country: CountryFeature,
row: LearningPovertyRow,
): LearningPovertyCountry {
return {
type: 'Feature',
id: country.id,
geometry: country.geometry,
properties: {
name: country.properties.name,
...row,
},
}
}packages/charts-demo-data/src/learning-poverty.js104 lines · dependency
packages/charts-demo-data/src/learning-poverty.js
// Generated by scripts/sync-demo-data.mjs from learning-poverty.csv.
// Do not edit this file directly.
export const learningPoverty = [
{"Country Name": "Afghanistan", "Out-of-School (OoS)": 49.6, "Below Minimum Proficiency (in School)": 87, "Learning Poverty": 93.4, "Assessment Year": 2013, Assessment: "NLA", population: 38928346, density: 60},
{"Country Name": "Argentina", "Out-of-School (OoS)": 0.6, "Below Minimum Proficiency (in School)": 53.6, "Learning Poverty": 53.9, "Assessment Year": 2013, Assessment: "LLECE", population: 45195774, density: 17},
{"Country Name": "Armenia", "Out-of-School (OoS)": 7.2, "Below Minimum Proficiency (in School)": 30, "Learning Poverty": 35, "Assessment Year": 2015, Assessment: "TIMSS", population: 2963243, density: 104},
{"Country Name": "Australia", "Out-of-School (OoS)": 3.2, "Below Minimum Proficiency (in School)": 5.5, "Learning Poverty": 8.6, "Assessment Year": 2016, Assessment: "PIRLS", population: 25499884, density: 3},
{"Country Name": "Austria", "Out-of-School (OoS)": 0, "Below Minimum Proficiency (in School)": 2.4, "Learning Poverty": 2.4, "Assessment Year": 2016, Assessment: "PIRLS", population: 9006398, density: 109},
{"Country Name": "Azerbaijan", "Out-of-School (OoS)": 5, "Below Minimum Proficiency (in School)": 19.2, "Learning Poverty": 23.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 10139177, density: 123},
{"Country Name": "Bahrain", "Out-of-School (OoS)": 2.1, "Below Minimum Proficiency (in School)": 30.6, "Learning Poverty": 32.1, "Assessment Year": 2016, Assessment: "PIRLS", population: 1701575, density: 2239},
{"Country Name": "Bangladesh", "Out-of-School (OoS)": 4.9, "Below Minimum Proficiency (in School)": 56, "Learning Poverty": 58.1, "Assessment Year": 2017, Assessment: "NLA", population: 164689383, density: 1265},
{"Country Name": "Belgium", "Out-of-School (OoS)": 1.3, "Below Minimum Proficiency (in School)": 5.1, "Learning Poverty": 6.4, "Assessment Year": 2016, Assessment: "PIRLS", population: 11589623, density: 383},
{"Country Name": "Benin", "Out-of-School (OoS)": 3.6, "Below Minimum Proficiency (in School)": 77.3, "Learning Poverty": 78.2, "Assessment Year": 2014, Assessment: "PASEC", population: 12123200, density: 108},
{"Country Name": "Botswana", "Out-of-School (OoS)": 7.2, "Below Minimum Proficiency (in School)": 44.3, "Learning Poverty": 48.3, "Assessment Year": 2011, Assessment: "PIRLS", population: 2351627, density: 4},
{"Country Name": "Brazil", "Out-of-School (OoS)": 2.7, "Below Minimum Proficiency (in School)": 46.9, "Learning Poverty": 48.4, "Assessment Year": 2013, Assessment: "LLECE", population: 212559417, density: 25},
{"Country Name": "Bulgaria", "Out-of-School (OoS)": 6.8, "Below Minimum Proficiency (in School)": 5.2, "Learning Poverty": 11.7, "Assessment Year": 2016, Assessment: "PIRLS", population: 6948445, density: 64},
{"Country Name": "Burkina Faso", "Out-of-School (OoS)": 31.7, "Below Minimum Proficiency (in School)": 78.6, "Learning Poverty": 85.4, "Assessment Year": 2014, Assessment: "PASEC", population: 20903273, density: 76},
{"Country Name": "Burundi", "Out-of-School (OoS)": 2.7, "Below Minimum Proficiency (in School)": 92.7, "Learning Poverty": 92.9, "Assessment Year": 2014, Assessment: "PASEC", population: 11890784, density: 463},
{"Country Name": "Cambodia", "Out-of-School (OoS)": 2.6, "Below Minimum Proficiency (in School)": 49.8, "Learning Poverty": 51.1, "Assessment Year": 2013, Assessment: "NLA", population: 16718965, density: 95},
{"Country Name": "Cameroon", "Out-of-School (OoS)": 5.2, "Below Minimum Proficiency (in School)": 75.9, "Learning Poverty": 77.2, "Assessment Year": 2014, Assessment: "PASEC", population: 26545863, density: 56},
{"Country Name": "Canada", "Out-of-School (OoS)": 0, "Below Minimum Proficiency (in School)": 4.3, "Learning Poverty": 4.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 37742154, density: 4},
{"Country Name": "Chad", "Out-of-School (OoS)": 21.1, "Below Minimum Proficiency (in School)": 97, "Learning Poverty": 97.7, "Assessment Year": 2014, Assessment: "PASEC", population: 16425864, density: 13},
{"Country Name": "Chile", "Out-of-School (OoS)": 9.3, "Below Minimum Proficiency (in School)": 30.3, "Learning Poverty": 36.8, "Assessment Year": 2013, Assessment: "LLECE", population: 19116201, density: 26},
{"Country Name": "China", "Out-of-School (OoS)": 0, "Below Minimum Proficiency (in School)": 18.2, "Learning Poverty": 18.2, "Assessment Year": 2016, Assessment: "NLA", population: 1439323776, density: 153},
{"Country Name": "Colombia", "Out-of-School (OoS)": 6.9, "Below Minimum Proficiency (in School)": 44.7, "Learning Poverty": 48.6, "Assessment Year": 2013, Assessment: "LLECE", population: 50882891, density: 46},
{"Country Name": "Congo, Dem Rep", "Out-of-School (OoS)": 63.2, "Below Minimum Proficiency (in School)": 62, "Learning Poverty": 86, "Assessment Year": 2011, Assessment: "NLA", population: 89561403, density: 40},
{"Country Name": "Congo, Rep", "Out-of-School (OoS)": 12.8, "Below Minimum Proficiency (in School)": 82.9, "Learning Poverty": 85.1, "Assessment Year": 2014, Assessment: "PASEC", population: 5518087, density: 16},
{"Country Name": "Costa Rica", "Out-of-School (OoS)": 1.1, "Below Minimum Proficiency (in School)": 31.7, "Learning Poverty": 32.5, "Assessment Year": 2013, Assessment: "LLECE", population: 5094118, density: 100},
{"Country Name": "Cote d’Ivoire", "Out-of-School (OoS)": 21.1, "Below Minimum Proficiency (in School)": 77.6, "Learning Poverty": 82.3, "Assessment Year": 2014, Assessment: "PASEC", population: 26378274, density: 83},
{"Country Name": "Croatia", "Out-of-School (OoS)": 3, "Below Minimum Proficiency (in School)": 1, "Learning Poverty": 4, "Assessment Year": 2011, Assessment: "PIRLS", population: 4105267, density: 73},
{"Country Name": "Cyprus", "Out-of-School (OoS)": 2.2, "Below Minimum Proficiency (in School)": 14.3, "Learning Poverty": 16.2, "Assessment Year": 2015, Assessment: "TIMSS", population: 1207359, density: 131},
{"Country Name": "Czech Republic", "Out-of-School (OoS)": 0, "Below Minimum Proficiency (in School)": 3, "Learning Poverty": 3, "Assessment Year": 2016, Assessment: "PIRLS", population: 10708981, density: 139},
{"Country Name": "Denmark", "Out-of-School (OoS)": 1, "Below Minimum Proficiency (in School)": 2.6, "Learning Poverty": 3.6, "Assessment Year": 2016, Assessment: "PIRLS", population: 5792202, density: 137},
{"Country Name": "Dominican Republic", "Out-of-School (OoS)": 6.6, "Below Minimum Proficiency (in School)": 79.4, "Learning Poverty": 80.7, "Assessment Year": 2013, Assessment: "LLECE", population: 10847910, density: 225},
{"Country Name": "Ecuador", "Out-of-School (OoS)": 1.9, "Below Minimum Proficiency (in School)": 62.1, "Learning Poverty": 62.8, "Assessment Year": 2013, Assessment: "LLECE", population: 17643054, density: 71},
{"Country Name": "Egypt, Arab Rep", "Out-of-School (OoS)": 1.4, "Below Minimum Proficiency (in School)": 69.2, "Learning Poverty": 69.6, "Assessment Year": 2016, Assessment: "PIRLS", population: 102334404, density: 103},
{"Country Name": "Ethiopia", "Out-of-School (OoS)": 14, "Below Minimum Proficiency (in School)": 88.7, "Learning Poverty": 90.3, "Assessment Year": 2015, Assessment: "NLA", population: 114963588, density: 115},
{"Country Name": "Finland", "Out-of-School (OoS)": 0.9, "Below Minimum Proficiency (in School)": 1.7, "Learning Poverty": 2.6, "Assessment Year": 2016, Assessment: "PIRLS", population: 5540720, density: 18},
{"Country Name": "France", "Out-of-School (OoS)": 0.9, "Below Minimum Proficiency (in School)": 6.3, "Learning Poverty": 7.1, "Assessment Year": 2016, Assessment: "PIRLS", population: 65273511, density: 119},
{"Country Name": "Georgia", "Out-of-School (OoS)": 0.4, "Below Minimum Proficiency (in School)": 13.5, "Learning Poverty": 13.8, "Assessment Year": 2016, Assessment: "PIRLS", population: 3989167, density: 57},
{"Country Name": "Germany", "Out-of-School (OoS)": 0.2, "Below Minimum Proficiency (in School)": 5.5, "Learning Poverty": 5.7, "Assessment Year": 2016, Assessment: "PIRLS", population: 83783942, density: 240},
{"Country Name": "Guatemala", "Out-of-School (OoS)": 10.1, "Below Minimum Proficiency (in School)": 63.6, "Learning Poverty": 67.3, "Assessment Year": 2013, Assessment: "LLECE", population: 17915568, density: 167},
{"Country Name": "Honduras", "Out-of-School (OoS)": 17.1, "Below Minimum Proficiency (in School)": 69.4, "Learning Poverty": 74.7, "Assessment Year": 2013, Assessment: "LLECE", population: 9904607, density: 89},
{"Country Name": "Hong Kong SAR, China", "Out-of-School (OoS)": 1.9, "Below Minimum Proficiency (in School)": 1.4, "Learning Poverty": 3.2, "Assessment Year": 2016, Assessment: "PIRLS", population: 7496981, density: 7140},
{"Country Name": "Hungary", "Out-of-School (OoS)": 3.1, "Below Minimum Proficiency (in School)": 2.9, "Learning Poverty": 5.9, "Assessment Year": 2016, Assessment: "PIRLS", population: 9660351, density: 107},
{"Country Name": "India", "Out-of-School (OoS)": 2.3, "Below Minimum Proficiency (in School)": 53.7, "Learning Poverty": 54.8, "Assessment Year": 2017, Assessment: "NLA", population: 1380004385, density: 464},
{"Country Name": "Indonesia", "Out-of-School (OoS)": 2.4, "Below Minimum Proficiency (in School)": 33.8, "Learning Poverty": 35.4, "Assessment Year": 2011, Assessment: "PIRLS", population: 273523615, density: 151},
{"Country Name": "Iran, Islamic Rep", "Out-of-School (OoS)": 0.9, "Below Minimum Proficiency (in School)": 35.1, "Learning Poverty": 35.7, "Assessment Year": 2016, Assessment: "PIRLS", population: 83992949, density: 52},
{"Country Name": "Ireland", "Out-of-School (OoS)": 0, "Below Minimum Proficiency (in School)": 2.3, "Learning Poverty": 2.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 4937786, density: 72},
{"Country Name": "Israel", "Out-of-School (OoS)": 2.9, "Below Minimum Proficiency (in School)": 9, "Learning Poverty": 11.7, "Assessment Year": 2016, Assessment: "PIRLS", population: 8655535, density: 400},
{"Country Name": "Italy", "Out-of-School (OoS)": 1.4, "Below Minimum Proficiency (in School)": 2.1, "Learning Poverty": 3.5, "Assessment Year": 2016, Assessment: "PIRLS", population: 60461826, density: 206},
{"Country Name": "Japan", "Out-of-School (OoS)": 1.2, "Below Minimum Proficiency (in School)": 1, "Learning Poverty": 2.2, "Assessment Year": 2015, Assessment: "TIMSS", population: 126476461, density: 347},
{"Country Name": "Jordan", "Out-of-School (OoS)": 4, "Below Minimum Proficiency (in School)": 50, "Learning Poverty": 52, "Assessment Year": 2015, Assessment: "TIMSS", population: 10203134, density: 115},
{"Country Name": "Kazakhstan", "Out-of-School (OoS)": 0.3, "Below Minimum Proficiency (in School)": 1.9, "Learning Poverty": 2.2, "Assessment Year": 2016, Assessment: "PIRLS", population: 18776707, density: 7},
{"Country Name": "Korea, Rep", "Out-of-School (OoS)": 2.7, "Below Minimum Proficiency (in School)": 0.3, "Learning Poverty": 3, "Assessment Year": 2015, Assessment: "TIMSS", population: 51269185, density: 527},
{"Country Name": "Kuwait", "Out-of-School (OoS)": 3.3, "Below Minimum Proficiency (in School)": 49.4, "Learning Poverty": 51, "Assessment Year": 2016, Assessment: "PIRLS", population: 4270571, density: 240},
{"Country Name": "Kyrgyz Republic", "Out-of-School (OoS)": 1.9, "Below Minimum Proficiency (in School)": 63.8, "Learning Poverty": 64.5, "Assessment Year": 2014, Assessment: "NLA", population: 6524195, density: 34},
{"Country Name": "Latvia", "Out-of-School (OoS)": 3.2, "Below Minimum Proficiency (in School)": 0.8, "Learning Poverty": 4, "Assessment Year": 2016, Assessment: "PIRLS", population: 1886198, density: 30},
{"Country Name": "Lithuania", "Out-of-School (OoS)": 0.3, "Below Minimum Proficiency (in School)": 2.7, "Learning Poverty": 3, "Assessment Year": 2016, Assessment: "PIRLS", population: 2722289, density: 43},
{"Country Name": "Macao SAR, China", "Out-of-School (OoS)": 1.3, "Below Minimum Proficiency (in School)": 2.4, "Learning Poverty": 3.7, "Assessment Year": 2016, Assessment: "PIRLS", population: 649335, density: 21645},
{"Country Name": "Madagascar", "Out-of-School (OoS)": 21.9, "Below Minimum Proficiency (in School)": 95.8, "Learning Poverty": 96.7, "Assessment Year": 2015, Assessment: "NLA", population: 27691018, density: 48},
{"Country Name": "Malaysia", "Out-of-School (OoS)": 1.4, "Below Minimum Proficiency (in School)": 11.7, "Learning Poverty": 12.9, "Assessment Year": 2017, Assessment: "NLA", population: 32365999, density: 99},
{"Country Name": "Mali", "Out-of-School (OoS)": 33, "Below Minimum Proficiency (in School)": 86.6, "Learning Poverty": 91, "Assessment Year": 2012, Assessment: "NLA", population: 20250833, density: 17},
{"Country Name": "Malta", "Out-of-School (OoS)": 2.4, "Below Minimum Proficiency (in School)": 26.8, "Learning Poverty": 28.6, "Assessment Year": 2016, Assessment: "PIRLS", population: 441543, density: 1380},
{"Country Name": "Mexico", "Out-of-School (OoS)": 1.2, "Below Minimum Proficiency (in School)": 42.5, "Learning Poverty": 43.2, "Assessment Year": 2013, Assessment: "LLECE", population: 128932753, density: 66},
{"Country Name": "Morocco", "Out-of-School (OoS)": 5.4, "Below Minimum Proficiency (in School)": 63.8, "Learning Poverty": 65.8, "Assessment Year": 2016, Assessment: "PIRLS", population: 36910560, density: 83},
{"Country Name": "Netherlands", "Out-of-School (OoS)": 0.3, "Below Minimum Proficiency (in School)": 1.3, "Learning Poverty": 1.6, "Assessment Year": 2016, Assessment: "PIRLS", population: 17134872, density: 508},
{"Country Name": "New Zealand", "Out-of-School (OoS)": 1.5, "Below Minimum Proficiency (in School)": 10, "Learning Poverty": 11.4, "Assessment Year": 2016, Assessment: "PIRLS", population: 4822233, density: 18},
{"Country Name": "Nicaragua", "Out-of-School (OoS)": 1.6, "Below Minimum Proficiency (in School)": 69.3, "Learning Poverty": 69.8, "Assessment Year": 2013, Assessment: "LLECE", population: 6624554, density: 55},
{"Country Name": "Niger", "Out-of-School (OoS)": 38.9, "Below Minimum Proficiency (in School)": 97.9, "Learning Poverty": 98.7, "Assessment Year": 2014, Assessment: "PASEC", population: 24206644, density: 19},
{"Country Name": "Norway", "Out-of-School (OoS)": 0.2, "Below Minimum Proficiency (in School)": 5.8, "Learning Poverty": 6, "Assessment Year": 2016, Assessment: "PIRLS", population: 5421241, density: 15},
{"Country Name": "Oman", "Out-of-School (OoS)": 1.5, "Below Minimum Proficiency (in School)": 40.9, "Learning Poverty": 41.8, "Assessment Year": 2016, Assessment: "PIRLS", population: 5106626, density: 16},
{"Country Name": "Pakistan", "Out-of-School (OoS)": 27.3, "Below Minimum Proficiency (in School)": 65, "Learning Poverty": 74.5, "Assessment Year": 2014, Assessment: "NLA", population: 220892340, density: 287},
{"Country Name": "Panama", "Out-of-School (OoS)": 7.1, "Below Minimum Proficiency (in School)": 64.1, "Learning Poverty": 66.6, "Assessment Year": 2013, Assessment: "LLECE", population: 4314767, density: 58},
{"Country Name": "Paraguay", "Out-of-School (OoS)": 10.8, "Below Minimum Proficiency (in School)": 71.3, "Learning Poverty": 74.4, "Assessment Year": 2013, Assessment: "LLECE", population: 7132538, density: 18},
{"Country Name": "Peru", "Out-of-School (OoS)": 4.2, "Below Minimum Proficiency (in School)": 53.7, "Learning Poverty": 55.7, "Assessment Year": 2013, Assessment: "LLECE", population: 32971854, density: 26},
{"Country Name": "Poland", "Out-of-School (OoS)": 4.4, "Below Minimum Proficiency (in School)": 2, "Learning Poverty": 6.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 37846611, density: 124},
{"Country Name": "Portugal", "Out-of-School (OoS)": 3.6, "Below Minimum Proficiency (in School)": 3, "Learning Poverty": 6.5, "Assessment Year": 2016, Assessment: "PIRLS", population: 10196709, density: 111},
{"Country Name": "Qatar", "Out-of-School (OoS)": 2.2, "Below Minimum Proficiency (in School)": 33.8, "Learning Poverty": 35.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 2881053, density: 248},
{"Country Name": "Romania", "Out-of-School (OoS)": 6.9, "Below Minimum Proficiency (in School)": 14.1, "Learning Poverty": 20, "Assessment Year": 2011, Assessment: "PIRLS", population: 19237691, density: 84},
{"Country Name": "Russian Federation", "Out-of-School (OoS)": 2.4, "Below Minimum Proficiency (in School)": 0.9, "Learning Poverty": 3.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 145934462, density: 9},
{"Country Name": "Saudi Arabia", "Out-of-School (OoS)": 2.5, "Below Minimum Proficiency (in School)": 36.7, "Learning Poverty": 38.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 34813871, density: 16},
{"Country Name": "Senegal", "Out-of-School (OoS)": 25.7, "Below Minimum Proficiency (in School)": 65.2, "Learning Poverty": 74.1, "Assessment Year": 2014, Assessment: "PASEC", population: 16743927, density: 87},
{"Country Name": "Serbia", "Out-of-School (OoS)": 0.8, "Below Minimum Proficiency (in School)": 7.4, "Learning Poverty": 8.1, "Assessment Year": 2015, Assessment: "TIMSS", population: 8737371, density: 100},
{"Country Name": "Singapore", "Out-of-School (OoS)": 0.1, "Below Minimum Proficiency (in School)": 2.7, "Learning Poverty": 2.8, "Assessment Year": 2016, Assessment: "PIRLS", population: 5850342, density: 8358},
{"Country Name": "Slovak Republic", "Out-of-School (OoS)": 2.1, "Below Minimum Proficiency (in School)": 6.6, "Learning Poverty": 8.5, "Assessment Year": 2016, Assessment: "PIRLS", population: 5459642, density: 114},
{"Country Name": "Slovenia", "Out-of-School (OoS)": 2.2, "Below Minimum Proficiency (in School)": 3.7, "Learning Poverty": 5.8, "Assessment Year": 2016, Assessment: "PIRLS", population: 2078938, density: 103},
{"Country Name": "South Africa", "Out-of-School (OoS)": 8.4, "Below Minimum Proficiency (in School)": 77.9, "Learning Poverty": 79.8, "Assessment Year": 2016, Assessment: "PIRLS", population: 59308690, density: 49},
{"Country Name": "Spain", "Out-of-School (OoS)": 1.5, "Below Minimum Proficiency (in School)": 3.4, "Learning Poverty": 4.9, "Assessment Year": 2016, Assessment: "PIRLS", population: 46754778, density: 94},
{"Country Name": "Sri Lanka", "Out-of-School (OoS)": 0.9, "Below Minimum Proficiency (in School)": 14, "Learning Poverty": 14.8, "Assessment Year": 2015, Assessment: "NLA", population: 21413249, density: 341},
{"Country Name": "Sweden", "Out-of-School (OoS)": 0.4, "Below Minimum Proficiency (in School)": 1.9, "Learning Poverty": 2.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 10099265, density: 25},
{"Country Name": "Thailand", "Out-of-School (OoS)": 2, "Below Minimum Proficiency (in School)": 21.9, "Learning Poverty": 23.5, "Assessment Year": 2011, Assessment: "TIMSS", population: 69799978, density: 137},
{"Country Name": "Togo", "Out-of-School (OoS)": 8.5, "Below Minimum Proficiency (in School)": 84.2, "Learning Poverty": 85.6, "Assessment Year": 2014, Assessment: "PASEC", population: 8278724, density: 152},
{"Country Name": "Trinidad and Tobago", "Out-of-School (OoS)": 1.3, "Below Minimum Proficiency (in School)": 19.7, "Learning Poverty": 20.7, "Assessment Year": 2016, Assessment: "PIRLS", population: 1399488, density: 273},
{"Country Name": "Tunisia", "Out-of-School (OoS)": 0.4, "Below Minimum Proficiency (in School)": 65.1, "Learning Poverty": 65.3, "Assessment Year": 2011, Assessment: "TIMSS", population: 11818619, density: 76},
{"Country Name": "Turkey", "Out-of-School (OoS)": 5, "Below Minimum Proficiency (in School)": 17.6, "Learning Poverty": 21.7, "Assessment Year": 2015, Assessment: "TIMSS", population: 84339067, density: 110},
{"Country Name": "Uganda", "Out-of-School (OoS)": 9, "Below Minimum Proficiency (in School)": 81.1, "Learning Poverty": 82.8, "Assessment Year": 2014, Assessment: "NLA", population: 45741007, density: 229},
{"Country Name": "United Arab Emirates", "Out-of-School (OoS)": 2.8, "Below Minimum Proficiency (in School)": 32.4, "Learning Poverty": 34.3, "Assessment Year": 2016, Assessment: "PIRLS", population: 9890402, density: 118},
{"Country Name": "United Kingdom", "Out-of-School (OoS)": 0.2, "Below Minimum Proficiency (in School)": 3.2, "Learning Poverty": 3.4, "Assessment Year": 2016, Assessment: "PIRLS", population: 67886011, density: 281},
{"Country Name": "United States", "Out-of-School (OoS)": 4.1, "Below Minimum Proficiency (in School)": 3.9, "Learning Poverty": 7.9, "Assessment Year": 2016, Assessment: "PIRLS", population: 331002651, density: 36},
{"Country Name": "Uruguay", "Out-of-School (OoS)": 0.5, "Below Minimum Proficiency (in School)": 41.4, "Learning Poverty": 41.7, "Assessment Year": 2013, Assessment: "LLECE", population: 3473730, density: 20},
{"Country Name": "Vietnam", "Out-of-School (OoS)": 0.6, "Below Minimum Proficiency (in School)": 1.1, "Learning Poverty": 1.7, "Assessment Year": 2011, Assessment: "NLA", population: 97338579, density: 314},
{"Country Name": "Yemen, Rep", "Out-of-School (OoS)": 18.9, "Below Minimum Proficiency (in School)": 93.5, "Learning Poverty": 94.7, "Assessment Year": 2011, Assessment: "TIMSS", population: 29825964, density: 56},
]packages/charts-demo-data/src/simplify-geo.ts130 lines · dependency
packages/charts-demo-data/src/simplify-geo.ts
import type { GeoGeometryObjects } from 'd3-geo'
type PolygonGeometry = Extract<
GeoGeometryObjects,
{ type: 'Polygon' | 'MultiPolygon' }
>
type Position = number[]
export function simplifyPolygonGeometry(
geometry: PolygonGeometry,
tolerance: number,
): PolygonGeometry {
if (geometry.type === 'Polygon') {
return {
type: 'Polygon',
coordinates: geometry.coordinates.map((ring) =>
simplifyRing(ring, tolerance),
),
}
}
return {
type: 'MultiPolygon',
coordinates: geometry.coordinates.map((polygon) =>
polygon.map((ring) => simplifyRing(ring, tolerance)),
),
}
}
function simplifyRing(
ring: readonly Position[],
tolerance: number,
): Position[] {
if (ring.length <= 4) return [...ring]
const openRing = ring.slice(0, -1)
const anchor = openRing[0]
if (!anchor) return [...ring]
let splitIndex = 1
let farthestDistance = 0
for (let index = 1; index < openRing.length; index += 1) {
const point = openRing[index]
if (!point) continue
const distance = squaredDistance(anchor, point)
if (distance > farthestDistance) {
farthestDistance = distance
splitIndex = index
}
}
const firstHalf = simplifyLine(
openRing.slice(0, splitIndex + 1),
tolerance * tolerance,
)
const secondHalf = simplifyLine(
[...openRing.slice(splitIndex), anchor],
tolerance * tolerance,
)
const simplified = [...firstHalf.slice(0, -1), ...secondHalf]
return simplified.length >= 4 ? simplified : [...ring]
}
function simplifyLine(
points: readonly Position[],
squaredTolerance: number,
): Position[] {
const first = points[0]
const last = points.at(-1)
if (!first || !last || points.length <= 2) return [...points]
let farthestIndex = 0
let farthestDistance = squaredTolerance
for (let index = 1; index < points.length - 1; index += 1) {
const point = points[index]
if (!point) continue
const distance = squaredSegmentDistance(point, first, last)
if (distance > farthestDistance) {
farthestDistance = distance
farthestIndex = index
}
}
if (farthestIndex === 0) return [first, last]
const left = simplifyLine(
points.slice(0, farthestIndex + 1),
squaredTolerance,
)
const right = simplifyLine(points.slice(farthestIndex), squaredTolerance)
return [...left.slice(0, -1), ...right]
}
function squaredSegmentDistance(
point: Position,
start: Position,
end: Position,
): number {
const [pointX = 0, pointY = 0] = point
let [x = 0, y = 0] = start
const [endX = 0, endY = 0] = end
let dx = endX - x
let dy = endY - y
if (dx !== 0 || dy !== 0) {
const progress =
((pointX - x) * dx + (pointY - y) * dy) / (dx * dx + dy * dy)
if (progress > 1) {
x = endX
y = endY
} else if (progress > 0) {
x += dx * progress
y += dy * progress
}
dx = pointX - x
dy = pointY - y
} else {
dx = pointX - x
dy = pointY - y
}
return dx * dx + dy * dy
}
function squaredDistance(left: Position, right: Position): number {
const dx = (left[0] ?? 0) - (right[0] ?? 0)
const dy = (left[1] ?? 0) - (right[1] ?? 0)
return dx * dx + dy * dy
}