diff --git a/CHANGELOG-2021.md b/CHANGELOG-2021.md index f10862d703..eddba4758a 100644 --- a/CHANGELOG-2021.md +++ b/CHANGELOG-2021.md @@ -1,6 +1,6 @@ # Observable Plot - Changelog [2021] -Year: [Current (2025)](./CHANGELOG.md) · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · **2021** +Year: [Current](./CHANGELOG.md) · [2025](./CHANGELOG-2025.md) · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · **2021** ## 0.3.2 diff --git a/CHANGELOG-2022.md b/CHANGELOG-2022.md index c763d85da4..42afd2f25c 100644 --- a/CHANGELOG-2022.md +++ b/CHANGELOG-2022.md @@ -1,6 +1,6 @@ # Observable Plot - Changelog [2022] -Year: [Current (2025)](./CHANGELOG.md) · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · **2022** · [2021](./CHANGELOG-2021.md) +Year: [Current](./CHANGELOG.md) · [2025](./CHANGELOG-2025.md) · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · **2022** · [2021](./CHANGELOG-2021.md) ## 0.6.1 diff --git a/CHANGELOG-2023.md b/CHANGELOG-2023.md index fa55053e80..dc93353d9f 100644 --- a/CHANGELOG-2023.md +++ b/CHANGELOG-2023.md @@ -1,6 +1,6 @@ # Observable Plot - Changelog [2023] -Year: [Current (2025)](./CHANGELOG.md) · [2024](./CHANGELOG-2024.md) · **2023** · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) +Year: [Current](./CHANGELOG.md) · [2025](./CHANGELOG-2025.md) · [2024](./CHANGELOG-2024.md) · **2023** · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) ## 0.6.13 diff --git a/CHANGELOG-2024.md b/CHANGELOG-2024.md index 0b4e7fd293..936b16e44b 100644 --- a/CHANGELOG-2024.md +++ b/CHANGELOG-2024.md @@ -1,6 +1,6 @@ # Observable Plot - Changelog [2024] -Year: [Current (2025)](./CHANGELOG.md) · **2024** · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) +Year: [Current](./CHANGELOG.md) · [2025](./CHANGELOG-2025.md) · **2024** · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) ## 0.6.16 diff --git a/CHANGELOG-2025.md b/CHANGELOG-2025.md new file mode 100644 index 0000000000..8ad757586e --- /dev/null +++ b/CHANGELOG-2025.md @@ -0,0 +1,66 @@ +# Observable Plot - Changelog + +Year: [Current](./CHANGELOG.md) · **2025** · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) + +## 0.6.17 + +[Released February 14, 2025.](https://github.com/observablehq/plot/releases/tag/v0.6.17) + +The [**clip** mark option](https://observablehq.com/plot/features/marks#clip) now supports GeoJSON objects 🌎 in addition to the named *frame* and *sphere* clipping methods, allowing the visual extent of marks to be limited to arbitrary polygons. For instance, this Voronoi mesh of world airports is clipped to land boundaries: + +[a map of world airports with a Voronoi mesh clipped to land](https://observablehq.com/@observablehq/plot-world-airports) + +```js +Plot.plot({ + projection: {type: "orthographic", rotate: [110, -50]}, + marks: [ + Plot.dot(airports, {x: "longitude", y: "latitude", fill: "red", r: 1}), + Plot.voronoiMesh(airports, {x: "longitude", y: "latitude", clip: land}), + Plot.sphere(), + Plot.geo(land) + ] +}) +``` + +The GeoJSON object passed to the **clip** option is rendered as a [`clipPath` element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath) using the same path data that a [geo mark](https://observablehq.com/plot/marks/geo) would produce, respecting the plot’s top-level **projection** option, if any. For performance, `clipPath` elements are shared by marks clipped with the same GeoJSON object. For example, the [raster mark](https://observablehq.com/plot/marks/raster) and [contour mark](https://observablehq.com/plot/marks/contour) below show atmospheric water vapor measurements across the United States from [NASA Earth Observations](https://neo.gsfc.nasa.gov/view.php?datasetId=MYDAL2_M_SKY_WV); both marks are clipped to the nation’s boundary, censoring the (absurd) values that would otherwise be interpolated between Alaska, Southern California, and Hawai’i. + +[a map of water vapor measurements in the United States](https://observablehq.com/@observablehq/plot-us-water-vapor) + +```js +Plot.raster(vapor, { + fill: Plot.identity, + width: 360, + height: 180, + x1: -180, y1: 90, x2: 180, y2: -90, + interpolate: "barycentric", + blur: 10, + clip: nation +}).plot() +``` + +[The code for the map above is too long to reproduce here in its entirety; click the image above for the complete code.] + +The **clip** mark option can also be used to clip against arbitrary polygons, not just geographic boundaries. For example, to show the value of [Math.atan2](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) over the unit circle: + +[the value of atan2 across the unit disc, encoded as color](https://observablehq.com/@observablehq/plot-color-angle) + +```js +Plot.raster({ + x1: -1, x2: 1, y1: -1, y2: 1, + fill: (x, y) => Math.atan2(y, x), + clip: { + type: "Polygon", + coordinates: [ + d3.range(0, 2 * Math.PI, 0.1).map((angle) => [Math.cos(angle), Math.sin(angle)]) + ] + } +}).plot({width: 300, aspectRatio: 1}) +``` + +The interactive **tip** associated with a [waffle mark](https://observablehq.com/plot/marks/waffle) is now anchored to the “center” of the visual representation of the associated datum. That center depends on the shape that is referenced. For fun, here’s a chart from our unit tests showing these anchoring points for various amounts of waffling. Baffling! + +waffle mark with the anchor position of each datum marked with its value + +--- + +For earlier changes, continue to the [2024 CHANGELOG](./CHANGELOG-2024.md). diff --git a/CHANGELOG.md b/CHANGELOG.md index ebc5a79725..8660beef04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,66 +1,103 @@ # Observable Plot - Changelog -Year: **Current (2025)** · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) +Year: **Current** · [2025](./CHANGELOG-2025.md) · [2024](./CHANGELOG-2024.md) · [2023](./CHANGELOG-2023.md) · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md) -## 0.6.17 +## 0.6.18 -[Released February 14, 2025.](https://github.com/observablehq/plot/releases/tag/v0.6.17) +[Released TK, 2026.](https://github.com/observablehq/plot/releases/tag/v0.6.18) -The [**clip** mark option](https://observablehq.com/plot/features/marks#clip) now supports GeoJSON objects 🌎 in addition to the named *frame* and *sphere* clipping methods, allowing the visual extent of marks to be limited to arbitrary polygons. For instance, this Voronoi mesh of world airports is clipped to land boundaries: +The [area mark](https://observablehq.com/plot/marks/area) now supports a **line** option: when set to true, a stroked line is drawn on top of the filled area, removing the need for a separate line mark; when line is true, the the fill opacity defaults to 0.3 and the stroke options (`strokeWidth`, etc.) apply only to that line. -[a map of world airports with a Voronoi mesh clipped to land](https://observablehq.com/@observablehq/plot-world-airports) +a ridgeline plot of traffic patterns by location, with stroked lines on top of filled areas ```js Plot.plot({ - projection: {type: "orthographic", rotate: [110, -50]}, + height: 300, marks: [ - Plot.dot(airports, {x: "longitude", y: "latitude", fill: "red", r: 1}), - Plot.voronoiMesh(airports, {x: "longitude", y: "latitude", clip: land}), - Plot.sphere(), - Plot.geo(land) + Plot.areaY(traffic, { + x: "date", + y: "vehicles", + fy: "location", + fill: "#eee", + line: true, + fillOpacity: 1, + curve: "basis" + }) ] }) ``` -The GeoJSON object passed to the **clip** option is rendered as a [`clipPath` element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath) using the same path data that a [geo mark](https://observablehq.com/plot/marks/geo) would produce, respecting the plot’s top-level **projection** option, if any. For performance, `clipPath` elements are shared by marks clipped with the same GeoJSON object. For example, the [raster mark](https://observablehq.com/plot/marks/raster) and [contour mark](https://observablehq.com/plot/marks/contour) below show atmospheric water vapor measurements across the United States from [NASA Earth Observations](https://neo.gsfc.nasa.gov/view.php?datasetId=MYDAL2_M_SKY_WV); both marks are clipped to the nation’s boundary, censoring the (absurd) values that would otherwise be interpolated between Alaska, Southern California, and Hawai’i. +The [raster mark](https://observablehq.com/plot/marks/raster) now supports a **colorSpace** option, allowing interpolation in wide-gamut color spaces such as *display-p3*. For example, this raster plot of penguin body mass vs. flipper length encodes the `island` channel with vivid P3 colors: -[a map of water vapor measurements in the United States](https://observablehq.com/@observablehq/plot-us-water-vapor) +three clusters of penguins rendered as blurred color fields in vivid lime, orange, and violet ```js -Plot.raster(vapor, { - fill: Plot.identity, - width: 360, - height: 180, - x1: -180, y1: 90, x2: 180, y2: -90, - interpolate: "barycentric", - blur: 10, - clip: nation -}).plot() +Plot.plot({ + color: {range: ["oklch(90% 0.4 135)", "oklch(75% 0.3 55)", "oklch(65% 0.35 305)"]}, + marks: [ + Plot.raster(penguins, { + x: "body_mass_g", + y: "flipper_length_mm", + fill: "island", + interpolate: "random-walk", + blur: 8, + colorSpace: "display-p3" + }) + ] +}) +``` + +[Pointer tips](https://observablehq.com/plot/interactions/pointer) are now exclusive by default: when multiple pointer-driven marks overlap, only the closest point is shown. This avoids the common issue of multiple marks creating overlapping tips. This behavior is controlled by the new **pool** [mark option](https://observablehq.com/plot/features/marks#mark-options), which defaults to tru for tip marks; set `pool: false` to restore the previous behavior. + +The new top-level [**legend** plot option](https://observablehq.com/plot/features/legends) provides a convenient shorthand for requesting a legend, replacing the more verbose `color: {legend: true}` pattern. + +```js +Plot.plot({ + legend: true, + color: {type: "ordinal", domain: "ABCDEFGHIJ"}, + marks: [Plot.cellX("ABCDEFGHIJ")] +}) +``` + +The new [plot.scale("projection")](https://observablehq.com/plot/features/projections) method exposes the plot’s projection, allowing programmatic access after rendering. The returned object supports *apply* and *invert* for converting between geographic and pixel coordinates, and can be passed as the **projection** option of another plot. + +```js +const plot = Plot.plot({projection: "mercator", marks: [Plot.graticule()]}); +const projection = plot.scale("projection"); +projection.apply([-1.55, 47.22]) // [316.7, 224.2] + +// reuse in a new plot with different marks but the same projection +Plot.plot({projection, marks: [Plot.geo(countries)]}) ``` -[The code for the map above is too long to reproduce here in its entirety; click the image above for the complete code.] +The [opacity scale](https://observablehq.com/plot/features/scales) and associated legend now support *ordinal* and *threshold* types. -The **clip** mark option can also be used to clip against arbitrary polygons, not just geographic boundaries. For example, to show the value of [Math.atan2](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) over the unit circle: +```js +Plot.cellX(d3.range(10), {fill: "red", opacity: Plot.identity}).plot({ + opacity: {type: "threshold", legend: true, domain: [2, 5, 8], range: [0.2, 0.4, 0.6, 0.8]} +}) +``` -[the value of atan2 across the unit disc, encoded as color](https://observablehq.com/@observablehq/plot-color-angle) +Plot now supports the `light-dark(…)` CSS color syntax, making it easier to define colors that adapt to dark mode. Color and opacity legends also render correctly with `light-dark(…)`, `currentColor`, and display-p3 colors. ```js -Plot.raster({ - x1: -1, x2: 1, y1: -1, y2: 1, - fill: (x, y) => Math.atan2(y, x), - clip: { - type: "Polygon", - coordinates: [ - d3.range(0, 2 * Math.PI, 0.1).map((angle) => [Math.cos(angle), Math.sin(angle)]) - ] - } -}).plot({width: 300, aspectRatio: 1}) +Plot.barX(alphabet, {x: "frequency", y: "letter", fill: "light-dark(steelblue, orange)"}) ``` -The interactive **tip** associated with a [waffle mark](https://observablehq.com/plot/marks/waffle) is now anchored to the “center” of the visual representation of the associated datum. That center depends on the shape that is referenced. For fun, here’s a chart from our unit tests showing these anchoring points for various amounts of waffling. Baffling! +This release includes several more bug fixes and improvements: + +The [tick format](https://observablehq.com/plot/features/scales) now defaults to a comma-less format when tick values are likely year integers. + +before and after comparison of year tick labels, showing commas removed + +The [stack transform](https://observablehq.com/plot/transforms/stack) now preserves NaN values instead of coercing them to zero. The [dodge transform](https://observablehq.com/plot/transforms/dodge) no longer crashes when the radius is negative. The [area mark](https://observablehq.com/plot/marks/area) now respects the **reduce** option when using the [interval transform](https://observablehq.com/plot/transforms/interval). + +For line and area marks, the *z* channel now defaults to null when the color channel is redundant with the position channel (*e.g.*, `lineY(data, {y: "value", stroke: "value"})`), preventing the line from being split into single-point series. The default offset in windowless environments (*e.g.* server-side rendering) is now zero, prioritizing high-density displays. The default colors for the [box mark](https://observablehq.com/plot/marks/box) and [tree transform](https://observablehq.com/plot/transforms/tree) now support dark mode. + +The [tip mark](https://observablehq.com/plot/marks/tip) no longer crashes when the title channel contains null; it now ignores the contours channel and literal symbols; and the tip arrow is smaller when using a corner anchor. [Markers](https://observablehq.com/plot/features/markers) no longer inherit `strokeDasharray`. The [pointer transform](https://observablehq.com/plot/interactions/pointer) now allows `pointerdown` event propagation. -waffle mark with the anchor position of each datum marked with its value +Various type fixes and documentation improvements; thanks @gka and @jsoref! --- -For earlier changes, continue to the [2024 CHANGELOG](./CHANGELOG-2024.md). +For earlier changes, continue to the [2025 CHANGELOG](./CHANGELOG-2025.md). diff --git a/img/axisYears.png b/img/axisYears.png new file mode 100644 index 0000000000..928d711229 Binary files /dev/null and b/img/axisYears.png differ diff --git a/img/raster-p3.png b/img/raster-p3.png new file mode 100644 index 0000000000..b8e531a6de Binary files /dev/null and b/img/raster-p3.png differ diff --git a/img/ridgeline.png b/img/ridgeline.png new file mode 100644 index 0000000000..f8104d132d Binary files /dev/null and b/img/ridgeline.png differ diff --git a/package.json b/package.json index 007363bf97..c1275cbf77 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@observablehq/plot", "description": "A JavaScript library for exploratory data visualization.", - "version": "0.6.17", + "version": "0.6.18", "author": { "name": "Observable, Inc.", "url": "https://observablehq.com"