Source
126 chart lines · 2 files · 3.5 kBBenchmark harness excluded · shared/mount.ts
Gridded surface wind4,800 records · CSV · 164.0 kB
@charts-poc/demo-data/windSelection: Complete published snapshot
- longitude
- number
- latitude
- number
- u
- number
- v
- number
Remote Sensing Systems / giCentreobservablehq/plot@356f579b1d94 · revision 356f579b1d94 · test/data/wind.csv · Observable Plot repository MIT; upstream source credited · SHA-256 906a2dcf864aPinned snapshot
cases/38-contour-topography/tanstack.ts92 lines · entry
cases/38-contour-topography/tanstack.ts
import { createMark, defineChart } from '@tanstack/charts'
import { contours } from 'd3-contour'
import { geoPath, geoTransform } from 'd3-geo'
import { scaleThreshold } from 'd3-scale'
import { contourThresholds, windSpeedGrid } from './transform'
import { tanstackMount } from '../../shared/mount'
import type { ContourMultiPolygon } from 'd3-contour'
import type { ConformanceInput } from '../../types'
import type { SceneNode } from '@tanstack/charts'
const colors = ['#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa', '#2563eb']
const definition = (input: ConformanceInput) => {
const grid = windSpeedGrid(input.revision)
const geometry = contours()
.size([grid.width, grid.height])
.thresholds([...contourThresholds])(grid.values)
return defineChart({
marks: [contourMark(geometry, grid.width, grid.height)],
color: {
scale: scaleThreshold<number, string>,
domain: contourThresholds.slice(1),
range: colors,
},
margin: 12,
})
}
function contourMark(
geometry: readonly ContourMultiPolygon[],
gridWidth: number,
gridHeight: number,
) {
return createMark<ContourMultiPolygon, never, never>(({ markIndex }) => {
const id = `contour-${markIndex}`
return {
id,
channels: {
color: {
scale: 'color',
values: geometry.map((contour) => contour.value),
},
},
render: ({ chart, color }) => {
const projection = geoTransform({
point(x, y) {
this.stream.point(
chart.x + (x / gridWidth) * chart.width,
chart.y + chart.height - (y / gridHeight) * chart.height,
)
},
})
const path = geoPath(projection)
const children: SceneNode[] = []
for (let index = 0; index < geometry.length; index++) {
const contour = geometry[index]
if (contour === undefined) continue
const pathData = path(contour)
if (pathData === null) continue
children.push({
kind: 'area',
key: `${id}:${index}`,
points: [],
path: pathData,
style: {
fill: color(contour.value),
stroke: '#ffffff',
strokeWidth: 0.75,
},
})
}
return {
nodes: [
{
kind: 'group',
key: id,
className: 'ts-chart__area',
ariaHidden: true,
children,
},
],
}
},
}
})
}
export const mount = tanstackMount(definition, 'Filled wind-speed contours')cases/38-contour-topography/transform.ts34 lines · support
cases/38-contour-topography/transform.ts
import { wind } from '@charts-poc/demo-data/wind'
export interface ContourGrid {
width: number
height: number
values: number[]
}
const sourceWidth = 80
export const contourGridWidth = 64
export const contourGridHeight = 60
export const contourThresholds = [2, 4, 6, 8, 10]
export function windSpeedGrid(revision: number): ContourGrid {
const firstColumn = (revision % 2) * 8
const values: number[] = []
for (let row = 0; row < contourGridHeight; row++) {
const rowOffset = row * sourceWidth
for (let column = 0; column < contourGridWidth; column++) {
const observation = wind[rowOffset + firstColumn + column]
if (!observation) {
throw new Error('Observable Plot wind grid is incomplete.')
}
values.push(Math.hypot(observation.u, observation.v))
}
}
return {
width: contourGridWidth,
height: contourGridHeight,
values,
}
}