diff --git a/assets/karma.conf.js b/assets/karma.conf.js index 427903b22cdb50c155c7024e29a670c7e9f35f27..7e9f7709a3f8c54faac7cc1adc0148d4964b8932 100644 --- a/assets/karma.conf.js +++ b/assets/karma.conf.js @@ -36,7 +36,7 @@ module.exports = function (config) { // list of files / patterns to exclude exclude: [ - 'src/scripts/index.js' + 'src/scripts/site-store.js' ], // preprocess matching files before serving them to the browser diff --git a/assets/package.json b/assets/package.json index efb2d886709f79f39081f529172b03737745a3e9..51d66f4cba0667be0e8ace897028142563a4a633 100644 --- a/assets/package.json +++ b/assets/package.json @@ -13,6 +13,7 @@ "build:css": "run-p build:css:*", "build:css:main": "sass --load-path node_modules/leaflet/dist --load-path node_modules/wdfn-viz/src/stylesheets --load-path node_modules/uswds/src/stylesheets src/styles/main.scss dist/main.css && postcss dist/main.css --map -o dist/main.css", "build:css:graph": "sass --load-path node_modules/wdfn-viz/src/stylesheets --load-path node_modules/uswds/src/stylesheets src/styles/graph.scss dist/graph.css && postcss dist/graph.css --map -o dist/graph.css", + "build:css:network": "sass --load-path node_modules/leaflet/dist --load-path node_modules/wdfn-viz/src/stylesheets --load-path node_modules/uswds/src/stylesheets src/styles/network.scss dist/network.css && postcss dist/network.css --map -o dist/network.css", "build:fonts": "mkdir -p dist/fonts && cp -r node_modules/uswds/src/fonts/* dist/fonts && cp node_modules/@fortawesome/fontawesome-free/webfonts/* dist/fonts", "build:images": "mkdir -p dist/img && mkdir -p dist/images && cp -r node_modules/uswds/src/img/* dist/img && cp -r node_modules/wdfn-viz/src/img/* dist/img && cp -r node_modules/leaflet/dist/images/* dist/images && cp -r src/img/* dist/img", "build:js": "rollup -c --environment NODE_ENV:production && mkdir -p dist/scripts && cp node_modules/date-time-format-timezone/build/browserified/date-time-format-timezone-complete-min.js dist/scripts", diff --git a/assets/rollup.config.js b/assets/rollup.config.js index 586b930ce09b0c49c8060982079fc1f4651eeada..72468a7608c73713facac10ac93d195b21374fee 100644 --- a/assets/rollup.config.js +++ b/assets/rollup.config.js @@ -62,11 +62,8 @@ const getBundleConfig = function (src, dest) { }; module.exports = [ - - getBundleConfig('src/scripts/index.js', 'dist/bundle.js') - - // getBundleConfig('js/bundles/coverage.js', 'dist/scripts/coverage.js'), - + getBundleConfig('src/scripts/index.js', 'dist/bundle.js'), + getBundleConfig('src/scripts/networks/index.js', 'dist/network-bundle.js') ]; diff --git a/assets/src/scripts/components/dailyValueHydrograph/index.js b/assets/src/scripts/components/dailyValueHydrograph/index.js index 522b066c0cdf250c71fddcc33573c7d07766a5aa..a4151c61d1a5d377aeac9f41686b67f9d062b257 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/index.js +++ b/assets/src/scripts/components/dailyValueHydrograph/index.js @@ -1,6 +1,6 @@ import {select} from 'd3-selection'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {drawErrorAlert, drawInfoAlert} from '../../d3-rendering/alerts'; import {drawLoadingIndicator} from '../../d3-rendering/loading-indicator'; diff --git a/assets/src/scripts/components/dailyValueHydrograph/index.spec.js b/assets/src/scripts/components/dailyValueHydrograph/index.spec.js index acb8c980797d5cdb7a5fe3179932b82f64717792..8cc790bcdd815892646cbf6c9a1e5d69af5ac857 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/index.spec.js +++ b/assets/src/scripts/components/dailyValueHydrograph/index.spec.js @@ -1,6 +1,6 @@ import {select} from 'd3-selection'; -import {configureStore, Actions} from '../../store'; +import {configureStore, Actions} from '../../store/site-store'; import {attachToNode} from './index'; diff --git a/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js b/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js index c45625a516738c71eeb845e5cc9dc5a8683323df..6fb09676d3deb78a5849e4861178dc7b1374df5d 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js +++ b/assets/src/scripts/components/dailyValueHydrograph/time-series-graph.spec.js @@ -1,6 +1,6 @@ import {select} from 'd3-selection'; -import {configureStore, Actions} from '../../store'; +import {configureStore, Actions} from '../../store/site-store'; import {drawTimeSeriesGraph} from './time-series-graph'; diff --git a/assets/src/scripts/components/dailyValueHydrograph/tooltip.js b/assets/src/scripts/components/dailyValueHydrograph/tooltip.js index 1e819c732e2bf4ac358421c56d68a16152015c80..433714d55b169a0f6c782b51abb1c20367627e43 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/tooltip.js +++ b/assets/src/scripts/components/dailyValueHydrograph/tooltip.js @@ -6,7 +6,7 @@ import {drawCursorSlider} from '../../d3-rendering/cursor-slider'; import {drawFocusCircles, drawFocusOverlay, drawFocusLine} from '../../d3-rendering/graph-tooltip'; import {link} from '../../lib/d3-redux'; import {getObservationsCursorOffset, getCurrentObservationsTimeSeriesUnitOfMeasure} from '../../selectors/observations-selector'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {APPROVED, ESTIMATED} from './time-series-graph'; import {getLayout} from './selectors/layout'; diff --git a/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js b/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js index 6162fdb55c4307d97cbb37fac1ae72503e84b438..87bd1de4d598c0c6c3c6695df01c9db2c84d20cf 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js +++ b/assets/src/scripts/components/dailyValueHydrograph/tooltip.spec.js @@ -1,6 +1,6 @@ import {select} from 'd3-selection'; -import {configureStore} from '../../store'; +import {configureStore} from '../../store/site-store'; import {drawTooltipFocus, drawTooltipText, drawTooltipCursorSlider} from './tooltip'; diff --git a/assets/src/scripts/components/hydrograph/audible.js b/assets/src/scripts/components/hydrograph/audible.js index 7c7985eb12d3574d53428a21bf4b1bbd8481c088..3897afb24c89a5e3ad3bda9a6430b9e3c1321d64 100644 --- a/assets/src/scripts/components/hydrograph/audible.js +++ b/assets/src/scripts/components/hydrograph/audible.js @@ -8,7 +8,7 @@ import { tsCursorPointsSelector } from './cursor'; import { getMainXScale, getMainYScale } from './scales'; import config from '../../config'; import { link } from '../../lib/d3-redux'; -import { Actions } from '../../store'; +import { Actions } from '../../store/site-store'; // Higher tones get lower volume const volumeScale = scaleLinear().range([2, .3]); diff --git a/assets/src/scripts/components/hydrograph/audible.spec.js b/assets/src/scripts/components/hydrograph/audible.spec.js index b0b5b94b3197135c9acfb348b87abb3f97bedd26..fe9ed994c4e48a43fc6f303743a12340154e6204 100644 --- a/assets/src/scripts/components/hydrograph/audible.spec.js +++ b/assets/src/scripts/components/hydrograph/audible.spec.js @@ -1,6 +1,6 @@ import { select } from 'd3-selection'; import { audibleUI } from './audible'; -import { configureStore } from '../../store'; +import { configureStore } from '../../store/site-store'; const TEST_STATE = { diff --git a/assets/src/scripts/components/hydrograph/cursor.spec.js b/assets/src/scripts/components/hydrograph/cursor.spec.js index 4dff5076b432b5af2db3455679bcbc5d5d4028ca..9788297e8c32b109ebe5bfa39fb67ed512178e76 100644 --- a/assets/src/scripts/components/hydrograph/cursor.spec.js +++ b/assets/src/scripts/components/hydrograph/cursor.spec.js @@ -1,4 +1,4 @@ -import {Actions, configureStore} from '../../store'; +import {Actions, configureStore} from '../../store/site-store'; import {tsCursorPointsSelector, cursorOffsetSelector} from './cursor'; let DATA = [12, 13, 14, 15, 16].map(hour => { diff --git a/assets/src/scripts/components/hydrograph/date-controls.js b/assets/src/scripts/components/hydrograph/date-controls.js index b342342a379d51ea39b75c7381b9e2c0e7836646..b15dc6a979f481470537528b27ea4c7c00be4ac4 100644 --- a/assets/src/scripts/components/hydrograph/date-controls.js +++ b/assets/src/scripts/components/hydrograph/date-controls.js @@ -11,7 +11,7 @@ import { getCustomTimeRange, getIanaTimeZone } from '../../selectors/time-series-selector'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; export const drawDateRangeControls = function(elem, store, siteno) { diff --git a/assets/src/scripts/components/hydrograph/date-controls.spec.js b/assets/src/scripts/components/hydrograph/date-controls.spec.js index 68db5a9ef007fed449a4015212622c7280d5e4fe..e282ba1249d687915b24b467c8b11d50b25e3d7b 100644 --- a/assets/src/scripts/components/hydrograph/date-controls.spec.js +++ b/assets/src/scripts/components/hydrograph/date-controls.spec.js @@ -1,6 +1,6 @@ import { select } from 'd3-selection'; -import { Actions, configureStore } from '../../store'; +import { Actions, configureStore } from '../../store/site-store'; import {drawDateRangeControls} from './date-controls'; diff --git a/assets/src/scripts/components/hydrograph/graph-brush.js b/assets/src/scripts/components/hydrograph/graph-brush.js index 12156e887d9528244a79eb459559d4f85ce58bbb..0244dc8aa54a4ae95fb7711af34327f21daa8eb1 100644 --- a/assets/src/scripts/components/hydrograph/graph-brush.js +++ b/assets/src/scripts/components/hydrograph/graph-brush.js @@ -4,7 +4,7 @@ import {createStructuredSelector} from 'reselect'; import {appendXAxis} from '../../d3-rendering/axes'; import {link} from '../../lib/d3-redux'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {getBrushXAxis} from './axes'; import {currentVariableLineSegmentsSelector} from './drawing-data'; diff --git a/assets/src/scripts/components/hydrograph/graph-brush.spec.js b/assets/src/scripts/components/hydrograph/graph-brush.spec.js index eb9769131e9b0486149045d6174187f3d81b772d..c1c5fe6df7762209c6534b02865b2ebd4a823333 100644 --- a/assets/src/scripts/components/hydrograph/graph-brush.spec.js +++ b/assets/src/scripts/components/hydrograph/graph-brush.spec.js @@ -1,6 +1,6 @@ import {select} from 'd3-selection'; -import{configureStore} from '../../store'; +import{configureStore} from '../../store/site-store'; import {drawGraphBrush} from './graph-brush'; diff --git a/assets/src/scripts/components/hydrograph/graph-controls.js b/assets/src/scripts/components/hydrograph/graph-controls.js index 6d616eebce6dcd914e842671578a6b765c79d066..33742ef257418f0f1e8895e1eb08f1d91f82c3db 100644 --- a/assets/src/scripts/components/hydrograph/graph-controls.js +++ b/assets/src/scripts/components/hydrograph/graph-controls.js @@ -1,7 +1,7 @@ import {link} from '../../lib/d3-redux'; import {getCurrentVariableTimeSeries} from '../../selectors/time-series-selector'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {audibleUI} from './audible'; import {getCurrentVariableMedianStatistics} from '../../selectors/median-statistics-selector'; diff --git a/assets/src/scripts/components/hydrograph/graph-controls.spec.js b/assets/src/scripts/components/hydrograph/graph-controls.spec.js index 79ca23104765a6c6dbdb6369f25b112c0be7cb8a..f08d3f82cf966f45f1ea901d561ebe45b1a7282c 100644 --- a/assets/src/scripts/components/hydrograph/graph-controls.spec.js +++ b/assets/src/scripts/components/hydrograph/graph-controls.spec.js @@ -1,4 +1,4 @@ -import {Actions, configureStore} from '../../store'; +import {Actions, configureStore} from '../../store/site-store'; import { select } from 'd3-selection'; import { drawGraphControls } from './graph-controls'; diff --git a/assets/src/scripts/components/hydrograph/index.js b/assets/src/scripts/components/hydrograph/index.js index 1e21cdd1a6277a8b1617303d127fadefda3ea3b9..5c610b3ee722b4603c820df4420325ba2a22df83 100644 --- a/assets/src/scripts/components/hydrograph/index.js +++ b/assets/src/scripts/components/hydrograph/index.js @@ -9,7 +9,7 @@ import {drawWarningAlert, drawInfoAlert} from '../../d3-rendering/alerts'; import {link} from '../../lib/d3-redux'; import {hasAnyTimeSeries, getCurrentParmCd, getVariables} from '../../selectors/time-series-selector'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {renderTimeSeriesUrlParams} from '../../url-params'; import {drawDateRangeControls} from './date-controls'; diff --git a/assets/src/scripts/components/hydrograph/index.spec.js b/assets/src/scripts/components/hydrograph/index.spec.js index 59d1b2a8304f17f18102efdcb46964553a2e20b2..a2203caea328d3ffb39820f99287f2e02be0327c 100644 --- a/assets/src/scripts/components/hydrograph/index.spec.js +++ b/assets/src/scripts/components/hydrograph/index.spec.js @@ -1,6 +1,6 @@ import { select, selectAll } from 'd3-selection'; import { attachToNode } from './index'; -import { Actions, configureStore } from '../../store'; +import { Actions, configureStore } from '../../store/site-store'; const TEST_STATE = { diff --git a/assets/src/scripts/components/hydrograph/legend.spec.js b/assets/src/scripts/components/hydrograph/legend.spec.js index dea6bd05991493839006cf66e492bdbd9c51d972..c32d79158e59af9e0e4cf040fc826ce979f77c84 100644 --- a/assets/src/scripts/components/hydrograph/legend.spec.js +++ b/assets/src/scripts/components/hydrograph/legend.spec.js @@ -1,7 +1,7 @@ import {select, selectAll} from 'd3-selection'; import {legendMarkerRowsSelector, drawTimeSeriesLegend} from './legend'; import {lineMarker, rectangleMarker, textOnlyMarker} from '../../d3-rendering/markers'; -import {Actions, configureStore} from '../../store'; +import {Actions, configureStore} from '../../store/site-store'; describe('UV: Legend module', () => { diff --git a/assets/src/scripts/components/hydrograph/method-picker.js b/assets/src/scripts/components/hydrograph/method-picker.js index 91bff95e92de2637ad7938dc68e1d6627ff2300b..2912d271b08ec7bc7699a41372c86e04363040fe 100644 --- a/assets/src/scripts/components/hydrograph/method-picker.js +++ b/assets/src/scripts/components/hydrograph/method-picker.js @@ -8,7 +8,7 @@ import {createStructuredSelector} from 'reselect'; import{link} from '../../lib/d3-redux'; import config from '../../config'; import {getCurrentMethodID, getAllMethodsForCurrentVariable} from '../../selectors/time-series-selector'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import { } from './time-series'; diff --git a/assets/src/scripts/components/hydrograph/method-picker.spec.js b/assets/src/scripts/components/hydrograph/method-picker.spec.js index b2391a7450ca8fbc9ac4606b62335abb5fa348d0..ac6cc38ad500bbe01697190817425fdd7f15f2db 100644 --- a/assets/src/scripts/components/hydrograph/method-picker.spec.js +++ b/assets/src/scripts/components/hydrograph/method-picker.spec.js @@ -1,6 +1,6 @@ import { select } from 'd3-selection'; -import { configureStore } from '../../store'; +import { configureStore } from '../../store/site-store'; import { drawMethodPicker } from './method-picker'; diff --git a/assets/src/scripts/components/hydrograph/parameters.js b/assets/src/scripts/components/hydrograph/parameters.js index 13ef13f3838cbebd66ceedbc1a957697e20302a1..dc86280baeaa0235dbc11c5e74bfbc2bcdc4d096 100644 --- a/assets/src/scripts/components/hydrograph/parameters.js +++ b/assets/src/scripts/components/hydrograph/parameters.js @@ -5,7 +5,7 @@ import {select} from 'd3-selection'; import {getVariables, getCurrentVariableID, getTimeSeries} from '../../selectors/time-series-selector'; import config from '../../config'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {appendTooltip} from '../../tooltips'; import {sortedParameters} from '../../utils'; diff --git a/assets/src/scripts/components/hydrograph/parameters.spec.js b/assets/src/scripts/components/hydrograph/parameters.spec.js index 769f2f8eaa4d52d4c6ec2b53fbce0a3f5da63cf8..8bea383ccb61b9add8bdfe4253dccaff2f3ade16 100644 --- a/assets/src/scripts/components/hydrograph/parameters.spec.js +++ b/assets/src/scripts/components/hydrograph/parameters.spec.js @@ -1,7 +1,7 @@ import { select } from 'd3-selection'; import { scaleLinear } from 'd3-scale'; import { addSparkLine, plotSeriesSelectTable, availableTimeSeriesSelector } from './parameters'; -import { configureStore} from '../../store'; +import { configureStore} from '../../store/site-store'; describe('Parameters module', () => { diff --git a/assets/src/scripts/components/hydrograph/time-series-graph.spec.js b/assets/src/scripts/components/hydrograph/time-series-graph.spec.js index 9b279120c3dc9665feea09409ebe11a1afb34e80..3044ffb7624fe29a5dc973866fb68115c2b51cf3 100644 --- a/assets/src/scripts/components/hydrograph/time-series-graph.spec.js +++ b/assets/src/scripts/components/hydrograph/time-series-graph.spec.js @@ -1,6 +1,6 @@ import { select, selectAll } from 'd3-selection'; -import {Actions, configureStore} from '../../store'; +import {Actions, configureStore} from '../../store/site-store'; import {drawTimeSeriesGraph} from './time-series-graph'; diff --git a/assets/src/scripts/components/hydrograph/tooltip.js b/assets/src/scripts/components/hydrograph/tooltip.js index fd9bd4cdd33545cd86277de59153b7709d7f5a05..fa56d888b2a8b47fb762bb35f5bfa271caad9502 100644 --- a/assets/src/scripts/components/hydrograph/tooltip.js +++ b/assets/src/scripts/components/hydrograph/tooltip.js @@ -10,7 +10,7 @@ import {drawCursorSlider} from '../../d3-rendering/cursor-slider'; import {drawFocusOverlay, drawFocusCircles, drawFocusLine} from '../../d3-rendering/graph-tooltip'; import {link} from '../../lib/d3-redux'; import {getCurrentVariable, getCurrentParmCd} from '../../selectors/time-series-selector'; -import {Actions} from '../../store'; +import {Actions} from '../../store/site-store'; import {mediaQuery, convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../utils'; import {cursorTimeSelector, tsCursorPointsSelector} from './cursor'; diff --git a/assets/src/scripts/components/hydrograph/tooltip.spec.js b/assets/src/scripts/components/hydrograph/tooltip.spec.js index 2a60e8c86ed5f3c759435b6471e22b1fdf96ef11..0aced6cbe1d32c81df6ade170cdc376a81fc4187 100644 --- a/assets/src/scripts/components/hydrograph/tooltip.spec.js +++ b/assets/src/scripts/components/hydrograph/tooltip.spec.js @@ -1,5 +1,5 @@ import { select } from 'd3-selection'; -import { Actions, configureStore } from '../../store'; +import { Actions, configureStore } from '../../store/site-store'; import {drawTooltipText, drawTooltipFocus, tooltipPointsSelector, drawTooltipCursorSlider} from './tooltip'; diff --git a/assets/src/scripts/components/map/flood-slider.js b/assets/src/scripts/components/map/flood-slider.js index 54f0403c31999092dfd1db605d58395b1d73996f..e4593af342e34925bc607be6560bcaa8ace882b4 100644 --- a/assets/src/scripts/components/map/flood-slider.js +++ b/assets/src/scripts/components/map/flood-slider.js @@ -1,7 +1,7 @@ import { createStructuredSelector } from 'reselect'; import { link } from '../../lib/d3-redux'; import { getFloodStages, getFloodStageHeight, getFloodGageHeightStageIndex, hasFloodData } from '../../selectors/flood-data-selector'; -import { Actions } from '../../store'; +import { Actions } from '../../store/site-store'; import { appendTooltip } from '../../tooltips'; diff --git a/assets/src/scripts/components/map/flood-slider.spec.js b/assets/src/scripts/components/map/flood-slider.spec.js index ed98d624bde910422a0aca12bb49658056aea004..1e0ee66a304e0873725ad673ef39c7bed34520b4 100644 --- a/assets/src/scripts/components/map/flood-slider.spec.js +++ b/assets/src/scripts/components/map/flood-slider.spec.js @@ -1,5 +1,5 @@ import { select } from 'd3-selection'; -import { configureStore } from '../../store'; +import { configureStore } from '../../store/site-store'; import { floodSlider } from './flood-slider'; diff --git a/assets/src/scripts/components/map/index.js b/assets/src/scripts/components/map/index.js index 5a92d4958e8d2350e826dde40bd08c7420ad56a4..634d0f283f2d25f3dd72e730773a9243dbf48342 100644 --- a/assets/src/scripts/components/map/index.js +++ b/assets/src/scripts/components/map/index.js @@ -6,7 +6,7 @@ import { link } from '../../lib/d3-redux'; import config from '../../config'; import { FLOOD_EXTENTS_ENDPOINT, FLOOD_BREACH_ENDPOINT, FLOOD_LEVEE_ENDPOINT } from '../../web-services/flood-data'; import { hasFloodData, getFloodExtent, getFloodStageHeight } from '../../selectors/flood-data-selector'; -import { Actions } from '../../store'; +import { Actions } from '../../store/site-store'; import { floodSlider } from './flood-slider'; import { createLegendControl, createFIMLegend, createNldiLegend } from './legend'; import { addNldiLayers} from './nldiMapping'; diff --git a/assets/src/scripts/components/map/index.spec.js b/assets/src/scripts/components/map/index.spec.js index 33dabbc1456001fc85219866b42c5b1dc2ca9ce2..65025a6617aca93828c428b9d56984550538f35b 100644 --- a/assets/src/scripts/components/map/index.spec.js +++ b/assets/src/scripts/components/map/index.spec.js @@ -1,6 +1,6 @@ import { select } from 'd3-selection'; import { attachToNode } from './index'; -import { configureStore } from '../../store'; +import { configureStore } from '../../store/site-store'; describe('map module', () => { let mapNode; diff --git a/assets/src/scripts/d3-rendering/legend.spec.js b/assets/src/scripts/d3-rendering/legend.spec.js index 1ce4f0688dff2f5b6afd8ed8dd0afcd1bffb2136..5ae110edecd2e66727e9882534ad6468b7cb5850 100644 --- a/assets/src/scripts/d3-rendering/legend.spec.js +++ b/assets/src/scripts/d3-rendering/legend.spec.js @@ -1,7 +1,7 @@ import {select, selectAll} from 'd3-selection'; import {lineMarker, rectangleMarker, textOnlyMarker} from './markers'; import {drawSimpleLegend} from './legend'; -import {configureStore} from '../store'; +import {configureStore} from '../store/site-store'; import {drawTimeSeriesLegend} from '../components/dailyValueHydrograph/legend'; describe('Legend module', () => { diff --git a/assets/src/scripts/index.js b/assets/src/scripts/index.js index b4df64ff1c658157693cb119160f1b2fbf38ce6c..5dc6716f43342c2266dfcf2922a76fd3d0f771de 100644 --- a/assets/src/scripts/index.js +++ b/assets/src/scripts/index.js @@ -6,21 +6,19 @@ import wdfnviz from 'wdfn-viz'; import {register} from './helpers'; register(); -import {configureStore} from './store'; +import {configureStore} from './store/site-store'; import {getParamString} from './url-params'; import {attachToNode as EmbedComponent} from './components/embed'; import {attachToNode as DailyValueHydrographComponent} from './components/dailyValueHydrograph'; import {attachToNode as HydrographComponent} from './components/hydrograph'; import {attachToNode as MapComponent} from './components/map'; -import {attachToNode as NetworkMapComponent} from './networks'; const COMPONENTS = { embed: EmbedComponent, 'dv-hydrograph': DailyValueHydrographComponent, hydrograph: HydrographComponent, - map: MapComponent, - 'network-map': NetworkMapComponent + map: MapComponent }; diff --git a/assets/src/scripts/index.spec.js b/assets/src/scripts/index.spec.js index b37f248ced2fbd81126b13c743ed9ea902e75ec7..6e1ced577ce329f3181e4d1b6a4aec2115f18ea4 100644 --- a/assets/src/scripts/index.spec.js +++ b/assets/src/scripts/index.spec.js @@ -69,7 +69,7 @@ import './selectors/time-series-selector.spec'; import './store/flood-data-reducer.spec'; import './store/nldi-data-reducer.spec'; import './store/flood-state-reducer.spec'; -import './store/index.spec'; +import './store/site-store.spec'; import './store/observations-data-reducer.spec'; import './store/observations-state-reducer.spec'; import './store/series-reducer.spec'; diff --git a/assets/src/scripts/networks/components/index.js b/assets/src/scripts/networks/components/index.js new file mode 100644 index 0000000000000000000000000000000000000000..9b27fdfb72bcbfd1d4d6aab9d3a20731ef02fb1b --- /dev/null +++ b/assets/src/scripts/networks/components/index.js @@ -0,0 +1,103 @@ +import { select } from 'd3-selection'; +import { createStructuredSelector } from 'reselect'; +import { map as createMap, control, layerGroup } from 'leaflet'; +import { TiledMapLayer, basemapLayer } from 'esri-leaflet/src/EsriLeaflet'; +import { link } from '../../lib/d3-redux'; +import config from '../../config'; + +import { Actions } from '../network-store'; +import { createLegendControl, createNetworkLegend } from './network-legend'; +import { addNetworkLayers} from './network-mapping'; +import { hasNetworkData, getNetworkSites} + from '../network-data-selector'; + +/* + * Creates a network map + */ +const networkMap = function(node, {extent}, store) { + + let gray = layerGroup(); + basemapLayer('Gray').addTo(gray); + + if (config.HYDRO_ENDPOINT) { + gray.addLayer(new TiledMapLayer({url: config.HYDRO_ENDPOINT, + maxZoom: 22, + maxNativeZoom: 19})); + } + + // Create map on node + const map = createMap('network-map', { + center: [0, 0], + zoom: 1, + scrollWheelZoom: false, + layers: gray + }); + + const pExt = JSON.parse(extent); + const leafletExtent = [[pExt[3],pExt[2]],[pExt[1],pExt[0]]]; + map.fitBounds(leafletExtent); + + map.on('focus', () => { + map.scrollWheelZoom.enable(); + }); + map.on('blur', () => { + map.scrollWheelZoom.disable(); + }); + + + let legendControl = createLegendControl({ + position: 'bottomright' + }); + legendControl.addTo(map); + + const updateNetworkLayers = function (node, {networkSites}) { + addNetworkLayers(map, networkSites); + }; + + //add additional baselayer + var baseLayers = { + 'Grayscale': gray, + 'Satellite': basemapLayer('ImageryFirefly') + }; + + //add layer control + control.layers(baseLayers).addTo(map); + + // Add the ESRI World Hydro Reference Overlay + if (config.HYDRO_ENDPOINT) { + map.addLayer(new TiledMapLayer({url: config.HYDRO_ENDPOINT})); + } + + // // Add a marker at the site location + // createMarker([latitude, longitude]).addTo(map); + + /* + * Creates the NLDI legend if NLDI data is available, otherwise removes the NLDI legend if it exists. + * @param {HTMLElement} node - element where the map is rendered + * @param {Boolean} isNldiAvailable + */ + const addNetworkLegend = function(node, isNetworkAvailable) { + createNetworkLegend(legendControl, isNetworkAvailable); + }; + + node + .call(link(store, addNetworkLegend, hasNetworkData)) + .call(link(store, updateNetworkLayers, createStructuredSelector({ + networkSites: getNetworkSites + }))); +}; + +/* + * Creates the network map with node and attach it to the Redux store. + * @param {Object} store - Redux store + * @param {Object} node - DOM element + * @param {String} networkcd + */ +export const attachToNode = function(store, node, {networkcd, extent}) { + + store.dispatch(Actions.retrieveNetworkData(networkcd)); + + select(node) + .call(networkMap, {extent}, store); +}; + diff --git a/assets/src/scripts/networks/network-legend.js b/assets/src/scripts/networks/components/network-legend.js similarity index 97% rename from assets/src/scripts/networks/network-legend.js rename to assets/src/scripts/networks/components/network-legend.js index f81419405716d3d9241489bfadeb19c2909f1726..bba46cd92b025237ac645de41e9212bc585e01c7 100644 --- a/assets/src/scripts/networks/network-legend.js +++ b/assets/src/scripts/networks/components/network-legend.js @@ -4,9 +4,9 @@ import { select } from 'd3-selection'; import { control as createControl, DomUtil, DomEvent } from 'leaflet'; // import { get } from '../ajax'; -import config from '../config'; -import { mediaQuery } from '../utils'; -import { markerFillColor, markerFillOpacity} from './networkMapping'; +import config from '../../config'; +import { mediaQuery } from '../../utils'; +import { markerFillColor, markerFillOpacity} from './network-mapping'; // const fetchLayerLegend = function(layer, defaultName) { diff --git a/assets/src/scripts/networks/networkMapping.js b/assets/src/scripts/networks/components/network-mapping.js similarity index 100% rename from assets/src/scripts/networks/networkMapping.js rename to assets/src/scripts/networks/components/network-mapping.js diff --git a/assets/src/scripts/networks/index.js b/assets/src/scripts/networks/index.js index f2f65aaf17c57955ef53a8aad5d154566c28972e..9caf0ae89d4d26fb501481ab06d02d3ff70320a3 100644 --- a/assets/src/scripts/networks/index.js +++ b/assets/src/scripts/networks/index.js @@ -1,103 +1,42 @@ -import { select } from 'd3-selection'; -import { createStructuredSelector } from 'reselect'; -import { map as createMap, control, layerGroup } from 'leaflet'; -import { TiledMapLayer, basemapLayer } from 'esri-leaflet/src/EsriLeaflet'; -import { link } from '../lib/d3-redux'; -import config from '../config'; +import '../polyfills'; -import { Actions } from '../store'; -import { createLegendControl, createNetworkLegend } from './network-legend'; -import { addNetworkLayers} from './networkMapping'; -import { hasNetworkData, getNetworkSites} - from '../selectors/network-data-selector'; +import wdfnviz from 'wdfn-viz'; -/* - * Creates a network map - */ -const networkMap = function(node, {extent}, store) { +// Load misc Javascript helpers for general page interactivity. +import {register} from '../helpers'; +register(); - let gray = layerGroup(); - basemapLayer('Gray').addTo(gray); +import {configureStore} from './network-store'; +import {getParamString} from '../url-params'; - if (config.HYDRO_ENDPOINT) { - gray.addLayer(new TiledMapLayer({url: config.HYDRO_ENDPOINT, - maxZoom: 22, - maxNativeZoom: 19})); - } - - // Create map on node - const map = createMap('network-map', { - center: [0, 0], - zoom: 1, - scrollWheelZoom: false, - layers: gray - }); - - const pExt = JSON.parse(extent); - const leafletExtent = [[pExt[3],pExt[2]],[pExt[1],pExt[0]]]; - map.fitBounds(leafletExtent); - - map.on('focus', () => { - map.scrollWheelZoom.enable(); - }); - map.on('blur', () => { - map.scrollWheelZoom.disable(); - }); +import {attachToNode as NetworkMapComponent} from './components'; +const COMPONENTS = { + 'network-map': NetworkMapComponent +}; - let legendControl = createLegendControl({ - position: 'bottomright' +const load = function () { + let nodes = document.getElementsByClassName('wdfn-component'); + let store = configureStore({ + ui: { + windowWidth: window.innerWidth + } }); - legendControl.addTo(map); - - const updateNetworkLayers = function (node, {networkSites}) { - addNetworkLayers(map, networkSites); - }; - - //add additional baselayer - var baseLayers = { - 'Grayscale': gray, - 'Satellite': basemapLayer('ImageryFirefly') - }; - - //add layer control - control.layers(baseLayers).addTo(map); - - // Add the ESRI World Hydro Reference Overlay - if (config.HYDRO_ENDPOINT) { - map.addLayer(new TiledMapLayer({url: config.HYDRO_ENDPOINT})); + for (let node of nodes) { + // If options is specified on the node, expect it to be a JSON string. + // Otherwise, use the dataset attributes as the component options. + const options = node.dataset.options ? JSON.parse(node.dataset.options) : node.dataset; + const hashOptions = Object.fromEntries(new window.URLSearchParams(getParamString())); + COMPONENTS[node.dataset.component](store, node, Object.assign({}, options, hashOptions)); } - // // Add a marker at the site location - // createMarker([latitude, longitude]).addTo(map); - - /* - * Creates the NLDI legend if NLDI data is available, otherwise removes the NLDI legend if it exists. - * @param {HTMLElement} node - element where the map is rendered - * @param {Boolean} isNldiAvailable - */ - const addNetworkLegend = function(node, isNetworkAvailable) { - createNetworkLegend(legendControl, isNetworkAvailable); - }; - node - .call(link(store, addNetworkLegend, hasNetworkData)) - .call(link(store, updateNetworkLayers, createStructuredSelector({ - networkSites: getNetworkSites - }))); }; -/* - * Creates the network map with node and attach it to the Redux store. - * @param {Object} store - Redux store - * @param {Object} node - DOM element - * @param {String} networkcd - */ -export const attachToNode = function(store, node, {networkcd, extent}) { +wdfnviz.main(load); - store.dispatch(Actions.retrieveNetworkData(networkcd)); - select(node) - .call(networkMap, {extent}, store); -}; +// Leaflet expects an exports global to exist - so although we don't use this, +// just set it to something so it's not undefined. +export var dummy = true; \ No newline at end of file diff --git a/assets/src/scripts/store/network-data-reducer.js b/assets/src/scripts/networks/network-data-reducer.js similarity index 100% rename from assets/src/scripts/store/network-data-reducer.js rename to assets/src/scripts/networks/network-data-reducer.js diff --git a/assets/src/scripts/store/network-data-reducer.spec.js b/assets/src/scripts/networks/network-data-reducer.spec.js similarity index 100% rename from assets/src/scripts/store/network-data-reducer.spec.js rename to assets/src/scripts/networks/network-data-reducer.spec.js diff --git a/assets/src/scripts/selectors/network-data-selector.js b/assets/src/scripts/networks/network-data-selector.js similarity index 100% rename from assets/src/scripts/selectors/network-data-selector.js rename to assets/src/scripts/networks/network-data-selector.js diff --git a/assets/src/scripts/selectors/network-data-selector.spec.js b/assets/src/scripts/networks/network-data-selector.spec.js similarity index 100% rename from assets/src/scripts/selectors/network-data-selector.spec.js rename to assets/src/scripts/networks/network-data-selector.spec.js diff --git a/assets/src/scripts/web-services/network-data.js b/assets/src/scripts/networks/network-data.js similarity index 100% rename from assets/src/scripts/web-services/network-data.js rename to assets/src/scripts/networks/network-data.js diff --git a/assets/src/scripts/web-services/network-data.spec.js b/assets/src/scripts/networks/network-data.spec.js similarity index 100% rename from assets/src/scripts/web-services/network-data.spec.js rename to assets/src/scripts/networks/network-data.spec.js diff --git a/assets/src/scripts/networks/network-store.js b/assets/src/scripts/networks/network-store.js new file mode 100644 index 0000000000000000000000000000000000000000..dba4db503a35d8d265d9fad9abc9e520fd4c7f51 --- /dev/null +++ b/assets/src/scripts/networks/network-store.js @@ -0,0 +1,53 @@ +import {fetchNetworkSites} from '../networks/network-data'; + +import {uiReducer as ui} from '../store/ui-reducer'; +import {networkDataReducer as networkData} from './network-data-reducer'; +import {configureReduxStore} from '../store'; + +export const Actions = { + retrieveNetworkData(networkCd) { + return function(dispatch) { + const networkSites = fetchNetworkSites(networkCd); + + return Promise.all( [networkSites] + ).then(function(data){ + const [networkSites] = data; + dispatch(Actions.setNetworkFeatures(networkSites)); + }); + }; + }, + setNetworkFeatures(networkSites) { + return { + type: 'SET_NETWORK_FEATURES', + networkSites + }; + }, + resizeUI(windowWidth, width) { + return { + type: 'RESIZE_UI', + windowWidth, + width + }; + } +}; + +const reducers = { + ui, + networkData +}; + +export const configureStore = function(initialState){ + + initialState = { + networkData: { + networkSites: [] + }, + ui: { + windowWidth: 1024, + width: 800 + }, + ...initialState + }; + + return configureReduxStore(initialState, reducers); +}; \ No newline at end of file diff --git a/assets/src/scripts/store/index.js b/assets/src/scripts/store/index.js index afc7284dc5166f6814403639224ed25c2567ad41..1300d454d0b023ef605dfb7b77903887bcaab133 100644 --- a/assets/src/scripts/store/index.js +++ b/assets/src/scripts/store/index.js @@ -1,583 +1,39 @@ - -import find from 'lodash/find'; -import findKey from 'lodash/findKey'; -import last from 'lodash/last'; -import {DateTime} from 'luxon'; -import {applyMiddleware, createStore, combineReducers, compose} from 'redux'; +import {createStore, combineReducers, compose, applyMiddleware} from 'redux'; import {default as thunk} from 'redux-thunk'; -import {normalize} from '../schema'; -import {calcStartTime, sortedParameters} from '../utils'; - -import {getCurrentParmCd, getCurrentDateRange, hasTimeSeries, getTsRequestKey, getRequestTimeRange, - getCustomTimeRange, getIanaTimeZone} from '../selectors/time-series-selector'; - -import {fetchFloodFeatures, fetchFloodExtent} from '../web-services/flood-data'; -import {getPreviousYearTimeSeries, getTimeSeries, queryWeatherService} from '../web-services/models'; -import {fetchNldiUpstreamSites, fetchNldiDownstreamSites, fetchNldiDownstreamFlow, fetchNldiUpstreamFlow, fetchNldiUpstreamBasin} from '../web-services/nldi-data'; -import {fetchTimeSeries} from '../web-services/observations'; -import {fetchSiteStatistics} from '../web-services/statistics-data'; -import {fetchNetworkSites} from '../web-services/network-data'; - -import {floodDataReducer as floodData} from './flood-data-reducer'; -import {floodStateReducer as floodState} from './flood-state-reducer'; -import {nldiDataReducer as nldiData} from './nldi-data-reducer'; -import {observationsDataReducer as observationsData} from './observations-data-reducer'; -import {observationsStateReducer as observationsState} from './observations-state-reducer'; -import {seriesReducer as series} from './series-reducer'; -import {statisticsDataReducer as statisticsData} from './statistics-data-reducer'; -import {timeSeriesStateReducer as timeSeriesState} from './time-series-state-reducer'; -import {uiReducer as ui} from './ui-reducer'; -import {networkDataReducer as networkData} from './network-data-reducer'; - -const GAGE_HEIGHT_CD = '00065'; -/* - * Helper functions - */ -const getLatestValue = function(collection, parmCd) { - let parmVar = findKey(collection.variables, (varValue) => { - return varValue.variableCode.value === parmCd; - }); - let parmTimeSeries = findKey(collection.timeSeries, (ts) => { - return ts.variable === parmVar; - }); - let points = parmTimeSeries ? collection.timeSeries[parmTimeSeries].points : []; - return points.length ? last(points).value : null; -}; - - -/* - * @param {Object} timeSeries - keys are time series id - * @param {Object} variables - keys are the variable id - */ -const getCurrentVariableId = function(timeSeries, variables) { - const tsVariablesWithData = Object.values(timeSeries) - .filter((ts) => ts.points.length) - .map((ts) => variables[ts.variable]); - const sortedVarsWithData = sortedParameters(tsVariablesWithData); - if (sortedVarsWithData.length) { - return sortedVarsWithData[0].oid; - } else { - const sortedVars = sortedParameters(Object.values(variables)); - return sortedVars.length ? sortedVars[0].oid : ''; - } -}; - -export const Actions = { - retrieveLocationTimeZone(latitude, longitude) { - return function(dispatch) { - return queryWeatherService(latitude, longitude).then( - resp => { - const tzIANA = resp.properties.timeZone || null; // set to time zone to null if unavailable - dispatch(Actions.setLocationIanaTimeZone(tzIANA)); - }, - () => { - dispatch(Actions.setLocationIanaTimeZone(null)); - } - ); - }; - }, - retrieveTimeSeries(siteno, params=null) { - return function (dispatch, getState) { - const currentState = getState(); - const requestKey = getTsRequestKey('current', 'P7D')(currentState); - dispatch(Actions.addTimeSeriesLoading([requestKey])); - return getTimeSeries({sites: [siteno], params}).then( - series => { - const collection = normalize(series, requestKey); - - // Get the start/end times of this request's range. - const notes = collection.queryInfo[requestKey].notes; - const endTime = notes.requestDT; - const startTime = calcStartTime('P7D', endTime, 'local'); - - // Trigger a call to get last year's data - dispatch(Actions.retrieveCompareTimeSeries(siteno, 'P7D', startTime, endTime)); - - // Update the series data for the 'current' series - dispatch(Actions.addSeriesCollection('current', collection)); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - - // Update the application state - dispatch(Actions.toggleTimeSeries('current', true)); - const variable = getCurrentVariableId(collection.timeSeries || {}, collection.variables || {}); - dispatch(Actions.setCurrentVariable(variable)); - dispatch(Actions.setGageHeight(getLatestValue(collection, GAGE_HEIGHT_CD))); - }, - () => { - dispatch(Actions.resetTimeSeries(getTsRequestKey('current', 'P7D')(currentState))); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - - dispatch(Actions.toggleTimeSeries('current', false)); - } - ); - }; - }, - retrieveCompareTimeSeries(site, period, startTime, endTime) { - return function (dispatch, getState) { - const requestKey = getTsRequestKey('compare', period)(getState()); - dispatch(Actions.addTimeSeriesLoading([requestKey])); - return getPreviousYearTimeSeries({site, startTime, endTime}).then( - series => { - const collection = normalize(series, requestKey); - dispatch(Actions.addSeriesCollection(requestKey, collection)); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - }, - () => { - dispatch(Actions.resetTimeSeries(getTsRequestKey('compare', period)(getState()))); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - } - ); - }; - }, - retrieveMedianStatistics(site) { - return function(dispatch) { - return fetchSiteStatistics({site, statType: 'median'}).then( - stats => { - dispatch(Actions.addMedianStats(stats)); - } - ); - }; - }, - - retrieveCustomTimePeriodTimeSeries(site, parameterCd, period) { - return function(dispatch, getState) { - const state = getState(); - const parmCd = parameterCd; - const requestKey = getTsRequestKey('current', 'custom', parmCd)(state); - dispatch(Actions.setCurrentDateRange('custom')); - dispatch(Actions.addTimeSeriesLoading([requestKey])); - return getTimeSeries({sites: [site], params: [parmCd], period: period}).then( - series => { - const collection = normalize(series, requestKey); - const variables = Object.values(collection.variables); - const variableToDraw = find(variables, v => v.variableCode.value === parameterCd); - dispatch(Actions.setCurrentVariable(variableToDraw.variableCode.variableID)); - dispatch(Actions.addSeriesCollection(requestKey, collection)); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - }, - () => { - console.log(`Unable to fetch data for period ${period} and parameter code ${parmCd}`); - dispatch(Actions.addSeriesCollection(requestKey, {})); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - } - ); - }; - }, - - retrieveCustomTimeSeries(site, startTime, endTime, parmCd) { - return function(dispatch, getState) { - const state = getState(); - const thisParmCd = parmCd ? parmCd : getCurrentParmCd(state); - const requestKey = getTsRequestKey('current', 'custom', thisParmCd)(state); - - dispatch(Actions.setCustomDateRange(startTime, endTime)); - dispatch(Actions.addTimeSeriesLoading([requestKey])); - dispatch(Actions.toggleTimeSeries('median', false)); - return getTimeSeries({ - sites: [site], - params: [thisParmCd], - startDate: startTime, - endDate: endTime - }).then( - series => { - const collection = normalize(series, requestKey); - dispatch(Actions.addSeriesCollection(requestKey, collection)); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - }, - () => { - console.log(`Unable to fetch data for between ${startTime} and ${endTime} and parameter code ${thisParmCd}`); - dispatch(Actions.addSeriesCollection(requestKey, {})); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - } - ); - }; - }, - retrieveExtendedTimeSeries(site, period, paramCd=null) { - return function(dispatch, getState) { - const state = getState(); - const thisParamCd = paramCd ? paramCd : getCurrentParmCd(state); - const requestKey = getTsRequestKey ('current', period, thisParamCd)(state); - dispatch(Actions.setCurrentDateRange(period)); - if (!hasTimeSeries('current', period, thisParamCd)(state)) { - dispatch(Actions.addTimeSeriesLoading([requestKey])); - const endTime = getRequestTimeRange('current', 'P7D')(state).end; - const startTime = calcStartTime(period, endTime); - return getTimeSeries({ - sites: [site], - params: [thisParamCd], - startDate: startTime, - endDate: endTime - }).then( - series => { - const collection = normalize(series, requestKey); - dispatch(Actions.retrieveCompareTimeSeries(site, period, startTime, endTime)); - dispatch(Actions.addSeriesCollection(requestKey, collection)); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - }, - () => { - console.log(`Unable to fetch data for period ${period} and parameter code ${thisParamCd}`); - dispatch(Actions.addSeriesCollection(requestKey, {})); - dispatch(Actions.removeTimeSeriesLoading([requestKey])); - } - ); - } else { - return Promise.resolve({}); - } - }; - }, - retrieveDailyValueData(monitoringLocationId, timeSeriesId) { - return function(dispatch) { - return fetchTimeSeries(monitoringLocationId, timeSeriesId) - .then( - (data) => { - dispatch(Actions.setObservationsTimeSeries(timeSeriesId, data)); - }, - () => { - console.log(`Unable to fetch observations time series for ${timeSeriesId}`); - }); - }; - }, - retrieveFloodData(siteno) { - return function (dispatch) { - const floodFeatures = fetchFloodFeatures(siteno); - const floodExtent = fetchFloodExtent(siteno); - return Promise.all([floodFeatures, floodExtent]).then((data) => { - const [features, extent] = data; - const stages = features.map((feature) => feature.attributes.STAGE).sort(function (a, b) { - return a - b; - }); - dispatch(Actions.setFloodFeatures(stages, stages.length ? extent.extent : {})); - }); - }; - }, - retrieveNldiData(siteno) { - return function (dispatch) { - const upstreamFlow = fetchNldiUpstreamFlow(siteno); - const downstreamFlow = fetchNldiDownstreamFlow(siteno); - const upstreamSites = fetchNldiUpstreamSites(siteno); - const downstreamSites = fetchNldiDownstreamSites(siteno); - const upstreamBasin = fetchNldiUpstreamBasin(siteno); - - return Promise.all([ - upstreamFlow, downstreamFlow, upstreamSites, downstreamSites, upstreamBasin - ]).then(function(data) { - const [upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin] = data; - dispatch(Actions.setNldiFeatures(upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin)); - }); - }; - }, - retrieveNetworkData(networkCd) { - return function(dispatch) { - const networkSites = fetchNetworkSites(networkCd); - - return Promise.all( [networkSites] - ).then(function(data){ - const [networkSites] = data; - dispatch(Actions.setNetworkFeatures(networkSites)); - }); - }; - }, - updateCurrentVariable(siteno, variableID) { - return function(dispatch, getState) { - dispatch(Actions.setCurrentVariable(variableID)); - const state = getState(); - const currentDateRange = getCurrentDateRange(state); - if (currentDateRange === 'custom') { - const timeRange = getCustomTimeRange(state); - dispatch( - Actions.retrieveCustomTimeSeries(siteno, timeRange.startDT, timeRange.endDT)); - } else { - dispatch(Actions.retrieveExtendedTimeSeries(siteno, currentDateRange)); - } - }; - }, - startTimeSeriesPlay(maxCursorOffset) { - return function (dispatch, getState) { - let state = getState().timeSeriesState; - if (state.cursorOffset == null || state.cursorOffset >= maxCursorOffset) { - dispatch(Actions.setCursorOffset(0)); - } - if (!state.audiblePlayId) { - let play = function () { - let newOffset = getState().timeSeriesState.cursorOffset + 15 * 60 * 1000; - if (newOffset > maxCursorOffset) { - dispatch(Actions.stopTimeSeriesPlay()); - } else { - dispatch(Actions.setCursorOffset(newOffset)); - } - }; - let playId = window.setInterval(play, 10); - dispatch(Actions.timeSeriesPlayOn(playId)); - } - }; - }, - stopTimeSeriesPlay() { - return function(dispatch, getState) { - window.clearInterval(getState().timeSeriesState.audiblePlayId); - dispatch(Actions.timeSeriesPlayStop()); - }; - }, - timeSeriesPlayOn(playId) { - return { - type: 'TIME_SERIES_PLAY_ON', - playId - }; - }, - timeSeriesPlayStop() { - return { - type: 'TIME_SERIES_PLAY_STOP' - }; - }, - addTimeSeriesLoading(tsKeys) { - return { - type: 'TIME_SERIES_LOADING_ADD', - tsKeys - }; - }, - removeTimeSeriesLoading(tsKeys) { - return { - type: 'TIME_SERIES_LOADING_REMOVE', - tsKeys - }; - }, - setFloodFeatures(stages, extent) { - return { - type: 'SET_FLOOD_FEATURES', - stages, - extent - }; - }, - setNldiFeatures(upstreamFlows, downstreamFlows, upstreamSites, downstreamSites, upstreamBasin) { - return { - type: 'SET_NLDI_FEATURES', - upstreamFlows, - downstreamFlows, - upstreamSites, - downstreamSites, - upstreamBasin - }; - }, - setNetworkFeatures(networkSites) { - return { - type: 'SET_NETWORK_FEATURES', - networkSites - }; - }, - setObservationsTimeSeries(timeSeriesId, data) { - return { - type: 'SET_OBSERVATIONS_TIME_SERIES', - timeSeriesId, - data - }; - }, - toggleTimeSeries(key, show) { - return { - type: 'TOGGLE_TIME_SERIES', - key, - show - }; - }, - addSeriesCollection(key, data) { - return { - type: 'ADD_TIME_SERIES_COLLECTION', - key, - data - }; - }, - resetTimeSeries(key) { - return { - type: 'RESET_TIME_SERIES', - key - }; - }, - addMedianStats(data) { - return { - type: 'MEDIAN_STATS_ADD', - data - }; - }, - setCursorOffset(cursorOffset) { - return { - type: 'SET_CURSOR_OFFSET', - cursorOffset - }; - }, - resizeUI(windowWidth, width) { - return { - type: 'RESIZE_UI', - windowWidth, - width - }; - }, - setHydrographBrushOffset(hydrographBrushOffset) { - return { - type: 'SET_HYDROGRAPH_BRUSH_OFFSET', - hydrographBrushOffset - }; - }, - clearHydrographBrushOffset() { - return { - type: 'CLEAR_HYDROGRAPH_BRUSH_OFFSET' - }; - }, - setCurrentVariable(variableID) { - return { - type: 'SET_CURRENT_VARIABLE', - variableID - }; - }, - setCurrentMethodID(methodID) { - return { - type: 'SET_CURRENT_METHOD_ID', - methodID - }; - }, - setCurrentDateRange(period) { - return { - type: 'SET_CURRENT_DATE_RANGE', - period - }; - }, - setCustomDateRange(startTime, endTime) { - return { - type: 'SET_CUSTOM_DATE_RANGE', - startTime, - endTime - }; - }, - retrieveUserRequestedDataForDateRange(siteno, startTimeStr, endTimeStr) { - return function(dispatch, getState) { - const state = getState(); - const locationIanaTimeZone = getIanaTimeZone(state); - const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis(); - const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis(); - return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime)); - }; - }, - retrieveDataForDateRange(siteno, startTimeStr, endTimeStr, parmCd) { - return function(dispatch, getState) { - const state = getState(); - const locationIanaTimeZone = getIanaTimeZone(state); - const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis(); - const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis(); - return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime, parmCd)); - }; - }, - setGageHeightFromStageIndex(index) { - return function(dispatch, getState) { - const stages = getState().floodData.stages; - if (index > -1 && index < stages.length) { - dispatch(Actions.setGageHeight(stages[index])); - } - }; - }, - setGageHeight(gageHeight) { - return { - type: 'SET_GAGE_HEIGHT', - gageHeight - }; - }, - setLocationIanaTimeZone(ianaTimeZone) { - return { - type: 'LOCATION_IANA_TIME_ZONE_SET', - ianaTimeZone - }; - }, - setCurrentObservationsTimeSeriesId(timeSeriesId) { - return { - type: 'SET_CURRENT_TIME_SERIES_ID', - timeSeriesId - }; - }, - /* - * @param {Number} cursorOffset - difference in epoch time from the start of the graph to the position of of the cursor - */ - setDailyValueCursorOffset(cursorOffset) { - return { - type: 'SET_DAILY_VALUE_CURSOR_OFFSET', - cursorOffset - }; - } -}; - -const appReducer = combineReducers({ - series, - observationsData, - statisticsData, - floodData, - nldiData, - timeSeriesState, - observationsState, - floodState, - ui, - networkData -}); +const createReducer = function(asyncReducers) { + return combineReducers({ + ...asyncReducers + }); +} const MIDDLEWARES = [thunk]; +export const configureReduxStore = function(initialState, reducers) { + + let enhancers; + if (window.__REDUX_DEVTOOLS_EXTENSION__) { + enhancers = compose( + applyMiddleware(...MIDDLEWARES), + window.__REDUX_DEVTOOLS_EXTENSION__({serialize: true}) + ); + } else { + enhancers = applyMiddleware(...MIDDLEWARES); + } -export const configureStore = function (initialState) { - initialState = { - series: {}, - observationsData: {}, - floodData: { - stages: [], - extent: {} - }, - nldiData: { - upstreamFlows: [], - downstreamFlows: [], - upstreamSites: [], - downstreamSites: [], - upstreamBasin: [] - }, - networkData: { - networkSites: [] - }, + const store = createStore(createReducer(reducers), initialState, enhancers); - statisticsData: {}, + // Add a dictionary to keep track of the registered async reducers + store.asyncReducers = {} - timeSeriesState: { - showSeries: { - current: true, - compare: false, - median: false - }, - currentDateRange: 'P7D', - customTimeRange: null, - currentVariableID: null, - cursorOffset: null, - audiblePlayId: null, - loadingTSKeys: [], - hydrographBrushOffset: null - }, - observationsState: { - cursorOffset: null - }, - floodState: { - gageHeight: null - }, - ui : { - windowWidth: 1024, - width: 800 - }, - ...initialState - }; + // Create an inject reducer function + // This function adds the async reducer, and creates a new combined reducer + store.injectReducer = (key, asyncReducer) => { + store.asyncReducers[key] = asyncReducer + store.replaceReducer(createReducer(store.asyncReducers)); + } - let enhancers; - if (window.__REDUX_DEVTOOLS_EXTENSION__) { - enhancers = compose( - applyMiddleware(...MIDDLEWARES), - window.__REDUX_DEVTOOLS_EXTENSION__({serialize: true}) - ); - } else { - enhancers = applyMiddleware(...MIDDLEWARES); - } + // Return the modified store + return store; +} - return createStore( - appReducer, - initialState, - enhancers - ); -}; diff --git a/assets/src/scripts/store/site-store.js b/assets/src/scripts/store/site-store.js new file mode 100644 index 0000000000000000000000000000000000000000..9770760c0cc5ab06a280f5c72a6b5f26156cda8e --- /dev/null +++ b/assets/src/scripts/store/site-store.js @@ -0,0 +1,560 @@ + +import find from 'lodash/find'; +import findKey from 'lodash/findKey'; +import last from 'lodash/last'; +import {DateTime} from 'luxon'; + +import {normalize} from '../schema'; +import {calcStartTime, sortedParameters} from '../utils'; + +import {getCurrentParmCd, getCurrentDateRange, hasTimeSeries, getTsRequestKey, getRequestTimeRange, + getCustomTimeRange, getIanaTimeZone} from '../selectors/time-series-selector'; + +import {fetchFloodFeatures, fetchFloodExtent} from '../web-services/flood-data'; +import {getPreviousYearTimeSeries, getTimeSeries, queryWeatherService} from '../web-services/models'; +import {fetchNldiUpstreamSites, fetchNldiDownstreamSites, fetchNldiDownstreamFlow, fetchNldiUpstreamFlow, fetchNldiUpstreamBasin} from '../web-services/nldi-data'; +import {fetchTimeSeries} from '../web-services/observations'; +import {fetchSiteStatistics} from '../web-services/statistics-data'; +import {fetchNetworkSites} from '../networks/network-data'; + +import {floodDataReducer as floodData} from './flood-data-reducer'; +import {floodStateReducer as floodState} from './flood-state-reducer'; +import {nldiDataReducer as nldiData} from './nldi-data-reducer'; +import {observationsDataReducer as observationsData} from './observations-data-reducer'; +import {observationsStateReducer as observationsState} from './observations-state-reducer'; +import {seriesReducer as series} from './series-reducer'; +import {statisticsDataReducer as statisticsData} from './statistics-data-reducer'; +import {timeSeriesStateReducer as timeSeriesState} from './time-series-state-reducer'; +import {uiReducer as ui} from './ui-reducer'; +import {configureReduxStore} from './index'; + +const GAGE_HEIGHT_CD = '00065'; +/* + * Helper functions + */ +const getLatestValue = function(collection, parmCd) { + let parmVar = findKey(collection.variables, (varValue) => { + return varValue.variableCode.value === parmCd; + }); + let parmTimeSeries = findKey(collection.timeSeries, (ts) => { + return ts.variable === parmVar; + }); + let points = parmTimeSeries ? collection.timeSeries[parmTimeSeries].points : []; + return points.length ? last(points).value : null; +}; + + +/* + * @param {Object} timeSeries - keys are time series id + * @param {Object} variables - keys are the variable id + */ +const getCurrentVariableId = function(timeSeries, variables) { + const tsVariablesWithData = Object.values(timeSeries) + .filter((ts) => ts.points.length) + .map((ts) => variables[ts.variable]); + const sortedVarsWithData = sortedParameters(tsVariablesWithData); + if (sortedVarsWithData.length) { + return sortedVarsWithData[0].oid; + } else { + const sortedVars = sortedParameters(Object.values(variables)); + return sortedVars.length ? sortedVars[0].oid : ''; + } +}; + +export const Actions = { + retrieveLocationTimeZone(latitude, longitude) { + return function(dispatch) { + return queryWeatherService(latitude, longitude).then( + resp => { + const tzIANA = resp.properties.timeZone || null; // set to time zone to null if unavailable + dispatch(Actions.setLocationIanaTimeZone(tzIANA)); + }, + () => { + dispatch(Actions.setLocationIanaTimeZone(null)); + } + ); + }; + }, + retrieveTimeSeries(siteno, params=null) { + return function (dispatch, getState) { + const currentState = getState(); + const requestKey = getTsRequestKey('current', 'P7D')(currentState); + dispatch(Actions.addTimeSeriesLoading([requestKey])); + return getTimeSeries({sites: [siteno], params}).then( + series => { + const collection = normalize(series, requestKey); + + // Get the start/end times of this request's range. + const notes = collection.queryInfo[requestKey].notes; + const endTime = notes.requestDT; + const startTime = calcStartTime('P7D', endTime, 'local'); + + // Trigger a call to get last year's data + dispatch(Actions.retrieveCompareTimeSeries(siteno, 'P7D', startTime, endTime)); + + // Update the series data for the 'current' series + dispatch(Actions.addSeriesCollection('current', collection)); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + + // Update the application state + dispatch(Actions.toggleTimeSeries('current', true)); + const variable = getCurrentVariableId(collection.timeSeries || {}, collection.variables || {}); + dispatch(Actions.setCurrentVariable(variable)); + dispatch(Actions.setGageHeight(getLatestValue(collection, GAGE_HEIGHT_CD))); + }, + () => { + dispatch(Actions.resetTimeSeries(getTsRequestKey('current', 'P7D')(currentState))); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + + dispatch(Actions.toggleTimeSeries('current', false)); + } + ); + }; + }, + retrieveCompareTimeSeries(site, period, startTime, endTime) { + return function (dispatch, getState) { + const requestKey = getTsRequestKey('compare', period)(getState()); + dispatch(Actions.addTimeSeriesLoading([requestKey])); + return getPreviousYearTimeSeries({site, startTime, endTime}).then( + series => { + const collection = normalize(series, requestKey); + dispatch(Actions.addSeriesCollection(requestKey, collection)); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + }, + () => { + dispatch(Actions.resetTimeSeries(getTsRequestKey('compare', period)(getState()))); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + } + ); + }; + }, + retrieveMedianStatistics(site) { + return function(dispatch) { + return fetchSiteStatistics({site, statType: 'median'}).then( + stats => { + dispatch(Actions.addMedianStats(stats)); + } + ); + }; + }, + + retrieveCustomTimePeriodTimeSeries(site, parameterCd, period) { + return function(dispatch, getState) { + const state = getState(); + const parmCd = parameterCd; + const requestKey = getTsRequestKey('current', 'custom', parmCd)(state); + dispatch(Actions.setCurrentDateRange('custom')); + dispatch(Actions.addTimeSeriesLoading([requestKey])); + return getTimeSeries({sites: [site], params: [parmCd], period: period}).then( + series => { + const collection = normalize(series, requestKey); + const variables = Object.values(collection.variables); + const variableToDraw = find(variables, v => v.variableCode.value === parameterCd); + dispatch(Actions.setCurrentVariable(variableToDraw.variableCode.variableID)); + dispatch(Actions.addSeriesCollection(requestKey, collection)); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + }, + () => { + console.log(`Unable to fetch data for period ${period} and parameter code ${parmCd}`); + dispatch(Actions.addSeriesCollection(requestKey, {})); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + } + ); + }; + }, + + retrieveCustomTimeSeries(site, startTime, endTime, parmCd) { + return function(dispatch, getState) { + const state = getState(); + const thisParmCd = parmCd ? parmCd : getCurrentParmCd(state); + const requestKey = getTsRequestKey('current', 'custom', thisParmCd)(state); + + dispatch(Actions.setCustomDateRange(startTime, endTime)); + dispatch(Actions.addTimeSeriesLoading([requestKey])); + dispatch(Actions.toggleTimeSeries('median', false)); + return getTimeSeries({ + sites: [site], + params: [thisParmCd], + startDate: startTime, + endDate: endTime + }).then( + series => { + const collection = normalize(series, requestKey); + dispatch(Actions.addSeriesCollection(requestKey, collection)); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + }, + () => { + console.log(`Unable to fetch data for between ${startTime} and ${endTime} and parameter code ${thisParmCd}`); + dispatch(Actions.addSeriesCollection(requestKey, {})); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + } + ); + }; + }, + retrieveExtendedTimeSeries(site, period, paramCd=null) { + return function(dispatch, getState) { + const state = getState(); + const thisParamCd = paramCd ? paramCd : getCurrentParmCd(state); + const requestKey = getTsRequestKey ('current', period, thisParamCd)(state); + dispatch(Actions.setCurrentDateRange(period)); + if (!hasTimeSeries('current', period, thisParamCd)(state)) { + dispatch(Actions.addTimeSeriesLoading([requestKey])); + const endTime = getRequestTimeRange('current', 'P7D')(state).end; + const startTime = calcStartTime(period, endTime); + return getTimeSeries({ + sites: [site], + params: [thisParamCd], + startDate: startTime, + endDate: endTime + }).then( + series => { + const collection = normalize(series, requestKey); + dispatch(Actions.retrieveCompareTimeSeries(site, period, startTime, endTime)); + dispatch(Actions.addSeriesCollection(requestKey, collection)); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + }, + () => { + console.log(`Unable to fetch data for period ${period} and parameter code ${thisParamCd}`); + dispatch(Actions.addSeriesCollection(requestKey, {})); + dispatch(Actions.removeTimeSeriesLoading([requestKey])); + } + ); + } else { + return Promise.resolve({}); + } + }; + }, + retrieveDailyValueData(monitoringLocationId, timeSeriesId) { + return function(dispatch) { + return fetchTimeSeries(monitoringLocationId, timeSeriesId) + .then( + (data) => { + dispatch(Actions.setObservationsTimeSeries(timeSeriesId, data)); + }, + () => { + console.log(`Unable to fetch observations time series for ${timeSeriesId}`); + }); + }; + }, + retrieveFloodData(siteno) { + return function (dispatch) { + const floodFeatures = fetchFloodFeatures(siteno); + const floodExtent = fetchFloodExtent(siteno); + return Promise.all([floodFeatures, floodExtent]).then((data) => { + const [features, extent] = data; + const stages = features.map((feature) => feature.attributes.STAGE).sort(function (a, b) { + return a - b; + }); + dispatch(Actions.setFloodFeatures(stages, stages.length ? extent.extent : {})); + }); + }; + }, + retrieveNldiData(siteno) { + return function (dispatch) { + const upstreamFlow = fetchNldiUpstreamFlow(siteno); + const downstreamFlow = fetchNldiDownstreamFlow(siteno); + const upstreamSites = fetchNldiUpstreamSites(siteno); + const downstreamSites = fetchNldiDownstreamSites(siteno); + const upstreamBasin = fetchNldiUpstreamBasin(siteno); + + return Promise.all([ + upstreamFlow, downstreamFlow, upstreamSites, downstreamSites, upstreamBasin + ]).then(function(data) { + const [upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin] = data; + dispatch(Actions.setNldiFeatures(upStreamLines, downStreamLines, upStreamPoints, downStreamPoints, upstreamBasin)); + }); + }; + }, + retrieveNetworkData(networkCd) { + return function(dispatch) { + const networkSites = fetchNetworkSites(networkCd); + + return Promise.all( [networkSites] + ).then(function(data){ + const [networkSites] = data; + dispatch(Actions.setNetworkFeatures(networkSites)); + }); + }; + }, + updateCurrentVariable(siteno, variableID) { + return function(dispatch, getState) { + dispatch(Actions.setCurrentVariable(variableID)); + const state = getState(); + const currentDateRange = getCurrentDateRange(state); + if (currentDateRange === 'custom') { + const timeRange = getCustomTimeRange(state); + dispatch( + Actions.retrieveCustomTimeSeries(siteno, timeRange.startDT, timeRange.endDT)); + } else { + dispatch(Actions.retrieveExtendedTimeSeries(siteno, currentDateRange)); + } + }; + }, + startTimeSeriesPlay(maxCursorOffset) { + return function (dispatch, getState) { + let state = getState().timeSeriesState; + if (state.cursorOffset == null || state.cursorOffset >= maxCursorOffset) { + dispatch(Actions.setCursorOffset(0)); + } + if (!state.audiblePlayId) { + let play = function () { + let newOffset = getState().timeSeriesState.cursorOffset + 15 * 60 * 1000; + if (newOffset > maxCursorOffset) { + dispatch(Actions.stopTimeSeriesPlay()); + } else { + dispatch(Actions.setCursorOffset(newOffset)); + } + }; + let playId = window.setInterval(play, 10); + dispatch(Actions.timeSeriesPlayOn(playId)); + } + }; + }, + stopTimeSeriesPlay() { + return function(dispatch, getState) { + window.clearInterval(getState().timeSeriesState.audiblePlayId); + dispatch(Actions.timeSeriesPlayStop()); + }; + }, + timeSeriesPlayOn(playId) { + return { + type: 'TIME_SERIES_PLAY_ON', + playId + }; + }, + timeSeriesPlayStop() { + return { + type: 'TIME_SERIES_PLAY_STOP' + }; + }, + addTimeSeriesLoading(tsKeys) { + return { + type: 'TIME_SERIES_LOADING_ADD', + tsKeys + }; + }, + removeTimeSeriesLoading(tsKeys) { + return { + type: 'TIME_SERIES_LOADING_REMOVE', + tsKeys + }; + }, + setFloodFeatures(stages, extent) { + return { + type: 'SET_FLOOD_FEATURES', + stages, + extent + }; + }, + setNldiFeatures(upstreamFlows, downstreamFlows, upstreamSites, downstreamSites, upstreamBasin) { + return { + type: 'SET_NLDI_FEATURES', + upstreamFlows, + downstreamFlows, + upstreamSites, + downstreamSites, + upstreamBasin + }; + }, + setNetworkFeatures(networkSites) { + return { + type: 'SET_NETWORK_FEATURES', + networkSites + }; + }, + setObservationsTimeSeries(timeSeriesId, data) { + return { + type: 'SET_OBSERVATIONS_TIME_SERIES', + timeSeriesId, + data + }; + }, + toggleTimeSeries(key, show) { + return { + type: 'TOGGLE_TIME_SERIES', + key, + show + }; + }, + addSeriesCollection(key, data) { + return { + type: 'ADD_TIME_SERIES_COLLECTION', + key, + data + }; + }, + resetTimeSeries(key) { + return { + type: 'RESET_TIME_SERIES', + key + }; + }, + addMedianStats(data) { + return { + type: 'MEDIAN_STATS_ADD', + data + }; + }, + setCursorOffset(cursorOffset) { + return { + type: 'SET_CURSOR_OFFSET', + cursorOffset + }; + }, + resizeUI(windowWidth, width) { + return { + type: 'RESIZE_UI', + windowWidth, + width + }; + }, + setHydrographBrushOffset(hydrographBrushOffset) { + return { + type: 'SET_HYDROGRAPH_BRUSH_OFFSET', + hydrographBrushOffset + }; + }, + clearHydrographBrushOffset() { + return { + type: 'CLEAR_HYDROGRAPH_BRUSH_OFFSET' + }; + }, + setCurrentVariable(variableID) { + return { + type: 'SET_CURRENT_VARIABLE', + variableID + }; + }, + setCurrentMethodID(methodID) { + return { + type: 'SET_CURRENT_METHOD_ID', + methodID + }; + }, + setCurrentDateRange(period) { + return { + type: 'SET_CURRENT_DATE_RANGE', + period + }; + }, + setCustomDateRange(startTime, endTime) { + return { + type: 'SET_CUSTOM_DATE_RANGE', + startTime, + endTime + }; + }, + retrieveUserRequestedDataForDateRange(siteno, startTimeStr, endTimeStr) { + return function(dispatch, getState) { + const state = getState(); + const locationIanaTimeZone = getIanaTimeZone(state); + const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis(); + const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis(); + return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime)); + }; + }, + retrieveDataForDateRange(siteno, startTimeStr, endTimeStr, parmCd) { + return function(dispatch, getState) { + const state = getState(); + const locationIanaTimeZone = getIanaTimeZone(state); + const startTime = new DateTime.fromISO(startTimeStr,{zone: locationIanaTimeZone}).toMillis(); + const endTime = new DateTime.fromISO(endTimeStr, {zone: locationIanaTimeZone}).toMillis(); + return dispatch(Actions.retrieveCustomTimeSeries(siteno, startTime, endTime, parmCd)); + }; + }, + setGageHeightFromStageIndex(index) { + return function(dispatch, getState) { + const stages = getState().floodData.stages; + if (index > -1 && index < stages.length) { + dispatch(Actions.setGageHeight(stages[index])); + } + }; + }, + setGageHeight(gageHeight) { + return { + type: 'SET_GAGE_HEIGHT', + gageHeight + }; + }, + setLocationIanaTimeZone(ianaTimeZone) { + return { + type: 'LOCATION_IANA_TIME_ZONE_SET', + ianaTimeZone + }; + }, + setCurrentObservationsTimeSeriesId(timeSeriesId) { + return { + type: 'SET_CURRENT_TIME_SERIES_ID', + timeSeriesId + }; + }, + /* + * @param {Number} cursorOffset - difference in epoch time from the start of the graph to the position of of the cursor + */ + setDailyValueCursorOffset(cursorOffset) { + return { + type: 'SET_DAILY_VALUE_CURSOR_OFFSET', + cursorOffset + }; + } +}; + +const reducers = { + series, + observationsData, + statisticsData, + floodData, + nldiData, + timeSeriesState, + observationsState, + floodState, + ui +}; + +export const configureStore = function(initialState){ + + initialState = { + series: {}, + observationsData: {}, + floodData: { + stages: [], + extent: {} + }, + nldiData: { + upstreamFlows: [], + downstreamFlows: [], + upstreamSites: [], + downstreamSites: [], + upstreamBasin: [] + }, + statisticsData: {}, + + timeSeriesState: { + showSeries: { + current: true, + compare: false, + median: false + }, + currentDateRange: 'P7D', + customTimeRange: null, + currentVariableID: null, + cursorOffset: null, + audiblePlayId: null, + loadingTSKeys: [], + hydrographBrushOffset: null + }, + observationsState: { + cursorOffset: null + }, + floodState: { + gageHeight: null + }, + ui: { + windowWidth: 1024, + width: 800 + }, + ...initialState + }; + + return configureReduxStore(initialState, reducers); +}; diff --git a/assets/src/scripts/store/index.spec.js b/assets/src/scripts/store/site-store.spec.js similarity index 99% rename from assets/src/scripts/store/index.spec.js rename to assets/src/scripts/store/site-store.spec.js index 0f2b87786663f69dc6b9d89ec90c74a166a8405b..49610eaa2a9bf27f0be6943a72ad0e455bce3a08 100644 --- a/assets/src/scripts/store/index.spec.js +++ b/assets/src/scripts/store/site-store.spec.js @@ -1,4 +1,4 @@ -import { Actions, configureStore } from './index'; +import { Actions, configureStore } from './site-store'; import { MOCK_RDB as MOCK_STATS_DATA } from '../web-services/statistics-data.spec.js'; describe('Redux store', () => { diff --git a/assets/src/scripts/url-params.spec.js b/assets/src/scripts/url-params.spec.js index df8f0853ec4f4d1c74dcf7fd5fe3c717d2d4dfc1..464ab5a7d00451baafaee60e635cd2c9c75a34e2 100644 --- a/assets/src/scripts/url-params.spec.js +++ b/assets/src/scripts/url-params.spec.js @@ -1,5 +1,5 @@ -import {configureStore, Actions} from './store'; +import {configureStore, Actions} from './store/site-store'; import {getParamString, renderTimeSeriesUrlParams} from './url-params'; describe('url-params module', () => { diff --git a/assets/src/styles/main.scss b/assets/src/styles/main.scss index b56b86d86f99098dc27f1eebd350e7f12a7f47f3..49b052ea97a05f86ef895e1406b523e038bf40ce 100644 --- a/assets/src/styles/main.scss +++ b/assets/src/styles/main.scss @@ -1,6 +1,5 @@ @import 'leaflet'; -@import '../../node_modules/markercluster-iow/dist/_MarkerCluster.scss'; -@import '../../node_modules/markercluster-iow/dist/_MarkerCluster.Default.scss'; + @import './common'; $fa-font-path: './fonts' !default; @@ -32,7 +31,7 @@ $fa-font-path: './fonts' !default; } &[data-component='network-map']{ - @import './networks/networkmap'; + @import 'network'; margin-bottom: 1em; } } diff --git a/assets/src/styles/network.scss b/assets/src/styles/network.scss new file mode 100644 index 0000000000000000000000000000000000000000..13f75da41140b88f6072366b48c124e713419342 --- /dev/null +++ b/assets/src/styles/network.scss @@ -0,0 +1,147 @@ +@import 'leaflet'; +@import 'common'; + +$fa-font-path: './fonts' !default; +@import '../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome'; +@import '../../node_modules/@fortawesome/fontawesome-free/scss/brands'; +@import '../../node_modules/@fortawesome/fontawesome-free/scss/solid'; + +@import 'partials/script'; +@import 'partials/slider'; +@import 'partials/social-share'; +@import 'partials/tooltip'; + +@import '../../node_modules/markercluster-iow/dist/MarkerCluster'; +@import '../../node_modules/markercluster-iow/dist/MarkerCluster.Default'; + +.wdfn-component { + &[data-component='network-map'] { + #network-map { + height: 350px; + margin: 1em 0; + + .legend { + padding: 2px; + background-color: white; + + .legend-expand-container { + font-size: 14px; + font-weight: bold; + + span { + padding-right: 2px; + } + + .legend-expand { + width: auto; + float: right; + padding: 0; + margin: 0; + color: black; + border: none; + border-radius: 0; + box-shadow: none; + + i { + padding: 2px; + background-color: color('base-lighter'); + } + } + } + + .legend-list-container { + #network-legend-list { + span { + margin-left: 2px; + } + + img { + padding-top: 2px; + width: 15px; + } + } + + #fim-legend-list { + span { + margin-left: 2px; + } + + img { + padding-top: 2px; + width: 15px; + height: 13px; + } + } + } + + } + + .leaflet-control-layers-selector { + left: 0; + position: relative; + display: inline; + height: auto; + width: auto; + outline-style: none; + } + + label { + margin: auto; + } + } + margin-bottom: 1em; + } +} + +/* This fixes an issue with Firefox not hiding long divs. I opened https://github.com/uswds/uswds/issues/2380 . + * If it gets addressed this can be removed. + */ +.usa-sr-only { + overflow: hidden; +} + +body { + #main-content { + margin-top: 1em; + margin-bottom: 1em; + padding: 0 1em; + } + + .content-container { + margin-left: auto; + margin-right: auto; + @include u-maxw('desktop'); + } + + #wdfn-alert-banner input:checked ~ * { + display: none; + } + + #wdfn-alert-banner { + background-color: color('accent-cool-lighter'); + + .wdfn-alert-body { + padding-top: 0.5em; + padding-bottom: 0.5em; + padding-right: 1em; + padding-left: 1em; + margin-top: 0; + + .close { + float: right; + cursor: pointer; + margin-top: -5px; + margin-right: -10px; + } + + .div { + font-size: 0.9em; + @include at-media('tablet') { + font-size: 1em; + } + padding-left: 1em; + } + } + + } +} diff --git a/assets/src/styles/networks/_networkmap.scss b/assets/src/styles/networks/_networkmap.scss deleted file mode 100644 index ba284668bd4dd391896745e33c3c3ea95a9a79e1..0000000000000000000000000000000000000000 --- a/assets/src/styles/networks/_networkmap.scss +++ /dev/null @@ -1,73 +0,0 @@ -#network-map { - height: 350px; - margin: 1em 0; - - .legend { - padding: 2px; - background-color: white; - - .legend-expand-container { - font-size: 14px; - font-weight: bold; - - span { - padding-right: 2px; - } - - .legend-expand { - width: auto; - float: right; - padding: 0; - margin: 0; - color: black; - border: none; - border-radius: 0; - box-shadow: none; - - i { - padding: 2px; - background-color: color('base-lighter'); - } - } - } - - .legend-list-container { - #network-legend-list { - span { - margin-left: 2px; - } - - img { - padding-top: 2px; - width: 15px; - } - } - - #fim-legend-list { - span { - margin-left: 2px; - } - - img { - padding-top: 2px; - width: 15px; - height: 13px; - } - } - } - - } - - .leaflet-control-layers-selector { - left: 0; - position: relative; - display: inline; - height: auto; - width: auto; - outline-style: none; - } - - label { - margin: auto; - } -} \ No newline at end of file diff --git a/graph-server/Dockerfile b/graph-server/Dockerfile index 83a837f1631c9fb694e8be30025286cecbb95ffb..ea1837d4ce9c1c4e8038c3b3d13027db39d40f03 100644 --- a/graph-server/Dockerfile +++ b/graph-server/Dockerfile @@ -34,4 +34,4 @@ USER grapher EXPOSE 2929 -CMD DEBUG=${DEBUG} STATIC_ROOT=${STATIC_URL} OGC_SITE_ENDPOINT=${OGC_SITE_ENDPOINT} node src/index.js \ No newline at end of file +CMD DEBUG=${DEBUG} STATIC_ROOT=${STATIC_URL} OGC_SITE_ENDPOINT=${OGC_SITE_ENDPOINT} node src/site-store.js \ No newline at end of file diff --git a/graph-server/README.md b/graph-server/README.md index 73c6e1dbf1f5bc22445042277e6a1bf24aea011d..0c9437bd3ad5f1737e307059dc0cd736754d0b24 100644 --- a/graph-server/README.md +++ b/graph-server/README.md @@ -19,7 +19,7 @@ one representing the time period specified and the second representing the same ## Running the server -The entrypoint is `src/index.js`, which accepts the following environment +The entrypoint is `src/site-store.js`, which accepts the following environment variables as arguments: - NODE_PORT: Port to run http server on. Default 2929. @@ -30,7 +30,7 @@ variables as arguments: For example: ```bash -% NODE_PORT=80 node src/index.js +% NODE_PORT=80 node src/site-store.js ``` Alternatively, if you want to use defaults as well as add DEBUG just use diff --git a/wdfn-server/waterdata/templates/base_network.html b/wdfn-server/waterdata/templates/base_network.html new file mode 100644 index 0000000000000000000000000000000000000000..3731b8242c43e230602080a6a08a9f27a8d82aa6 --- /dev/null +++ b/wdfn-server/waterdata/templates/base_network.html @@ -0,0 +1,21 @@ +{% extends 'base_plain_network.html' %} + +{% block body %} + <section role="banner"> + {% include 'partials/header.html' %} + <header id="wdfn-alert-banner"> + <input type="checkbox" id="wdfn-alert-dismiss" hidden/> + <div class="wdfn-alert-body content-container"> + <label title="Close" class="close" for="wdfn-alert-dismiss"><i class="fas fa-times"></i></label> + <div> + Beta release. Use <a class="usa-link" target="_blank" href="https://water.usgs.gov/contact/gsanswers?pemail=gs-w_water_data_for_the_nation&subject=Water%20Data%20for%20the%20Nation%20Feedback&viewnote=%3CH1%3EUSGS+WDFN+TNG+Feedback%3C/H1%3E">this form</a> to provide feedback. + </div> + </div> + </header> + </section> + <main id="main-content" class="grid-container content-container usa-prose"> + {% block content %}{% endblock %} + </main> + {% include 'partials/footer.html' %} + {% block extra_body %}{% endblock %} +{% endblock %} diff --git a/wdfn-server/waterdata/templates/base_plain_network.html b/wdfn-server/waterdata/templates/base_plain_network.html new file mode 100644 index 0000000000000000000000000000000000000000..be4843f4ff04b6c768056da9d275265876c3b80c --- /dev/null +++ b/wdfn-server/waterdata/templates/base_plain_network.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="format-detection" content="telephone=no"> + {% block extra_head_tags %}{% endblock %} + + {% include 'partials/google_analytics.html' %} + + <link rel="shortcut icon" type="image/ico" href="{{ 'img/usgs_favicon.ico' | asset_url }}"> + <link rel="stylesheet" href="{{ 'network.css' | asset_url }}"> + <script type="text/javascript"> + var CONFIG = { + 'SERVICE_ROOT': '{{ config.SERVICE_ROOT }}/nwis', + 'PAST_SERVICE_ROOT': '{{ config.PAST_SERVICE_ROOT }}/nwis', + 'OBSERVATIONS_ENDPOINT': '{{ config.OBSERVATIONS_ENDPOINT }}', + 'HYDRO_ENDPOINT': '{{ config.HYDRO_ENDPOINT }}', + 'FIM_GIS_ENDPOINT': '{{ config.FIM_GIS_ENDPOINT }}', + 'FIM_ENDPOINT': '{{ config.FIM_ENDPOINT }}', + 'STATIC_URL': '{{ config.STATIC_ROOT }}', + 'TIMESERIES_AUDIO_ENABLED': '{{ config.TIMESERIES_AUDIO_ENABLED}}' === 'True' ? true : false, + 'MULTIPLE_TIME_SERIES_METADATA_SELECTOR_ENABLED': '{{ config.MULTIPLE_TIME_SERIES_METADATA_SELECTOR_ENABLED }}' === 'True' ? true : false, + 'WEATHER_SERVICE_ROOT': '{{ config.WEATHER_SERVICE_ROOT }}', + 'CITIES_ENDPOINT': '{{ config.CITIES_ENDPOINT }}', + 'NLDI_SERVICES_ENDPOINT': '{{ config.NLDI_SERVICES_ENDPOINT }}', + 'NLDI_SERVICES_DISTANCE': '{{ config.NLDI_SERVICES_DISTANCE }}', + 'NETWORK_ENDPOINT': '{{ config.NETWORK_ENDPOINT }}' + }; + </script> + <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script> + <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script> + <script type="text/javascript"> + if (!!document.documentMode) { + document.write('<script src="{{ "scripts/date-time-format-timezone-complete-min.js" | asset_url }}"><\/script>'); + } + </script> + + + <script async src="{{ 'network-bundle.js' | asset_url }}"></script> + {% block page_script %}{% endblock %} + <!-- Google Tag Manager --><script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-TKQR8KP');</script> + <title> + {% block title %}{% if page_title %}{{ page_title }} - {% endif %}USGS Water Data for the Nation{% endblock %} + </title> + </head> + <body {% if body_id %}id="{{ body_id }}"{% endif %}> + <script>document.body.className += ' js';</script> + {% block body %}{% endblock %} + </body> + {% if config.LIVE_RELOAD_PATH %} + <script type="text/javascript"> + // <![CDATA[ <-- For SVG support + if ('WebSocket' in window) { + (function() { + function refreshCSS() { + var sheets = [].slice.call(document.getElementsByTagName("link")); + var head = document.getElementsByTagName("head")[0]; + for (var i = 0; i < sheets.length; ++i) { + var elem = sheets[i]; + head.removeChild(elem); + var rel = elem.rel; + if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") { + var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, ''); + elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf()); + } + head.appendChild(elem); + } + } + var socket = new WebSocket('{{ config.LIVE_RELOAD_PATH }}'); + socket.onmessage = function(msg) { + if (msg.data == 'reload') window.location.reload(); + else if (msg.data == 'refreshcss') refreshCSS(); + }; + console.log('Live reload enabled.'); + })(); + } + // ]]> + </script> + {% endif %} + {% block extra_html %}{% endblock %} + <!-- Google Tag Manager (noscript) --><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TKQR8KP" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> +</html> diff --git a/wdfn-server/waterdata/templates/networks.html b/wdfn-server/waterdata/templates/networks.html index 499fd7d599c532cd8813d4c1907c3cf8cf5c115f..0a886dfc5e12415b4bba1a01ce1ba3015e7502ff 100644 --- a/wdfn-server/waterdata/templates/networks.html +++ b/wdfn-server/waterdata/templates/networks.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'base_network.html' %} {% if network_cd %}{% set page_title = 'Networks' %}{% endif %}