Skip to content

Commit

Permalink
Allow temporal scales
Browse files Browse the repository at this point in the history
This implements a feature I've long wanted & it does it the quick and
easy way: by requiring that the value already be numeric! In the future
it'd be nice to allow YYYY-MM-DD values, but that's for another PR (and
comes with its own complexities - what about -XX dates?)

This commit will require a corresponding one in augur to update the
schema
  • Loading branch information
jameshadfield committed Jun 2, 2022
1 parent 2829e47 commit b1e0741
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 30 deletions.
9 changes: 8 additions & 1 deletion src/components/tree/infoPanels/click.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,9 @@ const getTraitsToDisplay = (node) => {
const Trait = ({node, trait, colorings, isTerminal}) => {
let value = getTraitFromNode(node, trait);
const confidence = getTraitFromNode(node, trait, {confidence: true});
const isTemporal = colorings[trait]?.type==="temporal";

if (typeof value === "number") {
if (typeof value === "number" && !isTemporal) {
if (!Number.isInteger(value)) {
value = Number.parseFloat(value).toPrecision(3);
}
Expand All @@ -215,6 +216,12 @@ const Trait = ({node, trait, colorings, isTerminal}) => {
const name = (colorings && colorings[trait] && colorings[trait].title) ?
colorings[trait].title :
trait;

/* case where the colorScale is temporal */
if (isTemporal && typeof value === "number") {
return item(name, numericToCalendar(value));
}

const url = getUrlFromNode(node, trait);
if (url) {
return <Link title={name} url={url} value={value}/>;
Expand Down
7 changes: 6 additions & 1 deletion src/components/tree/infoPanels/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ const ColorBy = ({node, colorBy, colorByConfidence, colorScale, colorings}) => {
const name = (colorings && colorings[colorBy] && colorings[colorBy].title) ?
colorings[colorBy].title :
colorBy;
const value = getTraitFromNode(node, colorBy);

/* case where the colorScale is temporal */
if (colorScale.scaleType==="temporal" && typeof value === "number") {
return <InfoLine name={`${name}:`} value={numericToCalendar(value)}/>;
}

/* helper function to avoid code duplication */
const showCurrentColorByWithoutConfidence = () => {
const value = getTraitFromNode(node, colorBy);
return isValueValid(value) ?
<InfoLine name={`${name}:`} value={value}/> :
null;
Expand Down
2 changes: 1 addition & 1 deletion src/components/tree/legend/legend.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class Legend extends React.Component {
return this.props.colorScale.legendLabels.get(label);
}
/* depending on the colorBy, we display different labels! */
if (this.props.colorBy === "num_date") {
if (this.props.colorBy === "num_date" || this.props.colorScale.scaleType==="temporal") {
const legendValues = this.props.colorScale.visibleLegendValues;
if (
(legendValues[legendValues.length-1] - legendValues[0] > 10) && /* range spans more than 10 years */
Expand Down
57 changes: 30 additions & 27 deletions src/util/colorScale.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export const calcColorScale = (colorBy, controls, tree, treeToo, metadata) => {
({legendValues, colorScale} = createScaleForGenotype(tree.nodes, controls.mutType));
domain = [...legendValues];
} else if (colorings && colorings[colorBy]) {
if (scaleType === "continuous") {
if (scaleType === "continuous" || scaleType==="temporal") {
({continuous, colorScale, legendBounds, legendValues} =
createContinuousScale(colorBy, colorings[colorBy].scale, tree.nodes, treeTooNodes));
createContinuousScale(colorBy, colorings[colorBy].scale, tree.nodes, treeTooNodes, scaleType==="temporal"));
} else if (colorings[colorBy].scale) { /* scale set via JSON */
({continuous, legendValues, colorScale} =
createNonContinuousScaleFromProvidedScaleMap(colorBy, colorings[colorBy].scale, tree.nodes, treeTooNodes));
Expand Down Expand Up @@ -204,17 +204,22 @@ function createOrdinalScale(colorBy, t1nodes, t2nodes) {
return {continuous, colorScale, legendValues, legendBounds};
}

function createContinuousScale(colorBy, providedScale, t1nodes, t2nodes) {
function createContinuousScale(colorBy, providedScale, t1nodes, t2nodes, isTemporal) {
/* Note that a temporal scale is treated very similar to a continuous one... for the time being.
In the future it'd be nice to allow YYYY-MM-DD values, but that's for another PR (and comes
with its own complexities - what about -XX dates?) james june 2022 */
// console.log("making a continuous color scale for ", colorBy);
if (colorBy==="num_date") {
/* before numeric scales were a definable type, num_date was specified as continuous */
isTemporal = true; // eslint-disable-line no-param-reassign
}
let minMax;
switch (colorBy) {
case "lbi":
minMax = [0, 0.7];
break;
case "num_date":
break; /* minMax not needed for num_date */
default:
minMax = getMinMaxFromTree(t1nodes, t2nodes, colorBy);
if (isTemporal) {
// empty - minMax not needed
} else if (colorBy==="lbi") {
minMax = [0, 0.7]; /* TODO: this is for historical reasons, and we should switch to a provided scale */
} else {
minMax = getMinMaxFromTree(t1nodes, t2nodes, colorBy);
}

/* user-defined anchor points across the scale */
Expand All @@ -225,17 +230,17 @@ function createContinuousScale(colorBy, providedScale, t1nodes, t2nodes) {
if (anchorPoints) {
domain = anchorPoints.map((pt) => pt[0]);
range = anchorPoints.map((pt) => pt[1]);
} else if (colorBy==="num_date") {
} else if (isTemporal) {
/* we want the colorScale to "focus" on the tip dates, and be spaced according to sampling */
let rootDate = getTraitFromNode(t1nodes[0], "num_date");
let rootDate = getTraitFromNode(t1nodes[0], colorBy);
let vals = t1nodes.filter((n) => !n.hasChildren)
.map((n) => getTraitFromNode(n, "num_date"));
.map((n) => getTraitFromNode(n, colorBy));
if (t2nodes) {
const treeTooRootDate = getTraitFromNode(t2nodes[0], "num_date");
const treeTooRootDate = getTraitFromNode(t2nodes[0], colorBy);
if (treeTooRootDate < rootDate) rootDate = treeTooRootDate;
vals.concat(
t2nodes.filter((n) => !n.hasChildren)
.map((n) => getTraitFromNode(n, "num_date"))
.map((n) => getTraitFromNode(n, colorBy))
);
}
vals = vals.sort();
Expand All @@ -253,17 +258,15 @@ function createContinuousScale(colorBy, providedScale, t1nodes, t2nodes) {
const scale = scaleLinear().domain(domain).range(range);

let legendValues;
switch (colorBy) {
case "lbi":
legendValues = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7];
break;
case "num_date":
legendValues = domain.slice(1);
break;
default:
const spread = minMax[1] - minMax[0];
const dp = spread > 5 ? 2 : 3;
legendValues = genericDomain.map((d) => parseFloat((minMax[0] + d*spread).toFixed(dp)));
if (isTemporal) {
legendValues = domain.slice(1);
} else if (colorBy==="lbi") {
/* TODO: this is for historical reasons, and we should switch to a provided scale */
legendValues = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7];
} else {
const spread = minMax[1] - minMax[0];
const dp = spread > 5 ? 2 : 3;
legendValues = genericDomain.map((d) => parseFloat((minMax[0] + d*spread).toFixed(dp)));
}
if (legendValues[0] === -0) legendValues[0] = 0; /* hack to avoid bugs */

Expand Down

0 comments on commit b1e0741

Please sign in to comment.