Skip to content

Commit

Permalink
default diverging based on scheme (observablehq#845)
Browse files Browse the repository at this point in the history
* default diverging based on scheme

* document

* typo

* Update README

Co-authored-by: Philippe Rivière <fil@rezo.net>
  • Loading branch information
mbostock and Fil authored May 29, 2022
1 parent 9c6197e commit 913629f
Show file tree
Hide file tree
Showing 14 changed files with 31 additions and 27 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ The following diverging scale schemes are supported:
* <sub><img src="./img/burd.png" width="32" height="16" alt="burd"></sub> *burd*
* <sub><img src="./img/buylrd.png" width="32" height="16" alt="buylrd"></sub> *buylrd*

Diverging color scales support a *scale*.**pivot** option, which defaults to zero. Values below the pivot will use the lower half of the color scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot will use the upper half (grays for *rdgy*).
Picking a diverging color scheme name defaults the scale type to *diverging*; set the scale type to *linear* to treat the color scheme as sequential instead. Diverging color scales support a *scale*.**pivot** option, which defaults to zero. Values below the pivot will use the lower half of the color scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot will use the upper half (grays for *rdgy*).

The following cylical color schemes are supported:

Expand Down
7 changes: 5 additions & 2 deletions src/scales.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {isColor, isEvery, isOrdinal, isFirst, isTemporal, isTemporalString, isNu
import {registry, color, position, radius, opacity, symbol, length} from "./scales/index.js";
import {ScaleLinear, ScaleSqrt, ScalePow, ScaleLog, ScaleSymlog, ScaleQuantile, ScaleQuantize, ScaleThreshold, ScaleIdentity} from "./scales/quantitative.js";
import {ScaleDiverging, ScaleDivergingSqrt, ScaleDivergingPow, ScaleDivergingLog, ScaleDivergingSymlog} from "./scales/diverging.js";
import {isDivergingScheme} from "./scales/schemes.js";
import {ScaleTime, ScaleUtc} from "./scales/temporal.js";
import {ScaleOrdinal, ScalePoint, ScaleBand, ordinalImplicit} from "./scales/ordinal.js";
import {isSymbol, maybeSymbol} from "./symbols.js";
Expand Down Expand Up @@ -212,7 +213,7 @@ function formatScaleType(type) {
return typeof type === "symbol" ? type.description : type;
}

function inferScaleType(key, channels, {type, domain, range, scheme}) {
function inferScaleType(key, channels, {type, domain, range, scheme, pivot}) {
// The facet scales are always band scales; this cannot be changed.
if (key === "fx" || key === "fy") return "band";

Expand Down Expand Up @@ -261,18 +262,20 @@ function inferScaleType(key, channels, {type, domain, range, scheme}) {
// Otherwise, infer the scale type from the data! Prefer the domain, if
// present, over channels. (The domain and channels should be consistently
// typed, and the domain is more explicit and typically much smaller.) We only
// check the first defined value for expedience and simplicitly; we expect
// check the first defined value for expedience and simplicity; we expect
// that the types are consistent.
if (domain !== undefined) {
if (isOrdinal(domain)) return asOrdinalType(kind);
if (isTemporal(domain)) return "utc";
if (kind === color && (pivot != null || isDivergingScheme(scheme))) return "diverging";
return "linear";
}

// If any channel is ordinal or temporal, it takes priority.
const values = channels.map(({value}) => value).filter(value => value !== undefined);
if (values.some(isOrdinal)) return asOrdinalType(kind);
if (values.some(isTemporal)) return "utc";
if (kind === color && (pivot != null || isDivergingScheme(scheme))) return "diverging";
return "linear";
}

Expand Down
18 changes: 18 additions & 0 deletions src/scales/schemes.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,21 @@ export function quantitativeScheme(scheme) {
if (!quantitativeSchemes.has(s)) throw new Error(`unknown scheme: ${s}`);
return quantitativeSchemes.get(s);
}

const divergingSchemes = new Set([
"brbg",
"prgn",
"piyg",
"puor",
"rdbu",
"rdgy",
"rdylbu",
"rdylgn",
"spectral",
"burd",
"buylrd"
]);

export function isDivergingScheme(scheme) {
return scheme != null && divergingSchemes.has(`${scheme}`.toLowerCase());
}
1 change: 0 additions & 1 deletion test/plots/gistemp-anomaly-moving.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default async function() {
grid: true
},
color: {
type: "diverging",
scheme: "BuRd",
symmetric: false
},
Expand Down
1 change: 0 additions & 1 deletion test/plots/gistemp-anomaly-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export default async function() {
grid: true
},
color: {
type: "diverging",
scheme: "BuRd",
domain: [-1, 1],
transform
Expand Down
3 changes: 1 addition & 2 deletions test/plots/gistemp-anomaly.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export default async function() {
grid: true
},
color: {
type: "diverging",
reverse: true
scheme: "BuRd"
},
marks: [
Plot.ruleY([0]),
Expand Down
1 change: 0 additions & 1 deletion test/plots/hadcrut-warming-stripes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default async function() {
round: true
},
color: {
type: "diverging",
scheme: "BuRd",
symmetric: false
},
Expand Down
3 changes: 0 additions & 3 deletions test/plots/legend-color.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ export function colorLegendImplicitLabel() {
export function colorLegendDiverging() {
return Plot.legend({
color: {
type: "diverging",
domain: [-0.1, 0.1],
scheme: "PiYG",
label: "Daily change"
Expand All @@ -285,7 +284,6 @@ export function colorLegendDiverging() {
export function colorLegendDivergingPivot() {
return Plot.legend({
color: {
type: "diverging",
domain: [1, 4],
pivot: 3,
scheme: "PiYG"
Expand All @@ -296,7 +294,6 @@ export function colorLegendDivergingPivot() {
export function colorLegendDivergingPivotAsymmetric() {
return Plot.legend({
color: {
type: "diverging",
symmetric: false,
domain: [1, 4],
pivot: 3,
Expand Down
3 changes: 1 addition & 2 deletions test/plots/metro-inequality-change.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export default async function() {
label: "↑ Inequality"
},
color: {
type: "diverging",
reverse: true,
scheme: "BuRd",
symmetric: false
},
marks: [
Expand Down
1 change: 0 additions & 1 deletion test/plots/metro-unemployment-slope.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default async function() {
grid: true
},
color: {
type: "diverging",
scheme: "buylrd",
domain: [-0.5, 0.5]
},
Expand Down
10 changes: 1 addition & 9 deletions test/plots/seattle-precipitation-rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,5 @@ import * as d3 from "d3";

export default async function() {
const data = await d3.csv("data/seattle-weather.csv", d3.autoType);
return Plot.plot({
color: {
scheme: "RdBu",
reverse: true
},
marks: [
Plot.ruleX(data, {x: "date", strokeOpacity: "precipitation"})
]
});
return Plot.ruleX(data, {x: "date", strokeOpacity: "precipitation"}).plot();
}
4 changes: 2 additions & 2 deletions test/plots/seattle-temperature-band.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export default async function() {
transform: f => f * 9 / 5 + 32 // convert from Celsius
},
color: {
scheme: "RdBu",
reverse: true
type: "linear",
scheme: "BuRd"
},
marks: [
Plot.ruleX(
Expand Down
1 change: 1 addition & 0 deletions test/plots/simpsons-ratings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default async function() {
label: "Season"
},
color: {
type: "linear",
scheme: "PiYG",
unknown: "#ddd"
},
Expand Down
3 changes: 1 addition & 2 deletions test/plots/us-presidential-election-2020.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ export default async function() {
label: "↑ Total number of votes"
},
color: {
type: "diverging",
reverse: true,
scheme: "BuRd",
symmetric: false
},
marks: [
Expand Down

0 comments on commit 913629f

Please sign in to comment.