TanStack
Catalog

Standard world projection gallery

geography

130 lines · 2 files · 3.2 kB

cases/110-projection-gallery/example.tsx92 lines · entry
cases/110-projection-gallery/example.tsx
import { Chart } from '@tanstack/charts/react/tooltip'
import { tooltip as exampleTooltip } from '@tanstack/charts/tooltip'

import { defineChart, facetChart } from '@tanstack/charts'
import { geoShape } from '@tanstack/charts/geo'
import {
  previewWorldLand,
  worldLand,
  worldSphere,
} from '@charts-poc/demo-data/country-atlas'
import { projectionGalleryData } from './projection'

const projectionColors = [
  ['#2563eb', '#7c3aed', '#0891b2', '#ea580c'],
  ['#1d4ed8', '#6d28d9', '#0e7490', '#c2410c'],
]

function projectionGalleryChart(input: ExampleOptions, preview: boolean) {
  const projections = projectionGalleryData()
  const color = {
    domain: projections.map(({ id }) => id),
    range: projectionColors[input.revision % 2] ?? projectionColors[0],
  }

  return facetChart(projections, {
    id: 'projection-gallery',
    by: 'id',
    columns: 2,
    gap: 0,
    label: false,
    chart: ([entry]) => {
      const projection = {
        type: preview ? () => entry.create().precision(2) : entry.create,
        fit: 'sphere' as const,
        inset: 8,
      }

      return {
        marks: [
          geoShape([worldSphere], {
            id: 'sphere',
            projection,
            fill: 'none',
            stroke: 'currentColor',
            strokeOpacity: 0.5,
            strokeWidth: 0.8,
          }),
          geoShape([preview ? previewWorldLand : worldLand], {
            id: 'land',
            projection,
            color: () => entry.id,
            fillOpacity: 0.78,
            stroke: 'currentColor',
            strokeOpacity: 0.28,
            strokeWidth: 0.45,
          }),
        ],
        color,
        guides: false,
        margin: 0,
      }
    },
  })
}

export const projectionGalleryDefinition = (input: ExampleOptions) =>
  projectionGalleryChart(input, false)
export interface ExampleOptions {
  width: number
  height: number
  revision: number
  preview?: boolean
}

export const exampleAriaLabel = 'Standard world projection gallery'

export const createExampleChart = (options: ExampleOptions) =>
  defineChart(projectionGalleryDefinition(options), {
    keyboard: true,
    tooltip: exampleTooltip,
  })

export const chart = createExampleChart({
  width: 640,
  height: 480,
  revision: 0,
  preview: false,
})

export default function Example() {
  return <Chart ariaLabel={exampleAriaLabel} definition={chart} height={480} />
}
cases/110-projection-gallery/projection.ts38 lines · dependency
cases/110-projection-gallery/projection.ts
import {
  geoEqualEarth,
  geoEquirectangular,
  geoMercator,
  geoNaturalEarth1,
} from 'd3-geo'
import type { GeoProjection } from 'd3-geo'

export type ProjectionGalleryId =
  'equal-earth' | 'natural-earth' | 'mercator' | 'equirectangular'

export interface ProjectionGalleryDatum {
  id: ProjectionGalleryId
  create: () => GeoProjection
}

const projections: readonly ProjectionGalleryDatum[] = [
  {
    id: 'equal-earth',
    create: geoEqualEarth,
  },
  {
    id: 'natural-earth',
    create: geoNaturalEarth1,
  },
  {
    id: 'mercator',
    create: geoMercator,
  },
  {
    id: 'equirectangular',
    create: geoEquirectangular,
  },
]

export function projectionGalleryData(): readonly ProjectionGalleryDatum[] {
  return projections
}