Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color tree by measurements #1924

Merged
merged 35 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d90c503
MeasurementsControlState: Remove unnecessary trailing commas
joverlee521 Dec 18, 2024
6c2e53b
measurements: Fix bug for numeric legend values in ordering
joverlee521 Dec 12, 2024
15dabdf
Add color tree by measurements feature
joverlee521 Dec 10, 2024
14a5650
ADD_EXTRA_METADATA: only use non-continuous colorings as filter traits
joverlee521 Dec 19, 2024
1391b9b
applyMeasurementsColorBy: Use multiple anchors for color scale
joverlee521 Dec 11, 2024
acf11a4
measurements: Refactor getting matching filters into separate function
joverlee521 Dec 12, 2024
a4b4d9c
applyMeasurementsColorBy: Only include measurements that pass filters
joverlee521 Dec 13, 2024
69853e2
measurements: Save color grouping value as a separate state
joverlee521 Dec 18, 2024
8b2773e
applyMeasurementsColorBy: remove old measurements coloring data
joverlee521 Dec 20, 2024
74eac4a
applyMeasurementsColorBy: batch dispatch actions
joverlee521 Dec 20, 2024
c0b7e91
Refactor `applyMeasurementsColorBy` into small functions
joverlee521 Dec 19, 2024
e1c74a4
Update measurements coloring when changing collection
joverlee521 Dec 24, 2024
9a5a183
Update measurements coloring when changing group by
joverlee521 Dec 24, 2024
3c35ed5
Update measurement coloring when updating measurements filters
joverlee521 Dec 21, 2024
c2db2bb
Update type `ColoringInfo`
joverlee521 Dec 24, 2024
17d411c
Refactor `addMeasurementsColorData` into smaller function
joverlee521 Dec 24, 2024
ade2c20
Support measurements coloring URL param `c=m-<grouping_value>`
joverlee521 Dec 24, 2024
440cddd
Add crosshair in measurements panel y-axis for coloring
joverlee521 Dec 30, 2024
ff28517
measurements: Allow click on grouping when current colorby is different
joverlee521 Dec 31, 2024
795efc7
Remove extra whitespace
joverlee521 Jan 2, 2025
9a4c328
tree: Add crosshair on tip matching measurements color grouping
joverlee521 Jan 2, 2025
5d51e9c
tree: re-draw measurements crosshair when modifying SVG
joverlee521 Jan 3, 2025
0dbc4e3
tree: update measurements crosshair when changing colorBy
joverlee521 Jan 3, 2025
fb3eba0
narratives: Ensure tree coloring is calculated for measurements colors
joverlee521 Jan 3, 2025
a2cfe54
narratives: Ensure old measurements coloring data is removed
joverlee521 Jan 3, 2025
bb44c2a
modifySVGInStages: remove crosshair in step 1
joverlee521 Jan 6, 2025
82e6f6d
Add comments for measurementColoringPrefix
joverlee521 Jan 7, 2025
9f6b032
Add tooltip to hint at color by measurements feature
joverlee521 Jan 7, 2025
27cdbb0
Click on measurements grouping to toggle back to previous coloring
joverlee521 Jan 8, 2025
e52f48d
Update type ColoringInfo: make `scale` optional
joverlee521 Jan 9, 2025
972ed91
Add `_hasMeasurementColor` attr and coloring for measurements coloring
joverlee521 Jan 9, 2025
e586a6c
Filter to colored tips when applying measurements coloring
joverlee521 Jan 9, 2025
921161c
measurements: Use `String` instead of `toString`
joverlee521 Jan 21, 2025
aead1cf
removeNodeAttrs: drop unnecessary conditional
joverlee521 Jan 21, 2025
daccd78
Update changelog
joverlee521 Jan 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
325 changes: 288 additions & 37 deletions src/actions/measurements.ts

Large diffs are not rendered by default.

62 changes: 58 additions & 4 deletions src/actions/recomputeReduxState.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import { computeMatrixFromRawData, checkIfNormalizableFromRawData } from "../uti
import { applyInViewNodesToTree } from "../actions/tree";
import { validateScatterVariables } from "../util/scatterplotHelpers";
import { isColorByGenotype, decodeColorByGenotype, encodeColorByGenotype, decodeGenotypeFilters, encodeGenotypeFilters, getCdsFromGenotype } from "../util/getGenotype";
import { getTraitFromNode, getDivFromNode, collectGenotypeStates } from "../util/treeMiscHelpers";
import { getTraitFromNode, getDivFromNode, collectGenotypeStates, addNodeAttrs, removeNodeAttrs } from "../util/treeMiscHelpers";
import { collectAvailableTipLabelOptions } from "../components/controls/choose-tip-label";
import { hasMultipleGridPanels } from "./panelDisplay";
import { strainSymbolUrlString } from "../middleware/changeURL";
import { combineMeasurementsControlsAndQuery, loadMeasurements } from "./measurements";
import { combineMeasurementsControlsAndQuery, encodeMeasurementColorBy, loadMeasurements } from "./measurements";

export const doesColorByHaveConfidence = (controlsState, colorBy) =>
controlsState.coloringsPresentOnTreeWithConfidence.has(colorBy);
Expand Down Expand Up @@ -916,11 +916,65 @@ export const createStateFromQueryOrJSONs = ({
controls = modifyStateViaURLQuery(controls, query);

/* Special handling of measurements controls and query params */
let newMeasurementsColoringData = false;
if (measurements.loaded) {
const { collectionToDisplay, collectionControls, updatedQuery} = combineMeasurementsControlsAndQuery(measurements, query);
const {
collectionToDisplay,
collectionControls,
updatedQuery,
newColoringData,
} = combineMeasurementsControlsAndQuery(measurements, query);
measurements.collectionToDisplay = collectionToDisplay;
controls = {...controls, ...collectionControls};
query = updatedQuery;

/**
* Similar to state changes applied for `REMOVE_METADATA`
* Remove the old measurements coloring data before adding the new data,
* which is necessary for changing measurements coloring in narratives
*/
if (oldState?.controls?.measurementsColorGrouping !== undefined) {
const colorByToRemove = encodeMeasurementColorBy(oldState.controls.measurementsColorGrouping);
// Update controls
controls.coloringsPresentOnTree.delete(colorByToRemove);
// Update metadata
if (colorByToRemove in metadata.colorings) {
delete metadata.colorings[colorByToRemove];
}
// Update tree
removeNodeAttrs(tree.nodes, [colorByToRemove]);
tree.nodeAttrKeys.delete(colorByToRemove);
if (colorByToRemove in tree.totalStateCounts) {
delete tree.totalStateCounts[colorByToRemove];
}
// Update treeToo if exists
if (treeToo && treeToo.loaded) {
removeNodeAttrs(treeToo.nodes, [colorByToRemove]);
}
}

// Similar to the state changes applied for `ADD_EXTRA_METADATA`
if (newColoringData !== undefined) {
newMeasurementsColoringData = true;
// Update controls
newColoringData.coloringsPresentOnTree.forEach((coloring) => controls.coloringsPresentOnTree.add(coloring));
// Update metadata
metadata.colorings = {...metadata.colorings, ...newColoringData.colorings};
// Update tree
addNodeAttrs(tree.nodes, newColoringData.nodeAttrs);
Object.keys(newColoringData.colorings).forEach((attr) => tree.nodeAttrKeys.add(attr));
const nonContinuousColorings = Object.keys(newColoringData.colorings).filter((coloring) => {
return newColoringData.colorings[coloring].type !== "continuous"
});
tree.totalStateCounts = {
...tree.totalStateCounts,
...countTraitsAcrossTree(tree.nodes, nonContinuousColorings, false, true)
};
// Update treeToo if exists
if (treeToo && treeToo.loaded) {
addNodeAttrs(treeToo.nodes, newColoringData.nodeAttrs);
}
Comment on lines +973 to +976
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have two trees + measurements? I've come to the opinion that we should drop a lot of functionality when viewing two trees, so that if the second tree is displayed we don't allow measurements, frequencies, map to be displayed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two trees + measurements is definitely possible right now, where the measurements panel always uses the measurements JSON from the main tree.

I'm not entirely sure how useful it is though...could it be useful for HA:NA + measurements?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure how useful it is though...could it be useful for HA:NA + measurements?

I can't answer that, but we should find the answer. Two trees complicates Auspice quite a lot, and I think it would be a worthwhile push to limit the cases in order to simplify the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can revisit simplifying code in the future, starting conversation in Slack.

}
} else {
// Hide measurements panel if loading failed
controls.panelsToDisplay = controls.panelsToDisplay.filter((panel) => panel !== "measurements");
Expand All @@ -947,7 +1001,7 @@ export const createStateFromQueryOrJSONs = ({


/* calculate colours if loading from JSONs or if the query demands change */
if (json || controls.colorBy !== oldState.controls.colorBy) {
if (json || controls.colorBy !== oldState.controls.colorBy || newMeasurementsColoringData) {
const colorScale = calcColorScale(controls.colorBy, controls, tree, treeToo, metadata);
const nodeColors = calcNodeColor(tree, colorScale);
controls.colorScale = colorScale;
Expand Down
2 changes: 2 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const CHANGE_PANEL_LAYOUT = "CHANGE_PANEL_LAYOUT";
export const TOGGLE_PANEL_DISPLAY = "TOGGLE_PANEL_DISPLAY";
export const SET_MODAL = "SET_MODAL";
export const ADD_EXTRA_METADATA = "ADD_EXTRA_METADATA";
export const REMOVE_METADATA = "REMOVE_METADATA";
export const CHANGE_TREE_ROOT_IDX = "CHANGE_TREE_ROOT_IDX";
export const TOGGLE_NARRATIVE = "TOGGLE_NARRATIVE";
export const ENTROPY_DATA = "ENTROPY_DATA";
Expand All @@ -57,6 +58,7 @@ export const CHANGE_MEASUREMENTS_GROUP_BY = "CHANGE_MEASUREMENTS_GROUP_BY";
export const TOGGLE_MEASUREMENTS_THRESHOLD = "TOGGLE_MEASUREMENTS_THRESHOLD";
export const TOGGLE_MEASUREMENTS_OVERALL_MEAN = "TOGGLE_MEASUREMENTS_OVERALL_MEAN";
export const CHANGE_MEASUREMENTS_DISPLAY = "CHANGE_MEASUREMENTS_DISPLAY";
export const CHANGE_MEASUREMENTS_COLOR_GROUPING = "CHANGE_MEASUREMENTS_COLOR_GROUPING";
export const APPLY_MEASUREMENTS_FILTER = "APPLY_MEASUREMENTS_FILTER";
export const TOGGLE_SHOW_ALL_BRANCH_LABELS = "TOGGLE_SHOW_ALL_BRANCH_LABELS";
export const TOGGLE_MOBILE_DISPLAY = "TOGGLE_MOBILE_DISPLAY";
Expand Down
53 changes: 32 additions & 21 deletions src/components/measurements/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { CSSProperties, MutableRefObject, useCallback, useRef, useEffect, useMemo, useState } from "react";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { isEqual, orderBy } from "lodash";
import { NODE_VISIBLE } from "../../util/globals";
import { getColorByTitle, getTipColorAttribute } from "../../util/colorHelpers";
Expand All @@ -23,12 +23,20 @@ import {
addHoverPanelToMeasurementsAndMeans,
addColorByAttrToGroupingLabel,
layout,
jitterRawMeansByColorBy
jitterRawMeansByColorBy,
addGroupingValueCrosshair,
removeColorGroupingCrosshair,
} from "./measurementsD3";
import { RootState } from "../../store";
import { MeasurementFilters } from "../../reducers/controls";
import { Visibility } from "../../reducers/tree/types";
import { Measurement, isMeasurement } from "../../reducers/measurements/types";
import {
applyMeasurementsColorBy,
isMeasurementColorBy,
getActiveMeasurementFilters,
matchesAllActiveFilters
} from "../../actions/measurements";

interface MeanAndStandardDeviation {
mean: number
Expand Down Expand Up @@ -131,39 +139,28 @@ const filterMeasurements = (
filteredMeasurements: Measurement[]
} => {
// Find active filters to filter measurements
const activeFilters: {string?: string[]} = {};
Object.entries(filters).forEach(([field, valuesMap]) => {
activeFilters[field] = activeFilters[field] || [];
valuesMap.forEach(({active}, fieldValue) => {
// Save array of active values for the field filter
if (active) activeFilters[field].push(fieldValue);
});
});
const activeFilters = getActiveMeasurementFilters(filters);

return {
activeFilters,
filteredMeasurements: measurements.filter((measurement) => {
// First check the strain is visible in the tree
if (!isVisible(treeStrainVisibility[measurement.strain])) return false;
// Then check that the measurement contains values for all active filters
for (const [field, values] of Object.entries(activeFilters)) {
const measurementValue = measurement[field];
if (values.length > 0 &&
((typeof measurementValue === "string") && !values.includes(measurementValue))){
return false;
}
}
return true;
return matchesAllActiveFilters(measurement, activeFilters);
})
};
};

const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => {
const dispatch = useDispatch();
// Use `lodash.isEqual` to deep compare object states to prevent unnecessary re-renderings of the component
const { treeStrainVisibility, treeStrainColors } = useSelector((state: RootState) => treeStrainPropertySelector(state), isEqual);
const legendValues = useSelector((state: RootState) => state.controls.colorScale.legendValues, isEqual);
// Convert legendValues to string to ensure that subsequent attribute matches work as intended
const legendValues = useSelector((state: RootState) => state.controls.colorScale.legendValues.map((v): string => v.toString()), isEqual);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor points - don't change them if you don't agree with them:

Since our attributes are all strings due to the use of Object.keys(),
this did not match the legend values when they were numeric and resulted
in the ordering of mean and raw values to be arbitrary. This has
been a bug all along, but I didn't notice it until I was working on the
coloring by measurements feature that focused on continuous numeric
legend values.

This commit converts all legend values to strings in the Redux
selector so that ordering works across all color scales.

  • toString will raise an exception on certain values (null, undefined etc) whereas String will not, and the latter can be succinctly written as .map(String). The legendValues are any[] so these values may occur? I'm surprised tsc didn't complain.
  • Not being super familiar with measurements terminology, I didn't understand "attributes" in the commit message and had to dive into the code. I think it's node attributes / node values, right? Where is the Object.keys you mention - I followed the code a little bit but didn't find it.

Copy link
Contributor Author

@joverlee521 joverlee521 Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point about toString raising exceptions, I'll update to String. (Edit: updated in 921161c.)

Not being super familiar with measurements terminology, I didn't understand "attributes" in the commit message and had to dive into the code. I think it's node attributes / node values, right?

These are referring to the coloring attributes, so yes these correspond to the node attributes / node values.

Where is the Object.keys you mention - I followed the code a little bit but didn't find it.

These are referring to where the node attributes get compared to the legend values for ordering. These are in measurementsD3.js, within jitterRawMeansByColorBy and drawMeansForColorBy.

const colorings = useSelector((state: RootState) => state.metadata.colorings);
const colorBy = useSelector((state: RootState) => state.controls.colorBy);
const colorGrouping = useSelector((state: RootState) => state.controls.measurementsColorGrouping);
const groupBy = useSelector((state: RootState) => state.controls.measurementsGroupBy);
const filters = useSelector((state: RootState) => state.controls.measurementsFilters);
const display = useSelector((state: RootState) => state.controls.measurementsDisplay);
Expand Down Expand Up @@ -257,6 +254,12 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => {
setHoverData(newHoverData);
}, [fields, colorings, colorBy]);

const handleClickOnGrouping = useCallback((grouping: string): void => {
if (grouping !== colorGrouping || !isMeasurementColorBy(colorBy)) {
dispatch(applyMeasurementsColorBy(grouping));
}
}, [dispatch, colorGrouping, colorBy]);

useEffect(() => {
setPanelTitle(`${title || "Measurements"} (grouped by ${fields.get(groupBy).title})`);
}, [setPanelTitle, title, fields, groupBy]);
Expand All @@ -267,8 +270,8 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => {
// the scroll position on whitespace.
svgContainerRef.current.scrollTop = 0;
clearMeasurementsSVG(d3Ref.current, d3XAxisRef.current);
drawMeasurementsSVG(d3Ref.current, d3XAxisRef.current, svgData);
}, [svgData]);
drawMeasurementsSVG(d3Ref.current, d3XAxisRef.current, svgData, handleClickOnGrouping);
}, [svgData, handleClickOnGrouping]);

// Color the SVG & redraw color-by means when SVG is re-drawn or when colors have changed
useEffect(() => {
Expand All @@ -292,6 +295,14 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => {
toggleDisplay(d3Ref.current, "threshold", showThreshold);
}, [svgData, showThreshold]);

useEffect(() => {
if(isMeasurementColorBy(colorBy)) {
addGroupingValueCrosshair(d3Ref.current, colorGrouping);
} else {
removeColorGroupingCrosshair(d3Ref.current);
}
}, [svgData, colorBy, colorGrouping])

const getSVGContainerStyle = (): CSSProperties => {
return {
overflowY: "auto",
Expand Down
45 changes: 42 additions & 3 deletions src/components/measurements/measurementsD3.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ layout['overallMeanYValue'] = layout.subplotHeight / 2;
const classes = {
xAxis: "measurementXAxis",
yAxis: "measurementYAxis",
groupingValue: "measurementGroupingValue",
yAxisColorByLabel: "measurementYAxisColorByLabel",
threshold: "measurementThreshold",
subplot: "measurementSubplot",
Expand Down Expand Up @@ -189,7 +190,7 @@ const drawStickyXAxis = (ref, containerHeight, svgHeight, xScale, x_axis_label)
.text(x_axis_label);
};

export const drawMeasurementsSVG = (ref, xAxisRef, svgData) => {
export const drawMeasurementsSVG = (ref, xAxisRef, svgData, handleClickOnGrouping) => {
const {containerHeight, xScale, yScale, x_axis_label, thresholds, groupingOrderedValues, groupedMeasurements} = svgData;

// Do not draw SVG if there are no measurements
Expand Down Expand Up @@ -251,6 +252,7 @@ export const drawMeasurementsSVG = (ref, xAxisRef, svgData) => {
subplot.append("g")
.attr("class", classes.yAxis)
.attr("transform", `translate(${layout.leftPadding}, 0)`)
.attr("cursor", "pointer")
.call(
axisLeft(yScale)
.tickValues([yScale((layout.yMax - layout.yMin) / 2)])
Expand All @@ -264,6 +266,7 @@ export const drawMeasurementsSVG = (ref, xAxisRef, svgData) => {
// but there're always limits of the available space so punting that for now.
// -Jover, 20 September 2022
g.selectAll('text')
.attr("class", classes.groupingValue)
.attr("transform", (_, i, element) => {
const textWidth = select(element[i]).node().getBoundingClientRect().width;
// Subtract the twice the y-axis tick size to give some padding around the text
Expand All @@ -273,7 +276,8 @@ export const drawMeasurementsSVG = (ref, xAxisRef, svgData) => {
}
return null;
});
});
})
.on("click", () => handleClickOnGrouping(groupingValue));

// Add circles for each measurement
// Note, "cy" is added later when jittering within color-by groups
Expand Down Expand Up @@ -466,7 +470,7 @@ export const addColorByAttrToGroupingLabel = (ref, treeStrainColors) => {
svg.selectAll(`.${classes.yAxis}`).select(".tick")
.each((_, i, elements) => {
const groupingLabel = select(elements[i]);
const groupingValue = groupingLabel.text();
const groupingValue = groupingLabel.select(`.${classes.groupingValue}`).text();
const groupingValueColorBy = treeStrainColors[groupingValue];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that value reflects the color bin that the other reference viruses map to based on their measurements against the selected reference and not some summary statistic of all measurements for the other references. Is that right?

Yes, this is reflecting the color of the strain in the tree

if (groupingValueColorBy) {
// Get the current label width to add colored line and text relative to the width
Expand All @@ -490,3 +494,38 @@ export const addColorByAttrToGroupingLabel = (ref, treeStrainColors) => {
}
});
};

const colorGroupingCrosshairId = "measurementsColorGroupingCrosshair";
export const removeColorGroupingCrosshair = (ref) => {
const svg = select(ref);
svg.select(`#${colorGroupingCrosshairId}`).remove();
};

export const addGroupingValueCrosshair = (ref, groupingValue) => {
// Remove previous color grouping crosshair
removeColorGroupingCrosshair(ref);

const svg = select(ref);
svg.selectAll(`.${classes.yAxis}`).select(".tick")
.each((_, i, elements) => {
const groupingLabel = select(elements[i]);
const currentGroupingValue = groupingLabel.select(`.${classes.groupingValue}`).text()
if (groupingValue === currentGroupingValue){
const {width} = groupingLabel.node().getBoundingClientRect();
groupingLabel.append("svg")
.attr("id", colorGroupingCrosshairId)
.attr("stroke", "currentColor")
.attr("fill", "currentColor")
.attr("strokeWidth", "0")
.attr("viewBox", "0 0 256 256")
.attr("height", layout.yAxisColorByLineHeight * 2)
.attr("width", layout.yAxisColorByLineHeight * 2)
.attr("x", -width - (layout.yAxisColorByLineHeight * 2))
.attr("y", -layout.yAxisColorByLineHeight)
.append("path")
// path copied from react-icons/pi/PiCrosshairSimpleBold
.attr("d", "M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm12,191.13V184a12,12,0,0,0-24,0v27.13A84.18,84.18,0,0,1,44.87,140H72a12,12,0,0,0,0-24H44.87A84.18,84.18,0,0,1,116,44.87V72a12,12,0,0,0,24,0V44.87A84.18,84.18,0,0,1,211.13,116H184a12,12,0,0,0,0,24h27.13A84.18,84.18,0,0,1,140,211.13Z" )

}
});
}
12 changes: 12 additions & 0 deletions src/components/tree/phyloTree/change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ export const modifySVG = function modifySVG(
} else if (elemsToUpdate.has('.branchLabel')) {
this.updateBranchLabels(transitionTime);
}

if (this.measurementsColorGrouping) {
this.drawMeasurementsColoringCrosshair();
} else {
this.removeMeasurementsColoringCrosshair();
}
};

/* instead of modifying the SVG the "normal" way, this is sometimes too janky (e.g. when we need to move everything)
Expand All @@ -242,6 +248,9 @@ export const modifySVGInStages = function modifySVGInStages(
this.updateTipLabels();
this.drawTips();
if (this.vaccines) this.drawVaccines();
if (this.measurementsColorGrouping) {
this.drawMeasurementsColoringCrosshair();
}
this.showTemporalSlice();
if (this.regression) this.drawRegression();
if (elemsToUpdate.has(".branchLabel")) this.drawBranchLabels(extras.newBranchLabellingKey || this.params.branchLabelKey);
Expand All @@ -266,6 +275,7 @@ export const modifySVGInStages = function modifySVGInStages(
.on("start", () => inProgress++)
.on("end", step2);
this.hideTemporalSlice();
this.removeMeasurementsColoringCrosshair();
if (!transitionTimeFadeOut) timerFlush();
};

Expand Down Expand Up @@ -312,6 +322,7 @@ export const change = function change(
branchThickness = undefined,
scatterVariables = undefined,
performanceFlags = undefined,
newMeasurementsColorGrouping = undefined,
}: ChangeParams
): void {
// console.log("\n** phylotree.change() (time since last run:", Date.now() - this.timeLastRenderRequested, "ms) **\n\n");
Expand Down Expand Up @@ -376,6 +387,7 @@ export const change = function change(
// recalculate gradients here?
if (changeColorBy) {
this.updateColorBy();
this.measurementsColorGrouping = newMeasurementsColorGrouping;
}
// recalculate existing regression if needed
if (changeVisibility && this.regression) {
Expand Down
2 changes: 2 additions & 0 deletions src/components/tree/phyloTree/phyloTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ PhyloTree.prototype.setClipMask = renderers.setClipMask;
PhyloTree.prototype.drawTips = renderers.drawTips;
PhyloTree.prototype.drawBranches = renderers.drawBranches;
PhyloTree.prototype.drawVaccines = renderers.drawVaccines;
PhyloTree.prototype.drawMeasurementsColoringCrosshair = renderers.drawMeasurementsColoringCrosshair;
PhyloTree.prototype.removeMeasurementsColoringCrosshair = renderers.removeMeasurementsColoringCrosshair;
PhyloTree.prototype.drawRegression = renderers.drawRegression;
PhyloTree.prototype.removeRegression = renderers.removeRegression;
PhyloTree.prototype.updateColorBy = renderers.updateColorBy;
Expand Down
Loading
Loading