diff --git a/package-lock.json b/package-lock.json
index 9cedd986c..c2a96036b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5400,9 +5400,9 @@
"integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g=="
},
"d3-drag": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz",
- "integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz",
+ "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==",
"requires": {
"d3-dispatch": "1",
"d3-selection": "1"
@@ -5413,6 +5413,16 @@
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
"integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
},
+ "d3-force": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-2.0.1.tgz",
+ "integrity": "sha512-zh73/N6+MElRojiUG7vmn+3vltaKon7iD5vB/7r9nUaBeftXMzRo5IWEG63DLBCto4/8vr9i3m9lwr1OTJNiCg==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-quadtree": "1",
+ "d3-timer": "1"
+ }
+ },
"d3-format": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz",
@@ -5431,6 +5441,11 @@
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz",
"integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA=="
},
+ "d3-quadtree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
+ "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="
+ },
"d3-scale": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz",
diff --git a/package.json b/package.json
index 26f2407cb..8296986b1 100644
--- a/package.json
+++ b/package.json
@@ -41,10 +41,10 @@
"@babel/core": "^7.3.4",
"@babel/plugin-proposal-class-properties": "^7.3.4",
"@babel/plugin-proposal-decorators": "^7.3.0",
- "@babel/plugin-transform-runtime": "^7.8.3",
- "@babel/preset-react": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.9.6",
+ "@babel/preset-react": "^7.0.0",
"@hot-loader/react-dom": "^16.13.0",
"argparse": "^1.0.10",
"awesomplete": "^1.1.2",
@@ -65,7 +65,9 @@
"d3-brush": "^1.0.4",
"d3-collection": "^1.0.4",
"d3-color": "^1.0.3",
+ "d3-drag": "^1.2.5",
"d3-ease": "^1.0.3",
+ "d3-force": "^2.0.1",
"d3-format": "^1.3.0",
"d3-interpolate": "^1.1.5",
"d3-scale": "^1.0.5",
@@ -118,9 +120,9 @@
"styled-components": "^4.0.3",
"typeface-lato": "^0.0.75",
"webpack": "^4.30.0",
+ "webpack-bundle-analyzer": "^3.3.2",
"webpack-chunk-hash": "^0.6.0",
"webpack-cli": "^3.1.2",
- "webpack-bundle-analyzer": "^3.3.2",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.24.3",
"whatwg-fetch": "^0.10.1",
diff --git a/scripts/get-data.sh b/scripts/get-data.sh
index 73dbb84fc..38d757cfa 100755
--- a/scripts/get-data.sh
+++ b/scripts/get-data.sh
@@ -75,4 +75,12 @@ do
curl http://data.nextstrain.org/"${i}" --compressed -o data/"${i}"
done
+staging_files=(
+ "testing_states.json" \
+)
+for i in "${staging_files[@]}"
+do
+ curl http://staging.nextstrain.org/"${i}" --compressed -o data/"${i}"
+done
+
echo "The local data directory ./data now contains up-to-date datasets from http://data.nextstrain.org"
diff --git a/src/actions/recomputeReduxState.js b/src/actions/recomputeReduxState.js
index 70baccd48..92516f3a8 100644
--- a/src/actions/recomputeReduxState.js
+++ b/src/actions/recomputeReduxState.js
@@ -16,7 +16,7 @@ import { computeMatrixFromRawData } from "../util/processFrequencies";
import { applyInViewNodesToTree } from "../actions/tree";
import { isColorByGenotype, decodeColorByGenotype } from "../util/getGenotype";
import { getTraitFromNode, getDivFromNode } from "../util/treeMiscHelpers";
-
+import { getMapTypesAvailable } from "../util/spatialResolutionHelpers";
export const doesColorByHaveConfidence = (controlsState, colorBy) =>
controlsState.coloringsPresentOnTreeWithConfidence.has(colorBy);
@@ -67,6 +67,9 @@ const modifyStateViaURLQuery = (state, query) => {
if (query.r) {
state["geoResolution"] = query.r;
}
+ if (Object.hasOwnProperty.call(query, "showNetwork")) {
+ state.mapDisplayType = "network";
+ }
if (query.p && state.canTogglePanelLayout && (query.p === "full" || query.p === "grid")) {
state["panelLayout"] = query.p;
}
@@ -446,6 +449,9 @@ const checkAndCorrectErrorsInState = (state, metadata, query, tree, viewingNarra
/* geoResolutions */
if (metadata.geoResolutions) {
+ if (isColorByGenotype(state.colorBy)) { // already error corrected above
+ metadata.geoResolutions.push({key: state.colorBy, title: state.colorBy, isGenotype: true});
+ }
const availableGeoResultions = metadata.geoResolutions.map((i) => i.key);
if (availableGeoResultions.indexOf(state["geoResolution"]) === -1) {
/* fallbacks: JSON defined default, then hardocded default, then any available */
@@ -459,6 +465,18 @@ const checkAndCorrectErrorsInState = (state, metadata, query, tree, viewingNarra
console.error("Error detected. Setting geoResolution to ", state.geoResolution);
delete query.r; // no-op if query.r doesn't exist
}
+
+ /* only once the geo-res has been set can we decide on the mapDisplayType and mapDisplayTypesAvailable */
+ const { mapDisplayType, mapDisplayTypesAvailable } = getMapTypesAvailable({
+ currentMapDisplayType: state.mapDisplayType,
+ currentMapDisplayTypesAvailable: state.mapDisplayTypesAvailable,
+ newGeoResolution: state.geoResolution,
+ geoResolutions: metadata.geoResolutions
+ });
+ state.mapDisplayType = mapDisplayType;
+ state.mapDisplayTypesAvailable = mapDisplayTypesAvailable;
+ if (state.mapDisplayTypesAvailable.length === 1 || state.mapDisplayType === "geo") delete query.showNetwork;
+
} else {
console.warn("JSONs did not include `geoResolutions`");
}
diff --git a/src/actions/types.js b/src/actions/types.js
index f33109774..f3d87cf61 100644
--- a/src/actions/types.js
+++ b/src/actions/types.js
@@ -15,6 +15,7 @@ export const CHANGE_DATES_VISIBILITY_THICKNESS = "CHANGE_DATES_VISIBILITY_THICKN
export const CHANGE_ABSOLUTE_DATE_MIN = "CHANGE_ABSOLUTE_DATE_MIN";
export const CHANGE_ABSOLUTE_DATE_MAX = "CHANGE_ABSOLUTE_DATE_MAX";
export const CHANGE_GEO_RESOLUTION = "CHANGE_GEO_RESOLUTION";
+export const CHANGE_MAP_DISPLAY_TYPE = "CHANGE_MAP_DISPLAY_TYPE";
export const CHANGE_LANGUAGE = "CHANGE_LANGUAGE";
export const CLEAN_START = "CLEAN_START";
export const APPLY_FILTER = "APPLY_FILTER";
diff --git a/src/components/controls/controls.js b/src/components/controls/controls.js
index 79a43e662..447d34bc6 100644
--- a/src/components/controls/controls.js
+++ b/src/components/controls/controls.js
@@ -16,6 +16,7 @@ import MapAnimationControls from "./map-animation";
import PanelToggles from "./panel-toggles";
import SearchStrains from "./search";
import ToggleTangle from "./toggle-tangle";
+import ToggleMapDisplayType from "./map-display-type-toggle";
import Language from "./language";
import { SidebarHeader, ControlsContainer } from "./styles";
@@ -45,6 +46,7 @@ function Controls({mapOn, frequenciesOn}) {
{t("sidebar:Map Options")}
+
diff --git a/src/components/controls/geo-resolution.js b/src/components/controls/geo-resolution.js
index dbe6fb855..911750817 100644
--- a/src/components/controls/geo-resolution.js
+++ b/src/components/controls/geo-resolution.js
@@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import Select from "react-select/lib/Select";
import { withTranslation } from "react-i18next";
-
+import { getMapTypesAvailable } from "../../util/spatialResolutionHelpers";
import { controlsWidth } from "../../util/globals";
import { CHANGE_GEO_RESOLUTION } from "../../actions/types";
import { analyticsControlsEvent } from "../../util/googleAnalytics";
@@ -11,7 +11,9 @@ import { SidebarSubtitle } from "./styles";
@connect((state) => {
return {
metadata: state.metadata,
- geoResolution: state.controls.geoResolution
+ geoResolution: state.controls.geoResolution,
+ mapDisplayType: state.controls.mapDisplayType,
+ mapDisplayTypesAvailable: state.controls.mapDisplayTypesAvailable
};
})
class GeoResolution extends React.Component {
@@ -21,9 +23,18 @@ class GeoResolution extends React.Component {
[];
}
- changeGeoResolution(resolution) {
+ changeGeoResolution(geoResolution) {
analyticsControlsEvent("change-geo-resolution");
- this.props.dispatch({ type: CHANGE_GEO_RESOLUTION, data: resolution });
+ this.props.dispatch({
+ type: CHANGE_GEO_RESOLUTION,
+ geoResolution,
+ ...getMapTypesAvailable({
+ currentMapDisplayType: this.props.mapDisplayType,
+ currentMapDisplayTypesAvailable: this.props.mapDisplayTypesAvailable,
+ newGeoResolution: geoResolution,
+ geoResolutions: this.props.metadata.geoResolutions
+ })
+ });
}
render() {
@@ -32,7 +43,7 @@ class GeoResolution extends React.Component {
return (
<>
- {t("sidebar:Geographic resolution")}
+ {t("sidebar:Spatial resolution")}