Skip to content

Commit d4c477a

Browse files
committed
fold spatial interpolators into raster
1 parent f9139e1 commit d4c477a

File tree

5 files changed

+61
-61
lines changed

5 files changed

+61
-61
lines changed

docs/.vitepress/config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ export default defineConfig({
6262
{text: "Formats", link: "/features/formats"},
6363
{text: "Markers", link: "/features/markers"},
6464
{text: "Shorthand", link: "/features/shorthand"},
65-
{text: "Spatial interpolators", link: "/features/spatial-interpolators"},
6665
{text: "Accessibility", link: "/features/accessibility"},
6766
]
6867
},

docs/features/scales.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -974,8 +974,8 @@ Both [*plot*.scale](./plots.md#plot-scale-name) and [Plot.scale](#scale-options-
974974
To reuse a scale across plots, pass the corresponding scale object into another plot specification:
975975

976976
```js
977-
const plot1 = Plot.plot();
978-
const plot2 = Plot.plot({, color: plot1.scale("color")});
977+
const plot1 = Plot.plot(options);
978+
const plot2 = Plot.plot({...options, color: plot1.scale("color")});
979979
```
980980

981981
For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value.

docs/features/spatial-interpolators.md

Lines changed: 0 additions & 51 deletions
This file was deleted.

docs/marks/contour.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function mandelbrot(x, y) {
2929
To produce a heatmap instead of contours, see the [raster mark](./raster.md). For contours of estimated point density, see the [density mark](./density.md).
3030
:::
3131

32-
The **contour mark** draws [isolines](https://en.wikipedia.org/wiki/Contour_line) to delineate regions above and below a particular continuous value. These contours are computed by applying the [marching squares algorithm](https://en.wikipedia.org/wiki/Marching_squares) to a discrete grid. Like the [raster mark](./raster.md), the grid can be constructed either by [interpolating spatial samples](../features/spatial-interpolators.md) (arbitrary points in **x** and **y**) or by sampling a continuous function *f*(*x*,*y*) along the grid.
32+
The **contour mark** draws [isolines](https://en.wikipedia.org/wiki/Contour_line) to delineate regions above and below a particular continuous value. These contours are computed by applying the [marching squares algorithm](https://en.wikipedia.org/wiki/Marching_squares) to a discrete grid. Like the [raster mark](./raster.md), the grid can be constructed either by [interpolating spatial samples](./raster.md#spatial-interpolators) (arbitrary points in **x** and **y**) or by sampling a continuous function *f*(*x*,*y*) along the grid.
3333

3434
For example, the contours below show the topography of the [Maungawhau volcano](https://en.wikipedia.org/wiki/Maungawhau), produced from a {{volcano.width}}×{{volcano.height}} grid of elevation samples.
3535

@@ -167,7 +167,7 @@ Plot.contour(ca55, {x: "LONGITUDE", y: "LATITUDE", fill: "MAG_IGRF90", blur: 4})
167167
:::
168168

169169
:::tip
170-
The contour mark also supports the **interpolate** option for control over [spatial interpolation](../features/spatial-interpolators.md).
170+
The contour mark also supports the **interpolate** option for control over [spatial interpolation](./raster.md#spatial-interpolators).
171171
:::
172172

173173
The contour mark supports Plot’s [projection system](../features/projections.md). The chart below shows global atmospheric water vapor measurements from [NASA Earth Observations](https://neo.gsfc.nasa.gov/view.php?datasetId=MYDAL2_M_SKY_WV).
@@ -230,7 +230,7 @@ When faceting, the sample function *f*(*x*,*y*) is passed a third argument of th
230230

231231
## Contour options
232232

233-
If *data* is provided, it represents discrete samples in abstract coordinates **x** and **y**; the **value** channel specifies further abstract quantitative values (_e.g._, height in a topographic map) to be [spatially interpolated](../features/spatial-interpolators.md) to produce the underlying raster grid.
233+
If *data* is provided, it represents discrete samples in abstract coordinates **x** and **y**; the **value** channel specifies further abstract quantitative values (_e.g._, height in a topographic map) to be [spatially interpolated](./raster.md#spatial-interpolators) to produce the underlying raster grid.
234234

235235
```js
236236
Plot.contour(volcano.values, {width: volcano.width, height: volcano.height, value: Plot.identity})

docs/marks/raster.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function mandelbrot(x, y) {
3030
To produce contours instead of a heatmap, see the [contour mark](./contour.md).
3131
:::
3232

33-
The **raster mark** renders a [raster image](https://en.wikipedia.org/wiki/Raster_graphics)—that is, an image formed by discrete pixels in a grid, not a vector graphic like other marks. And whereas the [image mark](./image.md) shows an *existing* image, the raster mark *creates* one from abstract data, either by [interpolating spatial samples](../features/spatial-interpolators.md) (arbitrary points in **x** and **y**) or by sampling a continuous function *f*(*x*,*y*) along the grid.
33+
The **raster mark** renders a [raster image](https://en.wikipedia.org/wiki/Raster_graphics)—that is, an image formed by discrete pixels in a grid, not a vector graphic like other marks. And whereas the [image mark](./image.md) shows an *existing* image, the raster mark *creates* one from abstract data, either by [interpolating spatial samples](#spatial-interpolators) (arbitrary points in **x** and **y**) or by sampling a continuous function *f*(*x*,*y*) along the grid.
3434

3535
For example, the heatmap below shows the topography of the [Maungawhau volcano](https://en.wikipedia.org/wiki/Maungawhau), produced from a {{volcano.width}}×{{volcano.height}} grid of elevation samples.
3636

@@ -179,7 +179,7 @@ Plot.raster(ca55, {x: "LONGITUDE", y: "LATITUDE", fill: "MAG_IGRF90", interpolat
179179
:::
180180

181181
:::tip
182-
If none of the built-in [spatial interpolators](../features/spatial-interpolators.md) suffice, you can write your own as a custom function!
182+
If none of the built-in [spatial interpolators](#spatial-interpolators) suffice, you can write your own as a custom function!
183183
:::
184184

185185
The raster mark can interpolate categorical values, too! Below, this creates an interesting “map” of penguin species in the space of culmen length _vs._ depth.
@@ -257,7 +257,7 @@ Plot.plot({
257257

258258
## Raster options
259259

260-
If *data* is provided, it represents discrete samples in abstract coordinates **x** and **y**; the **fill** and **fillOpacity** channels specify further abstract values (_e.g._, height in a topographic map) to be [spatially interpolated](../features/spatial-interpolators.md) to produce an image.
260+
If *data* is provided, it represents discrete samples in abstract coordinates **x** and **y**; the **fill** and **fillOpacity** channels specify further abstract values (_e.g._, height in a topographic map) to be [spatially interpolated](#spatial-interpolators) to produce an image.
261261

262262
```js
263263
Plot.raster(volcano.values, {width: volcano.width, height: volcano.height})
@@ -286,7 +286,7 @@ If **width** is specified, **x1** defaults to 0 and **x2** defaults to **width**
286286

287287
The following raster-specific constant options are supported:
288288

289-
* **interpolate** - the [spatial interpolation method](../features/spatial-interpolators.md)
289+
* **interpolate** - the [spatial interpolation method](#spatial-interpolators)
290290
* **imageRendering** - the [image-rendering attribute](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/image-rendering); defaults to *auto* (bilinear)
291291
* **blur** - a non-negative pixel radius for smoothing; defaults to 0
292292

@@ -299,3 +299,55 @@ Plot.raster(volcano.values, {width: volcano.width, height: volcano.height})
299299
```
300300

301301
Returns a new raster mark with the given (optional) *data* and *options*.
302+
303+
## Spatial interpolators
304+
305+
The [raster](#raster-mark) and [contour](./contour.md) marks use **spatial interpolators** to populate a raster grid from a discrete set of (often ungridded) spatial samples. The **interpolate** option controls how these marks compute the raster grid. The following built-in methods are provided:
306+
307+
* *none* (or null) - assign each sample to the containing pixel
308+
* *nearest* - assign each pixel to the closest sample’s value (Voronoi diagram)
309+
* *barycentric* - apply barycentric interpolation over the Delaunay triangulation
310+
* *random-walk* - apply a random walk from each pixel, stopping when near a sample
311+
312+
The **interpolate** option can also be specified as a function with the following arguments:
313+
314+
* *index* - an array of numeric indexes into the channels *x*, *y*, *value*
315+
* *width* - the width of the raster grid; a positive integer
316+
* *height* - the height of the raster grid; a positive integer
317+
* *x* - an array of values representing the *x*-position of samples
318+
* *y* - an array of values representing the *y*-position of samples
319+
* *value* - an array of values representing the sample’s observed value
320+
321+
So, *x*[*index*[0]] represents the *x*-position of the first sample, *y*[*index*[0]] its *y*-position, and *value*[*index*[0]] its value (*e.g.*, the observed height for a topographic map).
322+
323+
## interpolateNone(*index*, *width*, *height*, *x*, *y*, *value*)
324+
325+
```js
326+
Plot.raster(ca55, {x: "LONGITUDE", y: "LATITUDE", fill: "MAG_IGRF90", interpolate: Plot.interpolateNone})
327+
```
328+
329+
Applies a simple forward mapping of samples, binning them into pixels in the raster grid without any blending or interpolation. If multiple samples map to the same pixel, the last one wins; this can introduce bias if the points are not in random order, so use [Plot.shuffle](../transforms/sort.md#shuffle-options) to randomize the input if needed.
330+
331+
## interpolateNearest(*index*, *width*, *height*, *x*, *y*, *value*)
332+
333+
```js
334+
Plot.raster(ca55, {x: "LONGITUDE", y: "LATITUDE", fill: "MAG_IGRF90", interpolate: Plot.interpolateNearest})
335+
```
336+
337+
Assigns each pixel in the raster grid the value of the closest sample; effectively a Voronoi diagram.
338+
339+
## interpolatorBarycentric(*options*)
340+
341+
```js
342+
Plot.raster(ca55, {x: "LONGITUDE", y: "LATITUDE", fill: "MAG_IGRF90", interpolate: Plot.interpolatorBarycentric()})
343+
```
344+
345+
Constructs a Delaunay triangulation of the samples, and then for each pixel in the raster grid, determines the triangle that covers the pixel’s centroid and interpolates the values associated with the triangle’s vertices using [barycentric coordinates](https://en.wikipedia.org/wiki/Barycentric_coordinate_system). If the interpolated values are ordinal or categorical (_i.e._, anything other than numbers or dates), then one of the three values will be picked randomly weighted by the barycentric coordinates; the given **random** number generator will be used, which defaults to a [linear congruential generator](https://github.com/d3/d3-random/blob/main/README.md#randomLcg) with a fixed seed (for deterministic results).
346+
347+
## interpolatorRandomWalk(*options*)
348+
349+
```js
350+
Plot.raster(ca55, {x: "LONGITUDE", y: "LATITUDE", fill: "MAG_IGRF90", interpolate: Plot.interpolatorRandomWalk()})
351+
```
352+
353+
For each pixel in the raster grid, initiates a random walk, stopping when either the walk is within a given distance (**minDistance**) of a sample or the maximum allowable number of steps (**maxSteps**) have been taken, and then assigning the current pixel the closest sample’s value. The random walk uses the “walk on spheres” algorithm in two dimensions described by [Sawhney and Crane](https://www.cs.cmu.edu/~kmcrane/Projects/MonteCarloGeometryProcessing/index.html), SIGGRAPH 2020; the given **random** number generator will be used, which defaults to a [linear congruential generator](https://github.com/d3/d3-random/blob/main/README.md#randomLcg) with a fixed seed (for deterministic results).

0 commit comments

Comments
 (0)