Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions benchmarks/conformance/shared/fixtures/geography.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { beagle } from '@tanstack/charts-data/beagle'
import { learningPoverty } from '@tanstack/charts-data/learning-poverty'
import { usCountyUnemployment } from '@tanstack/charts-data/us-county-unemployment'
import { geoContains } from 'd3-geo'
import { describe, expect, it } from 'vitest'
import { beagleRoute } from '../../cases/105-route-map/transform'
import {
Expand All @@ -18,15 +19,28 @@ import {
} from '../transforms/learning-poverty'

describe('geography demo data', () => {
it('converts the published world atlases without replacing their geometry', () => {
expect(worldCountries).toHaveLength(177)
it('converts the published world atlases without New Zealand', () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that this is unnecessary. New Zealand shouldn't have been on the map in the first place. It's just a fix, no need to say that the bug is no longer present.

expect(worldCountries).toHaveLength(176)
expect(worldCountries.every(({ id }) => typeof id === 'string')).toBe(true)
expect(
worldCountries.some(
({ properties }) => properties.name === 'New Zealand',
),
).toBe(false)
expect(worldLand.geometry.coordinates).not.toHaveLength(0)
expect(detailedWorldLand.geometry.coordinates).not.toHaveLength(0)
expect(geoContains(worldLand, [174.78, -41.29])).toBe(false)
expect(geoContains(detailedWorldLand, [174.78, -41.29])).toBe(false)
expect(geoContains(worldLand, [151.21, -33.87])).toBe(true)
})

it('joins learning-poverty rows without changing published values', () => {
expect(learningPovertyCountries).toHaveLength(95)
expect(learningPovertyCountries).toHaveLength(94)
expect(
learningPovertyCountries.some(
({ properties }) => properties.name === 'New Zealand',
),
).toBe(false)

const sourceByName = new Map(
learningPoverty.map((row) => [row['Country Name'], row]),
Expand Down
84 changes: 69 additions & 15 deletions packages/charts-demo-data/src/country-atlas.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import countriesAtlasJson from 'world-atlas/countries-110m.json'
import detailedCountriesAtlasJson from 'world-atlas/countries-50m.json'
import landAtlasJson from 'world-atlas/land-110m.json'
import detailedLandAtlasJson from 'world-atlas/land-50m.json'
import { geoGraticule, geoGraticule10 } from 'd3-geo'
import {
geoCentroid,
geoContains,
geoGraticule,
geoGraticule10,
} from 'd3-geo'
import { feature } from 'topojson-client'
import { simplifyPolygonGeometry } from './simplify-geo'
import type {
Expand All @@ -13,6 +19,8 @@ import type {

type AtlasTopology = Parameters<typeof feature>[0]

const excludedCountryName = 'New Zealand'

@NotQuiteLoona NotQuiteLoona Aug 16, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see, the context of this variable's usage is self-explanatory enough. While this is only a small catch, I think that unless we plan on changing or extending excluded countries it may be better to just call it something like newZealand, as it's a one-time constant that is not planned to be changed and accomplishes only one goal of transmitting the name of New Zealand.


export type CountryGeometry = Extract<
GeoGeometryObjects,
{ type: 'Polygon' | 'MultiPolygon' }
Expand Down Expand Up @@ -53,6 +61,8 @@ export const worldCountries: readonly CountryFeature[] =
return []
}

if (entry.properties.name === excludedCountryName) return []

return [
{
type: 'Feature',
Expand All @@ -65,9 +75,9 @@ export const worldCountries: readonly CountryFeature[] =
]
})

if (worldCountries.length !== 177) {
if (worldCountries.length !== 176) {
throw new TypeError(
`Expected 177 world-atlas countries, got ${worldCountries.length}`,
`Expected 176 world-atlas countries after excluding ${excludedCountryName}, got ${worldCountries.length}`,
)
}

Expand All @@ -77,14 +87,19 @@ export const worldCountryCollection: ExtendedFeatureCollection<CountryFeature> =
features: [...worldCountries],
}

export const worldLand = convertLand(landAtlasJson, 'world-atlas land-110m')
export const worldLand = convertLandWithoutExcludedCountry(
landAtlasJson,
countriesAtlasJson,
'world-atlas 110m',
)
export const previewWorldLand: LandFeature = {
...worldLand,
geometry: simplifyPolygonGeometry(worldLand.geometry, 2),
}
export const detailedWorldLand = convertLand(
export const detailedWorldLand = convertLandWithoutExcludedCountry(
detailedLandAtlasJson,
'world-atlas land-50m',
detailedCountriesAtlasJson,
'world-atlas 50m',
)

function atlasTopology(value: unknown, label: string): AtlasTopology {
Expand All @@ -94,23 +109,62 @@ function atlasTopology(value: unknown, label: string): AtlasTopology {
return value
}

function convertLand(value: unknown, label: string): LandFeature {
const topology = atlasTopology(value, label)
const landObject = topology.objects.land
function convertLandWithoutExcludedCountry(
landValue: unknown,
countriesValue: unknown,
label: string,
): LandFeature {
const countriesTopology = atlasTopology(countriesValue, `${label} countries`)
const countriesObject = countriesTopology.objects.countries
if (!countriesObject) {
throw new TypeError(`${label} is missing countries`)
}

const convertedCountries = feature(countriesTopology, countriesObject)
if (convertedCountries.type !== 'FeatureCollection') {
throw new TypeError(`${label} countries did not produce a collection`)
}
const excludedCountry = convertedCountries.features.find(
(country) =>
isRecord(country.properties) &&
country.properties.name === excludedCountryName,
)
if (!excludedCountry || !isCountryGeometry(excludedCountry.geometry)) {
throw new TypeError(`${label} did not contain ${excludedCountryName}`)
}

const landTopology = atlasTopology(landValue, `${label} land`)
const landObject = landTopology.objects.land
if (!landObject) {
throw new TypeError(`${label} is missing land`)
}

const converted = feature(topology, landObject)
const convertedLand = feature(landTopology, 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`)
convertedLand.type === 'FeatureCollection'
? convertedLand.features[0]
: convertedLand
if (!land || !isCountryGeometry(land.geometry)) {
throw new TypeError(`${label} did not produce polygon land geometry`)
}

const polygons =
land.geometry.type === 'MultiPolygon'
? land.geometry.coordinates
: [land.geometry.coordinates]
const includedPolygons = polygons.filter((coordinates) => {
const polygon: CountryGeometry = { type: 'Polygon', coordinates }
return !geoContains(excludedCountry, geoCentroid(polygon))
})
if (includedPolygons.length === polygons.length) {
throw new TypeError(`${label} land did not contain ${excludedCountryName}`)
}

return {
type: 'Feature',
geometry: land.geometry,
geometry: {
type: 'MultiPolygon',
coordinates: includedPolygons,
},
properties: {},
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/charts-demo-data/src/learning-poverty-geography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export const previewLearningPovertyCountries: readonly LearningPovertyCountry[]
geometry: simplifyPolygonGeometry(country.geometry, 2),
}))

if (learningPovertyCountries.length !== 95) {
if (learningPovertyCountries.length !== 94) {
throw new TypeError(
`Expected 95 learning-poverty countries in world-atlas, got ${learningPovertyCountries.length}`,
`Expected 94 learning-poverty countries in the filtered world-atlas, got ${learningPovertyCountries.length}`,
)
}

Expand Down