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

All the zooms #886

Merged
merged 22 commits into from
Feb 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bde3d87
merge master and zoom_button_with_filter
emmahodcroft Jan 30, 2020
8a213ac
detect change in geoRegion via filter too
emmahodcroft Jan 30, 2020
91b1de7
add safety check to only do this in narrative mode
emmahodcroft Jan 30, 2020
52c8de4
[bugfix] don't unnecessarily recompute colours on narrative page changes
jameshadfield Jan 31, 2020
a49e13b
[map] Simplify reset button functionality
jameshadfield Jan 31, 2020
43ee2ed
[map] modify map zoom behavior
jameshadfield Jan 31, 2020
a6e34f5
[map] automatically move map if user has not panned/zoomed
jameshadfield Jan 31, 2020
b0365d1
[map bugfix] ensure `updateOnMoveEnd` runs on map load
jameshadfield Jan 31, 2020
9a34bd3
[map bugfix] clicking reset zoom resets zoom behavior also
jameshadfield Jan 31, 2020
99da19b
remove console logs
jameshadfield Feb 1, 2020
6e9abd3
zoom by custom clade labels
emmahodcroft Jan 29, 2020
96e319b
add pop up messages for invalid URL zoom requests
emmahodcroft Jan 29, 2020
1a31df6
add backwards compat & stop bad URL when no label
emmahodcroft Feb 3, 2020
b1a3f85
use query.label=<key>:<value> throughout code
jameshadfield Feb 4, 2020
125206d
fix bug resulting in URL query `?label=undefined:undefined`
jameshadfield Feb 4, 2020
0f7a397
Allow URL and JSON to define which branch label is displayed
jameshadfield Feb 4, 2020
709f793
clarify code usage re: calculating branch thickness
jameshadfield Feb 4, 2020
b69c708
bugfix: ensure `dispatch` available for `createStateFromQueryOrJSONs`
jameshadfield Feb 4, 2020
bb27a51
Silently remove any `?clade=root` URL query
jameshadfield Feb 4, 2020
943b566
bugfix affecting tree zoom in narratives
jameshadfield Feb 4, 2020
9ef62eb
remove narrative console warnings about grid settings
jameshadfield Feb 4, 2020
8397e40
add `label` URL query to documentation
jameshadfield Feb 5, 2020
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
7 changes: 5 additions & 2 deletions docs-src/docs/advanced-functionality/view-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Each of these can be overridden by the JSON `display_defaults`, and then the vie
* Default phylogeny distance measure is time, if available.
* Default geographic resolution is "country", if available.
* Default colouring is "country", if available.

* Default branch labelling is "clade", if available.

## Dataset (JSON) configurable defaults

Expand All @@ -35,6 +35,7 @@ For instance, if you set `display_defaults.color_by` to `country`, but load the
| `distance_measure` | Phylogeny x-axis measure | "div" or "num_date" |
| `map_triplicate` | Should the map repeat, so that you can pan further in each direction? | Boolean |
| `layout` | Tree layout | "rect", "radial", "clock" or "unrooted |
| `branch_label` | Which set of branch labels are to be displayed | "aa", "lineage" |

Furthermore, a JSON property `meta.panels` lists which panels auspice displays.
If this is not included, then auspice tries to display as many as possible.
Expand Down Expand Up @@ -66,7 +67,9 @@ All URL queries modify the view away from the default settings -- if you change
| `animate` | Animation settings | |
| `n` | Narrative page number | `n=1` goes to the first page |
| `s` | Selected strain | `s=1_0199_PF` |
| `clade` | Labeled clade that tree is zoomed to | `clade=B3` (numeric values are buggy) |
| `branchLabel` | Branch labels to display | `branchLabel=aa` |
| `label` | Labeled branch that tree is zoomed to | `label=clade:B3`, `label=lineage:relapse` |
| `clade` | _DEPRECATED_ Labeled clade that tree is zoomed to | `clade=B3` should now become `label=clade:B3` |


**See this in action:**
Expand Down
5 changes: 3 additions & 2 deletions src/actions/loadData.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ const fetchDataAndDispatch = async (dispatch, url, query, narrativeBlocks) => {
query,
narrativeBlocks,
mainTreeName: secondTreeUrl ? mainDatasetUrl : null,
secondTreeName: secondTreeUrl ? secondTreeUrl : null
secondTreeName: secondTreeUrl ? secondTreeUrl : null,
dispatch
})
});

Expand Down Expand Up @@ -233,7 +234,7 @@ export const loadSecondTree = (secondTreeUrl, firstTreeUrl) => async (dispatch,
return;
}
const oldState = getState();
const newState = createTreeTooState({treeTooJSON: secondJson.tree, oldState, originalTreeUrl: firstTreeUrl, secondTreeUrl: secondTreeUrl});
const newState = createTreeTooState({treeTooJSON: secondJson.tree, oldState, originalTreeUrl: firstTreeUrl, secondTreeUrl: secondTreeUrl, dispatch});
dispatch({type: types.TREE_TOO_DATA, ...newState});
};

Expand Down
4 changes: 2 additions & 2 deletions src/actions/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const changePage = ({
}

/* the path (dataset) remains the same... but the state may be modulated by the query */
const newState = createStateFromQueryOrJSONs({oldState, query});
const newState = createStateFromQueryOrJSONs({oldState, query, dispatch});
dispatch({
type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
...newState,
Expand All @@ -92,7 +92,7 @@ export const goTo404 = (errorMessage) => ({

export const EXPERIMENTAL_showMainDisplayMarkdown = ({query, queryToDisplay}) =>
(dispatch, getState) => {
const newState = createStateFromQueryOrJSONs({oldState: getState(), query});
const newState = createStateFromQueryOrJSONs({oldState: getState(), query, dispatch});
newState.controls.panelsToDisplay = ["EXPERIMENTAL_MainDisplayMarkdown"];
dispatch({
type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
Expand Down
74 changes: 57 additions & 17 deletions src/actions/recomputeReduxState.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ const modifyStateViaURLQuery = (state, query) => {
} else {
state.animationPlayPauseButton = "Play";
}
if (query.branchLabel) {
state.selectedBranchLabel = query.branchLabel;
// do not modify the default (only the JSON can do this)
}
return state;
};

Expand Down Expand Up @@ -164,8 +168,8 @@ const modifyStateViaMetadata = (state, metadata) => {
console.warn("JSON did not include any filters");
}
if (metadata.displayDefaults) {
const keysToCheckFor = ["geoResolution", "colorBy", "distanceMeasure", "layout", "mapTriplicate"];
const expectedTypes = ["string", "string", "string", "string", "boolean"];
const keysToCheckFor = ["geoResolution", "colorBy", "distanceMeasure", "layout", "mapTriplicate", "selectedBranchLabel"];
const expectedTypes = ["string", "string", "string", "string", "boolean", "string"];

for (let i = 0; i < keysToCheckFor.length; i += 1) {
if (metadata.displayDefaults[keysToCheckFor[i]]) {
Expand Down Expand Up @@ -310,7 +314,13 @@ const modifyControlsStateViaTree = (state, tree, treeToo, colorings) => {
state.distanceMeasure = state.branchLengthsToDisplay === "divOnly" ? "div" :
state.branchLengthsToDisplay === "dateOnly" ? "num_date" : state.distanceMeasure;

state.selectedBranchLabel = tree.availableBranchLabels.indexOf("clade") !== -1 ? "clade" : "none";
/* if clade is available as a branch label, then set this as the "default". This
is largely due to historical reasons. Note that it *can* and *will* be overridden
by JSON display_defaults and URL query */
if (tree.availableBranchLabels.indexOf("clade") !== -1) {
state.defaults.selectedBranchLabel = "clade";
state.selectedBranchLabel = "clade";
}

state.temporalConfidence = getTraitFromNode(tree.nodes[0], "num_date", {confidence: true}) ?
{exists: true, display: true, on: false} :
Expand Down Expand Up @@ -395,6 +405,13 @@ const checkAndCorrectErrorsInState = (state, metadata, query, tree) => {
console.warn("JSONs did not include `geoResolutions`");
}

/* show label */
if (state.selectedBranchLabel && !tree.availableBranchLabels.includes(state.selectedBranchLabel)) {
console.error("Can't set selected branch label to ", state.selectedBranchLabel);
state.selectedBranchLabel = "none";
state.defaults.selectedBranchLabel = "none";
}

/* temporalConfidence */
if (state.temporalConfidence.exists) {
if (state.layout !== "rect") {
Expand Down Expand Up @@ -436,7 +453,7 @@ const checkAndCorrectErrorsInState = (state, metadata, query, tree) => {
return state;
};

const modifyTreeStateVisAndBranchThickness = (oldState, tipSelected, cladeSelected, controlsState) => {
const modifyTreeStateVisAndBranchThickness = (oldState, tipSelected, zoomSelected, controlsState, dispatch) => {
/* calculate new branch thicknesses & visibility */
let tipSelectedIdx = 0;
/* check if the query defines a strain to be selected */
Expand All @@ -445,16 +462,21 @@ const modifyTreeStateVisAndBranchThickness = (oldState, tipSelected, cladeSelect
tipSelectedIdx = strainNameToIdx(oldState.nodes, tipSelected);
oldState.selectedStrain = tipSelected;
}
if (cladeSelected) {
const cladeSelectedIdx = cladeSelected === 'root' ? 0 : getIdxMatchingLabel(oldState.nodes, "clade", cladeSelected);
oldState.selectedClade = cladeSelected;
if (zoomSelected) {
// Check and fix old format 'clade=B' - in this case selectionClade is just 'B'
const [labelName, labelValue] = zoomSelected.split(":");
const cladeSelectedIdx = getIdxMatchingLabel(oldState.nodes, labelName, labelValue, dispatch);
oldState.selectedClade = zoomSelected;
newIdxRoot = applyInViewNodesToTree(cladeSelectedIdx, oldState); // tipSelectedIdx, oldState);
} else {
oldState.selectedClade = undefined;
newIdxRoot = applyInViewNodesToTree(0, oldState); // tipSelectedIdx, oldState);
}
const visAndThicknessData = calculateVisiblityAndBranchThickness(
oldState,
controlsState,
{dateMinNumeric: controlsState.dateMinNumeric, dateMaxNumeric: controlsState.dateMaxNumeric},
{tipSelectedIdx, validIdxRoot: newIdxRoot}
{tipSelectedIdx}
);

const newState = Object.assign({}, oldState, visAndThicknessData);
Expand Down Expand Up @@ -546,6 +568,7 @@ const createMetadataStateFromJSON = (json) => {
color_by: "colorBy",
geo_resolution: "geoResolution",
distance_measure: "distanceMeasure",
branch_label: "selectedBranchLabel",
map_triplicate: "mapTriplicate",
layout: "layout"
};
Expand Down Expand Up @@ -574,7 +597,8 @@ export const createStateFromQueryOrJSONs = ({
narrativeBlocks = false,
mainTreeName = false,
secondTreeName = false,
query
query,
dispatch
}) => {
let tree, treeToo, entropy, controls, metadata, narrative, frequencies;
/* first task is to create metadata, entropy, controls & tree partial state */
Expand Down Expand Up @@ -642,7 +666,7 @@ export const createStateFromQueryOrJSONs = ({


/* calculate colours if loading from JSONs or if the query demands change */
if (json || controls.colorBy !== oldState.colorBy) {
if (json || controls.colorBy !== oldState.controls.colorBy) {
const colorScale = calcColorScale(controls.colorBy, controls, tree, treeToo, metadata);
const nodeColors = calcNodeColor(tree, colorScale);
controls.colorScale = colorScale;
Expand All @@ -651,16 +675,31 @@ export const createStateFromQueryOrJSONs = ({
tree.nodeColors = nodeColors;
}

/* parse the query.label / query.clade */
if (query.clade) {
tree = modifyTreeStateVisAndBranchThickness(tree, undefined, query.clade, controls);
} else { /* if not specifically given in URL, zoom to root */
tree = modifyTreeStateVisAndBranchThickness(tree, undefined, undefined, controls);
if (!query.label && query.clade !== "root") {
query.label = `clade:${query.clade}`;
}
delete query.clade;
}
tree = modifyTreeStateVisAndBranchThickness(tree, query.s, undefined, controls);
if (query.label) {
if (!query.label.includes(":")) {
console.error("Defined a label without ':' separator.");
delete query.label;
}
if (!tree.availableBranchLabels.includes(query.label.split(":")[0])) {
console.error(`Label name ${query.label.split(":")[0]} doesn't exist`);
delete query.label;
}
}

/* if query.label is undefined then we intend to zoom to the root */
tree = modifyTreeStateVisAndBranchThickness(tree, query.s, query.label, controls, dispatch);

if (treeToo && treeToo.loaded) {
treeToo.nodeColorsVersion = tree.nodeColorsVersion;
treeToo.nodeColors = calcNodeColor(treeToo, controls.colorScale);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, query.s, undefined, controls);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, query.s, undefined, controls, dispatch);
controls = modifyControlsViaTreeToo(controls, treeToo.name);
treeToo.tangleTipLookup = constructVisibleTipLookupBetweenTrees(tree.nodes, treeToo.nodes, tree.visibility, treeToo.visibility);
}
Expand Down Expand Up @@ -695,7 +734,8 @@ export const createTreeTooState = ({
treeTooJSON, /* raw json data */
oldState,
originalTreeUrl,
secondTreeUrl /* treeToo URL */
secondTreeUrl, /* treeToo URL */
dispatch
}) => {
/* TODO: reconsile choices (filters, colorBys etc) with this new tree */
/* TODO: reconcile query with visibility etc */
Expand All @@ -707,7 +747,7 @@ export const createTreeTooState = ({
treeToo.debug = "RIGHT";
controls = modifyControlsStateViaTree(controls, tree, treeToo, oldState.metadata.colorings);
controls = modifyControlsViaTreeToo(controls, secondTreeUrl);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, tree.selectedStrain, undefined, controls);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, tree.selectedStrain, undefined, controls, dispatch);

/* calculate colours if loading from JSONs or if the query demands change */
const colorScale = calcColorScale(controls.colorBy, controls, tree, treeToo, oldState.metadata);
Expand Down
6 changes: 3 additions & 3 deletions src/actions/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const applyInViewNodesToTree = (idx, tree) => {
} else {
applyToChildren(tree.nodes[validIdxRoot].shell, (d) => {d.inView = true;});
}
} else if (idx !== tree.idxOfInViewRootNode) { /* if shell isn't set yet - have set clade in URL */
} else {
tree.nodes.forEach((d) => {
d.inView = false;
});
Expand Down Expand Up @@ -92,7 +92,7 @@ export const updateVisibleTipsAndBranchThicknesses = (
tree,
controls,
{dateMinNumeric: controls.dateMinNumeric, dateMaxNumeric: controls.dateMaxNumeric},
{tipSelectedIdx: tipIdx1, validIdxRoot: rootIdxTree1}
{tipSelectedIdx: tipIdx1}
);
const dispatchObj = {
type: types.UPDATE_VISIBILITY_AND_BRANCH_THICKNESS,
Expand All @@ -113,7 +113,7 @@ export const updateVisibleTipsAndBranchThicknesses = (
treeToo,
controls,
{dateMinNumeric: controls.dateMinNumeric, dateMaxNumeric: controls.dateMaxNumeric},
{tipSelectedIdx: tipIdx2, validIdxRoot: rootIdxTree2}
{tipSelectedIdx: tipIdx2}
);
dispatchObj.tangleTipLookup = constructVisibleTipLookupBetweenTrees(tree.nodes, treeToo.nodes, data.visibility, dataToo.visibility);
dispatchObj.visibilityToo = dataToo.visibility;
Expand Down
2 changes: 0 additions & 2 deletions src/components/main/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ export const calcPanelDims = (grid, panels, narrativeIsDisplayed, availableWidth
}
/* widths */
if (panels.includes("map") && panels.includes("tree") && !grid) {
console.warn("narrative mode specified full display but we have both map & tree");
bigWidthFraction = 0.5;
}
if (grid && (!panels.includes("map") || !panels.includes("tree"))) {
console.warn("narrative mode specified grid display but we are not showing both map & tree");
bigWidthFraction = 1;
}
}
Expand Down
Loading