diff --git a/src/actions/recomputeReduxState.js b/src/actions/recomputeReduxState.js index cdda54227..7accd1dd1 100644 --- a/src/actions/recomputeReduxState.js +++ b/src/actions/recomputeReduxState.js @@ -10,6 +10,7 @@ import { getDefaultFrequenciesState } from "../reducers/frequencies"; import { countTraitsAcrossTree, calcTotalTipsInTree } from "../util/treeCountingHelpers"; import { calcEntropyInView } from "../util/entropy"; import { treeJsonToState } from "../util/treeJsonProcessing"; +import { castIncorrectTypes } from "../util/castJsonTypes"; import { entropyCreateState } from "../util/entropyCreateStateFromJsons"; import { determineColorByGenotypeMutType, calcNodeColor } from "../util/colorHelpers"; import { calcColorScale, createVisibleLegendValues } from "../util/colorScale"; @@ -768,11 +769,13 @@ export const createStateFromQueryOrJSONs = ({ frequencies = getDefaultFrequenciesState(); /* new tree state(s) */ tree = treeJsonToState(json.tree); + castIncorrectTypes(metadata, tree); tree.debug = "LEFT"; tree.name = mainTreeName; metadata.mainTreeNumTips = calcTotalTipsInTree(tree.nodes); if (secondTreeDataset) { treeToo = treeJsonToState(secondTreeDataset.tree); + castIncorrectTypes(metadata, treeToo); treeToo.debug = "RIGHT"; treeToo.name = secondTreeName; /* TODO: calc & display num tips in 2nd tree */ diff --git a/src/util/castJsonTypes.js b/src/util/castJsonTypes.js new file mode 100644 index 000000000..a792bed5e --- /dev/null +++ b/src/util/castJsonTypes.js @@ -0,0 +1,37 @@ +/** + * Values in the JSON should be appropriately typed however this may not always be the case. + * For instance, certain values of a continuous trait may be strings, and we should cast + * these to numbers. See https://github.com/nextstrain/auspice/issues/1626 for more discussion + * of this particular case. Feel free to add more checks / casts to this function! + * @param {Object} metadata + * @param {Object} tree + * @returns {undefined} Any type casting is in-place + */ +export const castIncorrectTypes = (metadata, tree) => { + try { + const continuousKeys = new Set(); + Object.entries(metadata.colorings || {}).forEach(([key, details]) => { + if (details.type==="continuous") { + continuousKeys.add(key); + } + }); + tree.nodes.forEach((node) => { + Object.entries(node.node_attrs || {}).forEach(([key, details]) => { + if (continuousKeys.has(key)) { + if ((typeof details.value) !== "number") { + if (details.value==="" || isNaN(details.value)) { // Note: Number("")=0 + // undefined values are handled appropriately (e.g. scatterplots, tooltips etc) + details.value = undefined; + } else { + details.value = Number(details.value); + } + } + } + }); + }); + } catch (err) { + // type casting shouldn't be required (the JSON should be correctly typed) + // so any errors shouldn't prevent Auspice loading + console.error(err); + } +};