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")}