diff --git a/assets/src/scripts/index.spec.js b/assets/src/scripts/index.spec.js
index f4ae9d5d18e046116fa604f790e7ee019ae807f4..ad4b8fc6461ef279ca6ab2ee5e0c8dced2c0f443 100644
--- a/assets/src/scripts/index.spec.js
+++ b/assets/src/scripts/index.spec.js
@@ -44,20 +44,20 @@ import './monitoring-location/components/daily-value-hydrograph/tooltip.spec';
 import './monitoring-location/components/embed.spec';
 
 import './monitoring-location/components/hydrograph/audible.spec';
-import './monitoring-location/components/hydrograph/cursor.spec';
+import './monitoring-location/components/hydrograph/selectors/cursor.spec';
 import './monitoring-location/components/hydrograph/date-controls.spec';
-import './monitoring-location/components/hydrograph/domain.spec';
+import './monitoring-location/components/hydrograph/selectors/domain.spec';
 import './monitoring-location/components/hydrograph/drawing-data.spec';
 import './monitoring-location/components/hydrograph/data-table.spec';
 import './monitoring-location/components/hydrograph/graph-brush.spec';
 import './monitoring-location/components/hydrograph/graph-controls.spec';
 import './monitoring-location/components/hydrograph/index.spec';
-import './monitoring-location/components/hydrograph/layout.spec';
+import './monitoring-location/components/hydrograph/selectors/layout.spec';
 import './monitoring-location/components/hydrograph/legend.spec';
 import './monitoring-location/components/hydrograph/method-picker.spec';
 import './monitoring-location/components/hydrograph/parameters.spec';
-import './monitoring-location/components/hydrograph/scales.spec';
-import './monitoring-location/components/hydrograph/time-series.spec';
+import './monitoring-location/components/hydrograph/selectors/scales.spec';
+import './monitoring-location/components/hydrograph/selectors/time-series-data.spec';
 import './monitoring-location/components/hydrograph/time-series-graph.spec';
 import './monitoring-location/components/hydrograph/tooltip.spec';
 
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/audible.js b/assets/src/scripts/monitoring-location/components/hydrograph/audible.js
index c97c016a68bcdc39c5c2668854ba7ed7a9f9453d..42d83036dba295287113bc6fe19fb6a18555677b 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/audible.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/audible.js
@@ -1,3 +1,8 @@
+/*
+ * Note the audible interface is not currently enabled and will likely need a major implementation
+ * The current patterns of putting the selectors in a separate module from rendering code and
+ * updating the selectors to use is* or get* pattern.
+ */
 import {scaleLinear} from 'd3-scale';
 import memoize from 'fast-memoize';
 import {createSelector, createStructuredSelector} from 'reselect';
@@ -7,7 +12,6 @@ import {link} from '../../../lib/d3-redux';
 import {getTimeSeries} from '../../selectors/time-series-selector';
 import {Actions} from '../../store/instantaneous-value-time-series-state';
 
-import {tsCursorPointsSelector} from './cursor';
 import {getMainXScale, getMainYScale} from './scales';
 
 
@@ -72,48 +76,10 @@ export const updateSound = function ({enabled, points}) {
     }
 };
 
-const audibleInterfaceOnSelector = state => state.ivTimeSeriesState.audiblePlayId !== null;
-
-const audibleScaleSelector = createSelector(
-    getMainYScale,
-    (yScale) => {
-        return scaleLinear()
-            .domain(yScale.domain())
-            .range([80, 1500]);
-    }
-);
-
-const audiblePointsSelector = createSelector(
-    getTimeSeries,
-    tsCursorPointsSelector('current'),
-    tsCursorPointsSelector('compare'),
-    audibleScaleSelector,
-    (allTimeSeries, currentPoints, comparePoints, yScale) => {
-        // Set null points for all time series, so we can turn audio for those
-        // points off when toggling to other time series.
-        let points = Object.keys(allTimeSeries).reduce((points, tsID) => {
-            points[tsID] = null;
-            return points;
-        }, {});
-
-        // Get the pitches for the current-year points
-        points = Object.keys(currentPoints).reduce((points, tsID) => {
-            const pt = currentPoints[tsID];
-            points[tsID] = yScale(pt.value);
-            return points;
-        }, points);
-
-        // Get the pitches for the compare-year points
-        return Object.keys(comparePoints).reduce((points, tsID) => {
-            const pt = comparePoints[tsID];
-            points[tsID] = yScale(pt.value);
-            return points;
-        }, points);
-    }
-);
-
+/*
+ * Renders the audible control if enabled.
+ */
 export const audibleUI = function (elem, store) {
-    // Only enable the audio interface on dev tiers.
     if (!config.TIMESERIES_AUDIO_ENABLED) {
         return;
     }
@@ -142,7 +108,7 @@ export const audibleUI = function (elem, store) {
             elem.select('i')
                 .classed('fa-play', !audibleOn)
                 .classed('fa-stop', audibleOn);
-        }, audibleInterfaceOnSelector))
+        }, isAudiblePlaying))
         .call(link(store, function(elem, xScale) {
             const domain = xScale.domain();
             elem.attr('data-max-offset', domain[1] - domain[0]);
@@ -157,15 +123,14 @@ export const audibleUI = function (elem, store) {
 
     // Listen for focus changes, and play back the audio representation of
     // the selected points.
-    // TODO: Handle more than just the first time series of each tsKey. This can
-    // piggyback on work to support multiple tooltip selections.
+    // TODO: This does not correctly handle parameter codes with multiple time series.
     elem.call(link(store,function (elem, {enabled, points}) {
         updateSound({
             points,
             enabled
         });
     }, createStructuredSelector({
-        enabled: audibleInterfaceOnSelector,
-        points: audiblePointsSelector
+        enabled: isAudiblePlaying,
+        points: getAudiblePoints
     })));
 };
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/audible.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/audible.spec.js
index 9a19854e8528c8984bdee2fc0f4327dd24dd1bb4..7ffbd7a9c8f8a7caa635035709363d63148c06d0 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/audible.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/audible.spec.js
@@ -88,8 +88,4 @@ describe('monitoring-location/components/hydrograph/audible audibleUI', () => {
             done();
         });
     });
-
-
-
-
 });
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/cursor.js b/assets/src/scripts/monitoring-location/components/hydrograph/cursor.js
deleted file mode 100644
index ac880b62573018ed749c5add33e129ff54769003..0000000000000000000000000000000000000000
--- a/assets/src/scripts/monitoring-location/components/hydrograph/cursor.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import memoize from 'fast-memoize';
-import {createSelector} from 'reselect';
-
-import {getCurrentMethodID} from '../../selectors/time-series-selector';
-import {getNearestTime} from '../../../utils';
-
-import {currentVariablePointsByTsIdSelector} from './drawing-data';
-import {getMainXScale} from './scales';
-import {isVisibleSelector} from './time-series';
-
-
-export const cursorOffsetSelector = createSelector(
-    getMainXScale('current'),
-    state => state.ivTimeSeriesState.ivGraphCursorOffset,
-    (xScale, cursorOffset) => {
-        // If cursorOffset is false, don't show it
-        if (cursorOffset === false) {
-            return null;
-        // If cursorOffset is otherwise unset, default to the last offset
-        } else if (!cursorOffset) {
-            const domain = xScale.domain();
-            return domain[1] - domain[0];
-        } else {
-            return cursorOffset;
-        }
-    }
-);
-
-/**
- * Returns a selector that, for a given tsKey:
- * Returns the time corresponding to the current cursor offset.
- * @param  {String} tsKey
- * @return {Date}
- */
-export const cursorTimeSelector = memoize(tsKey => createSelector(
-    cursorOffsetSelector,
-    getMainXScale(tsKey),
-    (cursorOffset, xScale) => {
-        return cursorOffset ? new Date(xScale.domain()[0] + cursorOffset) : null;
-    }
-));
-
-/*
- * Returns a function that the time series data point nearest the tooltip focus time for the current time series
- * with the current variable and current method
- * @param {Object} state - Redux store
- * @param String} tsKey - Time series key
- * @return {Object}
- */
-export const tsCursorPointsSelector = memoize(tsKey => createSelector(
-    currentVariablePointsByTsIdSelector(tsKey),
-    getCurrentMethodID,
-    cursorTimeSelector(tsKey),
-    isVisibleSelector(tsKey),
-    (timeSeries, currentMethodId, cursorTime, isVisible) => {
-        if (!cursorTime || !isVisible) {
-            return {};
-        }
-        return Object.keys(timeSeries).reduce((data, tsId) => {
-            if (timeSeries[tsId].length && parseInt(tsId.split(':')[0]) === currentMethodId) {
-                const datum = getNearestTime(timeSeries[tsId], cursorTime);
-                data[tsId] = {
-                    ...datum,
-                    tsKey: tsKey
-                };
-            }
-            return data;
-        }, {});
-    }));
\ No newline at end of file
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js b/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js
index fcccd62b7ef73ae16cdb1041e210b3bf15737fad..cc6047de0b9538029b3ba128144f38fe04255487 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/date-controls.js
@@ -3,6 +3,7 @@ import {createStructuredSelector} from 'reselect';
 
 import {link} from '../../../lib/d3-redux';
 import {drawLoadingIndicator} from '../../../d3-rendering/loading-indicator';
+
 import {
     isLoadingTS,
     hasAnyTimeSeries,
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/graph-brush.js b/assets/src/scripts/monitoring-location/components/hydrograph/graph-brush.js
index c51939540f86e7ebd1c691dff016fa783bde50a2..7845a28c8ae600098f21b798177c3306be7d4616 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/graph-brush.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/graph-brush.js
@@ -6,12 +6,12 @@ import {appendXAxis} from '../../../d3-rendering/axes';
 import {link} from '../../../lib/d3-redux';
 import {Actions} from '../../store/instantaneous-value-time-series-state';
 
-import {getBrushXAxis} from './axes';
-import {currentVariableLineSegmentsSelector} from './drawing-data';
-import {getBrushLayout} from './layout';
-import {getBrushXScale, getBrushYScale} from './scales';
-import {isVisibleSelector} from './time-series';
-import {drawDataLines} from './time-series-data';
+import {getBrushXAxis} from './selectors/axes';
+import {getCurrentVariableLineSegments} from './drawing-data';
+import {getBrushLayout} from './selectors/layout';
+import {getBrushXScale, getBrushYScale} from './selectors/scales';
+import {isVisibleSelector} from './selectors/time-series-data';
+import {drawDataLines} from './time-series-lines';
 
 export const drawGraphBrush = function(container, store) {
 
@@ -53,7 +53,7 @@ export const drawGraphBrush = function(container, store) {
                 })))
                 .call(link(store, drawDataLines, createStructuredSelector({
                     visible: isVisibleSelector('current'),
-                    tsLinesMap: currentVariableLineSegmentsSelector('current'),
+                    tsLinesMap: getCurrentVariableLineSegments('current'),
                     xScale: getBrushXScale('current'),
                     yScale: getBrushYScale,
                     tsKey: () => 'current',
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
index b86f3c91e65701f22e55a9a7b6a8f3d5c11209ee..d9ca33d28617417a8f60c9284addbd4ec8c0b26d 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/graph-controls.js
@@ -1,11 +1,12 @@
 
 import {link} from '../../../lib/d3-redux';
+
 import {getCurrentVariableMedianStatistics} from '../../selectors/median-statistics-selector';
 import {getCurrentVariableTimeSeries} from '../../selectors/time-series-selector';
 import {Actions} from '../../store/instantaneous-value-time-series-state';
 
 import {audibleUI} from './audible';
-import {isVisibleSelector} from './time-series';
+import {isVisibleSelector} from './selectors/time-series-data';
 
 /*
  * Create the show audible toggle, last year toggle, and median toggle for the time series graph.
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/index.js b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
index bd7355600a1723361e21b3f1afbabba4c5c0f51a..8c23fe1696ae8a8ef58bd0ef54f6fcba503a9a52 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/index.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
@@ -19,14 +19,15 @@ import {renderTimeSeriesUrlParams} from '../../url-params';
 
 import {drawDateRangeControls} from './date-controls';
 import {drawDataTable} from './data-table';
-import {lineSegmentsByParmCdSelector} from './drawing-data';
 import {drawGraphBrush} from './graph-brush';
 import {drawGraphControls} from './graph-controls';
-import {SPARK_LINE_DIM}  from './layout';
+import {SPARK_LINE_DIM}  from './selectors/layout';
 import {drawTimeSeriesLegend} from './legend';
 import {drawMethodPicker} from './method-picker';
-import {plotSeriesSelectTable, availableTimeSeriesSelector} from './parameters';
-import {timeSeriesScalesByParmCdSelector} from './scales';
+import {plotSeriesSelectTable} from './parameters';
+import {getLineSegmentsByParmCd} from './selectors/drawing-data';
+import {getAvailableParameterCodes} from './selectors/parameter-data';
+import {getTimeSeriesScalesByParmCd} from './selectors/scales';
 import {drawTimeSeriesGraph} from './time-series-graph';
 import {drawTooltipCursorSlider} from './tooltip';
 
@@ -165,9 +166,9 @@ export const attachToNode = function (store,
                 nodeElem.select('.select-time-series-container')
                     .call(link(store, plotSeriesSelectTable, createStructuredSelector({
                         siteno: () => siteno,
-                        availableTimeSeries: availableTimeSeriesSelector,
-                        lineSegmentsByParmCd: lineSegmentsByParmCdSelector('current', 'P7D'),
-                        timeSeriesScalesByParmCd: timeSeriesScalesByParmCdSelector('current', 'P7D', SPARK_LINE_DIM)
+                        availableParameterCodes: getAvailableParameterCodes,
+                        lineSegmentsByParmCd: getLineSegmentsByParmCd('current', 'P7D'),
+                        timeSeriesScalesByParmCd: getTimeSeriesScalesByParmCd('current', 'P7D', SPARK_LINE_DIM)
                     }), store));
 
 
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/legend.js b/assets/src/scripts/monitoring-location/components/hydrograph/legend.js
index ee35e7fffd9f8c0e81ead8a1aa1b7969d7510841..1718473463ea45815172fdd76c0dacb8458d7210 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/legend.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/legend.js
@@ -1,183 +1,17 @@
-// functions to facilitate legend creation for a d3 plot
-import {set} from 'd3-collection';
-import memoize from 'fast-memoize';
-import {createSelector, createStructuredSelector} from 'reselect';
+import {createStructuredSelector} from 'reselect';
 
 import {drawSimpleLegend} from '../../../d3-rendering/legend';
-import {defineLineMarker, defineTextOnlyMarker, defineRectangleMarker} from '../../../d3-rendering/markers';
 import {link} from '../../../lib/d3-redux';
 
-import {getWaterwatchFloodLevels, waterwatchVisible} from '../../selectors/flood-data-selector';
-import {getCurrentVariableMedianMetadata} from '../../selectors/median-statistics-selector';
-
-import {currentVariableLineSegmentsSelector, HASH_ID, MASK_DESC} from './drawing-data';
-import {getMainLayout} from './layout';
-
-
-const TS_LABEL = {
-    'current': 'Current: ',
-    'compare': 'Last year: ',
-    'median': 'Median: '
-};
-
-
-const tsMaskMarkers = function(tsKey, masks) {
-    return Array.from(masks.values()).map((mask) => {
-        const maskName = MASK_DESC[mask];
-        const tsClass = `${maskName.replace(' ', '-').toLowerCase()}-mask`;
-        const fill = `url(#${HASH_ID[tsKey]})`;
-        return defineRectangleMarker(null, `mask ${tsClass}`, maskName, fill);
-    });
-};
-
-const tsLineMarkers = function(tsKey, lineClasses) {
-    let result = [];
-
-    if (lineClasses.default) {
-        result.push(defineLineMarker(null, `line-segment ts-${tsKey}`, 'Provisional'));
-    }
-    if (lineClasses.approved) {
-        result.push(defineLineMarker(null, `line-segment approved ts-${tsKey}`, 'Approved'));
-    }
-    if (lineClasses.estimated) {
-        result.push(defineLineMarker(null, `line-segment estimated ts-${tsKey}`, 'Estimated'));
-    }
-    return result;
-};
-
-/**
- * create elements for the legend in the svg
- *
- * @param {Object} displayItems - Object containing keys for each ts. The current and compare will contain an
- *                 object that has a masks property containing the Set of masks that are currently displayed.
- *                 The median property will contain the metadata for the median statistics
- * @return {Object} - Each key represents a ts and contains an array of markers to show.
- */
-const createLegendMarkers = function(displayItems) {
-    const legendMarkers = [];
-
-    if (displayItems.current) {
-        const currentMarkers = [
-            ...tsLineMarkers('current', displayItems.current),
-            ...tsMaskMarkers('current', displayItems.current.dataMasks)
-        ];
-        if (currentMarkers.length) {
-            legendMarkers.push([
-                defineTextOnlyMarker(TS_LABEL.current, null, 'ts-legend-current-text'),
-                ...currentMarkers
-            ]);
-        }
-    }
-    if (displayItems.compare) {
-        const compareMarkers = [
-            ...tsLineMarkers('compare', displayItems.compare),
-            ...tsMaskMarkers('compare', displayItems.compare.dataMasks)
-        ];
-        if (compareMarkers.length) {
-            legendMarkers.push([
-                defineTextOnlyMarker(TS_LABEL.compare, null, 'ts-legend-compare-text'),
-                ...compareMarkers
-            ]);
-        }
-    }
-
-    if (displayItems.median) {
-        const medians = Object.values(displayItems.median);
-        for (let index = 0; index < medians.length; index++) {
-            const stats = medians[index];
-            // Get the unique non-null years, in chronological order
-            const years = [];
-            if (stats.beginYear) {
-                years.push(stats.beginYear);
-            }
-            if (stats.endYear && stats.beginYear !== stats.endYear) {
-                years.push(stats.endYear);
-            }
-            const dateText = years.join(' - ');
-
-            const descriptionText = stats.methodDescription ? `${stats.methodDescription} ` : '';
-            const classes = `median-data-series median-step median-step-${index % 6}`;
-            const label = `${descriptionText}${dateText}`;
-
-            legendMarkers.push([
-                defineTextOnlyMarker(TS_LABEL.median),
-                defineLineMarker(null, classes, label)]);
-        }
-    }
-
-    if (displayItems.floodLevels) {
-        const floodLevels = displayItems.floodLevels;
-        const keys = ['actionStage', 'floodStage', 'moderateFloodStage', 'majorFloodStage'];
-        const labels = ['Action Stage: ', 'Flood Stage: ', 'Moderate Flood Stage: ', 'Major Flood Stage: '];
-        const wwSeriesClass = 'waterwatch-data-series';
-        const classes = ['action-stage', 'flood-stage', 'moderate-flood-stage', 'major-flood-stage'];
-
-        for (let index = 0; index < keys.length; index++) {
-            legendMarkers.push([
-                defineTextOnlyMarker(labels[index]),
-                defineLineMarker(null, `${wwSeriesClass} ${classes[index]}`,
-                    `${floodLevels[keys[index]]} ft`)]);
-        }
-    }
-
-    return legendMarkers;
-};
-
-
-const uniqueClassesSelector = memoize(tsKey => createSelector(
-    currentVariableLineSegmentsSelector(tsKey),
-    (tsLineSegments) => {
-        let classes = [].concat(...Object.values(tsLineSegments)).map((line) => line.classes);
-        return {
-            default: classes.some((cls) => !cls.approved && !cls.estimated && !cls.dataMask),
-            approved: classes.some((cls) => cls.approved),
-            estimated: classes.some((cls) => cls.estimated),
-            dataMasks: set(classes.map((cls) => cls.dataMask).filter((mask) => {
-                return mask;
-            }))
-        };
-    }
-));
-
-
-
-/**
- * Select attributes from the state useful for legend creation
- */
-const legendDisplaySelector = createSelector(
-    (state) => state.ivTimeSeriesState.showIVTimeSeries,
-    getCurrentVariableMedianMetadata,
-    uniqueClassesSelector('current'),
-    uniqueClassesSelector('compare'),
-    waterwatchVisible,
-    getWaterwatchFloodLevels,
-    (showSeries, medianSeries, currentClasses, compareClasses, visible, floodLevels) => {
-        return {
-            current: showSeries.current ? currentClasses : undefined,
-            compare: showSeries.compare ? compareClasses : undefined,
-            median: showSeries.median ? medianSeries : undefined,
-            floodLevels: visible ? floodLevels : undefined
-        };
-    }
-);
-
-
-/*
- * Factory function  that returns an array of array of markers to be used for the
- * time series graph legend
- * @return {Array of Array} of markers
- */
-export const legendMarkerRowsSelector = createSelector(
-    legendDisplaySelector,
-    displayItems => createLegendMarkers(displayItems)
-);
+import {getMainLayout} from './selectors/layout';
+import {getLegendMarkerRows} from './selectors/legend-data';
 
 
 export const drawTimeSeriesLegend = function(elem, store) {
     elem.append('div')
         .classed('hydrograph-container', true)
         .call(link(store, drawSimpleLegend, createStructuredSelector({
-            legendMarkerRows: legendMarkerRowsSelector,
+            legendMarkerRows: getLegendMarkerRows,
             layout: getMainLayout
         })));
 };
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/legend.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/legend.spec.js
index 4e0d18d0fdb7adf24dcc74e5db893161e1ecc9dd..7fa0c6a342a772f4d0a5f3f3fc62de6278c34570 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/legend.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/legend.spec.js
@@ -1,11 +1,10 @@
 import {select, selectAll} from 'd3-selection';
 
-import {lineMarker, rectangleMarker, textOnlyMarker} from '../../../d3-rendering/markers';
 
 import {configureStore} from '../../store';
 import {Actions} from '../../store/instantaneous-value-time-series-state';
 
-import {legendMarkerRowsSelector, drawTimeSeriesLegend} from './legend';
+import {drawTimeSeriesLegend} from './legend';
 
 
 describe('monitoring-location/components/hydrograph/legend module', () => {
@@ -107,77 +106,6 @@ describe('monitoring-location/components/hydrograph/legend module', () => {
         }
     };
 
-    describe('legendMarkerRowSelector', () => {
-
-        it('Should return no markers if no time series to show', () => {
-            let newData = {
-                ...TEST_DATA,
-                ivTimeSeriesData: {
-                    ...TEST_DATA.ivTimeSeriesData,
-                    timeSeries: {}
-                },
-                statisticsData: {},
-                floodState: {}
-            };
-
-            expect(legendMarkerRowsSelector(newData)).toEqual([]);
-        });
-
-        it('Should return markers for the selected variable', () => {
-            const result = legendMarkerRowsSelector(TEST_DATA);
-
-            expect(result.length).toBe(2);
-            expect(result[0].length).toBe(4);
-            expect(result[0][0].type).toEqual(textOnlyMarker);
-            expect(result[0][1].type).toEqual(lineMarker);
-            expect(result[0][2].type).toEqual(rectangleMarker);
-            expect(result[0][3].type).toEqual(rectangleMarker);
-            expect(result[1].length).toBe(2);
-            expect(result[1][0].type).toEqual(textOnlyMarker);
-            expect(result[1][1].type).toEqual(lineMarker);
-        });
-
-        it('Should return markers for a different selected variable', () => {
-            const newData = {
-                ...TEST_DATA,
-                ivTimeSeriesState: {
-                    ...TEST_DATA.ivTimeSeriesState,
-                    currentIVVariableID: '45807202'
-                }
-            };
-            const result = legendMarkerRowsSelector(newData);
-
-            expect(result.length).toBe(5);
-            expect(result[0].length).toBe(3);
-            expect(result[0][0].type).toEqual(textOnlyMarker);
-            expect(result[0][1].type).toEqual(lineMarker);
-            expect(result[0][2].type).toEqual(lineMarker);
-        });
-
-        it('Should return markers only for time series shown', () => {
-            const newData = {
-                ...TEST_DATA,
-                ivTimeSeriesState: {
-                    ...TEST_DATA.ivTimeSeriesState,
-                    showIVTimeSeries: {
-                        'current': true,
-                        'compare': false,
-                        'median': false
-                    }
-                }
-            };
-
-            const result = legendMarkerRowsSelector(newData);
-
-            expect(result.length).toBe(1);
-            expect(result[0].length).toBe(4);
-            expect(result[0][0].type).toEqual(textOnlyMarker);
-            expect(result[0][1].type).toEqual(lineMarker);
-            expect(result[0][2].type).toEqual(rectangleMarker);
-            expect(result[0][3].type).toEqual(rectangleMarker);
-        });
-    });
-
     describe('legends should render', () => {
 
         let graphNode;
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/method-picker.js b/assets/src/scripts/monitoring-location/components/hydrograph/method-picker.js
index caccee9178feb98e29dce73671ebd64420f23acd..6634fcd042463c10eff456318628fcf912d8824b 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/method-picker.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/method-picker.js
@@ -10,7 +10,7 @@ import{link}  from '../../../lib/d3-redux';
 import {getCurrentMethodID, getAllMethodsForCurrentVariable} from '../../selectors/time-series-selector';
 import {Actions} from '../../store/instantaneous-value-time-series-state';
 
-import { } from './time-series';
+import { } from './selectors/time-series-data';
 
 export const drawMethodPicker = function(elem, store) {
     const pickerContainer = elem.insert('div', ':nth-child(2)')
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/parameters.js b/assets/src/scripts/monitoring-location/components/hydrograph/parameters.js
index bcf7863566192598923f40efcb7d70e429bd5952..08acbff75ba01bbc381ea5175baec2d5f95f8e41 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/parameters.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/parameters.js
@@ -1,56 +1,13 @@
 import {line} from 'd3-shape';
 import {select} from 'd3-selection';
-import {createSelector} from 'reselect';
 
 import config from '../../../config';
 import {appendTooltip} from '../../../tooltips';
-import {sortedParameters} from '../../../utils';
 
-import {getVariables, getCurrentVariableID, getTimeSeries} from '../../selectors/time-series-selector';
 import {Actions} from '../../store/instantaneous-value-time-series-data';
 
-import {MASK_DESC} from './drawing-data';
-import {SPARK_LINE_DIM, CIRCLE_RADIUS_SINGLE_PT} from './layout';
-
-/**
- * Returns metadata for each available time series.
- * @param  {Object} state Redux state
- * @return {Array}        Sorted array of [code, metadata] pairs.
- */
-export const availableTimeSeriesSelector = createSelector(
-    getVariables,
-    getTimeSeries,
-    getCurrentVariableID,
-    (variables, timeSeries, currentVariableID) => {
-        if (!variables) {
-            return [];
-        }
-
-        let sorted = [];
-        const seriesList = Object.values(timeSeries);
-        const timeSeriesVariables = seriesList.map(x => x.variable);
-        const sortedVariables = sortedParameters(variables).map(x => x.oid);
-        for (const variableID of sortedVariables) {
-            // start the next iteration if a variable is not a
-            // series returned by the getTimeSeries
-            if (!timeSeriesVariables.includes(variableID)) {
-                continue;
-            }
-            const variable = variables[variableID];
-            const currentTimeSeriesCount = seriesList.filter(ts => ts.tsKey === 'current:P7D' && ts.variable === variableID).length;
-            if (currentTimeSeriesCount > 0) {
-                let varCodes = {
-                    variableID: variable.oid,
-                    description: variable.variableDescription,
-                    selected: currentVariableID === variableID,
-                    currentTimeSeriesCount: currentTimeSeriesCount
-                };
-                sorted.push([variable.variableCode.value, varCodes]);
-            }
-        }
-        return sorted;
-    }
-);
+import {MASK_DESC} from './selectors/drawing-data';
+import {SPARK_LINE_DIM, CIRCLE_RADIUS_SINGLE_PT} from './selectors/layout';
 
 /**
  * Draw a sparkline in a selected SVG element
@@ -126,14 +83,14 @@ export const addSparkLine = function(svgSelection, {seriesLineSegments, scales})
  * a row changes the active parameter code.
  * @param  {Object} elem                        d3 selection
  * @param  {String} siteno
- * @param  {Object} availableTimeSeries         Time series metadata to display
+ * @param  {Object} availableParameterCodes        parameter metadata to display
  * @param  {Object} lineSegmentsByParmCd        line segments for each parameter code
  * @param  {Object} timeSeriesScalesByParmCd    scales for each parameter code
  */
 export const plotSeriesSelectTable = function (elem,
    {
         siteno,
-        availableTimeSeries,
+        availableParameterCodes,
         lineSegmentsByParmCd,
         timeSeriesScalesByParmCd
    }, store ){
@@ -142,7 +99,7 @@ export const plotSeriesSelectTable = function (elem,
     const scrollTop = lastTable.size() ? lastTable.property('scrollTop') : null;
     elem.select('#select-time-series').remove();
 
-    if (!availableTimeSeries.length) {
+    if (!availableParameterCodes.length) {
         return;
     }
 
@@ -171,25 +128,25 @@ export const plotSeriesSelectTable = function (elem,
 
     table.append('tbody')
         .selectAll('tr')
-        .data(availableTimeSeries)
+        .data(availableParameterCodes)
         .enter().append('tr')
             .attr('ga-on', 'click')
             .attr('ga-event-category', 'selectTimeSeries')
-            .attr('ga-event-action', (parm) => `time-series-parmcd-${parm[0]}`)
+            .attr('ga-event-action', (parm) => `time-series-parmcd-${parm.parameterCode}`)
             .attr('role', 'option')
-            .classed('selected', parm => parm[1].selected)
-            .attr('aria-selected', parm => parm[1].selected)
+            .classed('selected', parm => parm.selected)
+            .attr('aria-selected', parm => parm.selected)
             .on('click', function (parm) {
-                if (!parm[1].selected) {
-                    store.dispatch(Actions.updateIVCurrentVariableAndRetrieveTimeSeries(siteno, parm[1].variableID));
+                if (!parm.selected) {
+                    store.dispatch(Actions.updateIVCurrentVariableAndRetrieveTimeSeries(siteno, parm.variableID));
                 }
             })
             .call(tr => {
                 let parmCdCol = tr.append('th')
                     .attr('scope', 'row');
                 parmCdCol.append('span')
-                    .text(parm => parm[1].description)
-                    .call(appendTooltip, parm => `Parameter code: ${parm[0]}`);
+                    .text(parm => parm.description)
+                    .call(appendTooltip, parm => `Parameter code: ${parm.parameterCode}`);
                 tr.append('td')
                     .append('svg')
                     .attr('width', SPARK_LINE_DIM.width.toString())
@@ -198,14 +155,14 @@ export const plotSeriesSelectTable = function (elem,
                     .text(parm => parm[1].currentTimeSeriesCount);
                 tr.append('td')
                     .style('white-space', 'nowrap')
-                    .text(parm =>`${config.uvPeriodOfRecord[parm[0]].begin_date} to ${config.uvPeriodOfRecord[parm[0]].end_date}`);
+                    .text(parm =>`${config.uvPeriodOfRecord[parm.parameterCode].begin_date} to ${config.uvPeriodOfRecord[parm.parameterCode].end_date}`);
             });
 
     table.property('scrollTop', scrollTop);
 
     table.selectAll('tbody svg').each(function(d) {
         let selection = select(this);
-        const parmCd = d[0];
+        const parmCd = d.parameterCode;
         const lineSegments = lineSegmentsByParmCd[parmCd] ? lineSegmentsByParmCd[parmCd] : [];
         for (const seriesLineSegments of lineSegments) {
             selection.call(addSparkLine, {
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/parameters.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/parameters.spec.js
index f71786aab7ecf82e681cb43fad160c1c87f84279..397a6476f4be8c7378c9d55ac4969ec2c6198019 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/parameters.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/parameters.spec.js
@@ -8,169 +8,6 @@ import {addSparkLine, plotSeriesSelectTable, availableTimeSeriesSelector} from '
 
 describe('monitoring-location/components/hydrograph/parameters module', () => {
 
-    describe('availableTimeSeriesSelector', () => {
-        it('sets attributes correctly when all series have data points', () => {
-            const available = availableTimeSeriesSelector({
-                ivTimeSeriesData: {
-                    timeSeries: {
-                        'current:00060': {description: '00060', tsKey: 'current:P7D', variable: 'code0', points: [{x: 1, y: 2}]},
-                        'current:00061': {description: '00061', tsKey: 'current:P7D', variable: 'code1', points: [{x: 2, y: 3}]},
-                        'current:00062': {description: '00062', tsKey: 'current:P7D', variable: 'code2', points: [{x: 3, y: 4}]},
-                        'compare:00061': {description: '00061', tsKey: 'compare:P7D', variable: 'code1', points: [{x: 1, y: 17}]},
-                        'compare:00062': {description: '00062', tsKey: 'compare:P7D', variable: 'code2', points: [{x: 2, y: 18}]},
-                        'compare:00063': {description: '00063', tsKey: 'compare:P7D', variable: 'code3', points: [{x: 3, y: 46}]}
-                    },
-                    variables: {
-                        'code0': {
-                            oid: 'code0',
-                            variableDescription: 'code0 desc',
-                            variableCode: {
-                                value: '00060'
-                            }
-                        },
-                        'code1': {
-                            oid: 'code1',
-                            variableDescription: 'code1 desc',
-                            variableCode: {
-                                value: '00061'
-                            }
-                        },
-                        'code2': {
-                            oid: 'code2',
-                            variableDescription: 'code2 desc',
-                            variableCode: {
-                                value: '00062'
-                            }
-                        },
-                        'code3': {
-                            oid: 'code3',
-                            variableDescription: 'code3 desc',
-                            variableCode: {
-                                value: '00063'
-                            }
-                        }
-                    }
-                },
-                ivTimeSeriesState: {
-                    currentIVVariableID: 'code0'
-                }
-            });
-            // Series are ordered by parameter code and have expected values.
-            expect(available).toEqual([
-                ['00060', {variableID: 'code0', description: 'code0 desc', selected: true, currentTimeSeriesCount: 1}],
-                ['00061', {variableID: 'code1', description: 'code1 desc', selected: false, currentTimeSeriesCount: 1}],
-                ['00062', {variableID: 'code2', description: 'code2 desc', selected: false, currentTimeSeriesCount: 1}]
-            ]);
-        });
-
-        it('sets attributes correctly when not all series have data points', () => {
-            const available = availableTimeSeriesSelector({
-                ivTimeSeriesData: {
-                    timeSeries: {
-                        'current:00060': {description: '00060', tsKey: 'current:P7D', variable: 'code0', points: [{x: 1, y: 2}]},
-                        'current:00061': {description: '00061', tsKey: 'current:P7D', variable: 'code1', points: [{x: 2, y: 3}]},
-                        'current:00062': {description: '00062', tsKey: 'current:P7D', variable: 'code2', points: [{x: 3, y: 4}]},
-                        'compare:00061': {description: '00061', tsKey: 'compare:P7D', variable: 'code1', points: []},
-                        'compare:00062': {description: '00062', tsKey: 'compare:P7D', variable: 'code2', points: [{x: 2, y: 18}]},
-                        'compare:00063': {description: '00063', tsKey: 'compare:P7D', variable: 'code3', points: [{x: 3, y: 46}]}
-                    },
-                    variables: {
-                        'code0': {
-                            oid: 'code0',
-                            variableDescription: 'code0 desc',
-                            variableCode: {
-                                value: '00060'
-                            }
-                        },
-                        'code1': {
-                            oid: 'code1',
-                            variableDescription: 'code1 desc',
-                            variableCode: {
-                                value: '00061'
-                            }
-                        },
-                        'code2': {
-                            oid: 'code2',
-                            variableDescription: 'code2 desc',
-                            variableCode: {
-                                value: '00062'
-                            }
-                        },
-                        'code3': {
-                            oid: 'code3',
-                            variableDescription: 'code3 desc',
-                            variableCode: {
-                                value: '00063'
-                            }
-                        }
-                    }
-                },
-                ivTimeSeriesState: {
-                    currentIVVariableID: 'code0'
-                }
-            });
-            // Series are ordered by parameter code and have expected values.
-            expect(available).toEqual([
-                ['00060', {variableID: 'code0', description: 'code0 desc', selected: true, currentTimeSeriesCount: 1}],
-                ['00061', {variableID: 'code1', description: 'code1 desc', selected: false, currentTimeSeriesCount: 1}],
-                ['00062', {variableID: 'code2', description: 'code2 desc', selected: false, currentTimeSeriesCount: 1}]
-            ]);
-        });
-
-        it('time series without data points are considered available', () => {
-            const available = availableTimeSeriesSelector({
-                ivTimeSeriesData: {
-                    timeSeries: {
-                        'current:00060': {description: '00060', tsKey: 'current:P7D', variable: 'code0', points: [{x: 1, y: 2}]},
-                        'current:00061': {description: '00061', tsKey: 'current:P7D', variable: 'code1', points: []},
-                        'current:00062': {description: '00062', tsKey: 'current:P7D', variable: 'code2', points: [{x: 3, y: 4}]},
-                        'compare:00061': {description: '00061', tsKey: 'compare:P7D', variable: 'code1', points: []},
-                        'compare:00062': {description: '00062', tsKey: 'compare:P7D', variable: 'code2', points: [{x: 2, y: 18}]},
-                        'compare:00063': {description: '00063', tsKey: 'compare:P7D', variable: 'code3', points: [{x: 3, y: 46}]}
-                    },
-                    variables: {
-                        'code0': {
-                            oid: 'code0',
-                            variableDescription: 'code0 desc',
-                            variableCode: {
-                                value: '00060'
-                            }
-                        },
-                        'code1': {
-                            oid: 'code1',
-                            variableDescription: 'code1 desc',
-                            variableCode: {
-                                value: '00061'
-                            }
-                        },
-                        'code2': {
-                            oid: 'code2',
-                            variableDescription: 'code2 desc',
-                            variableCode: {
-                                value: '00062'
-                            }
-                        },
-                        'code3': {
-                            oid: 'code3',
-                            variableDescription: 'code3 desc',
-                            variableCode: {
-                                value: '00063'
-                            }
-                        }
-                    }
-                },
-                ivTimeSeriesState: {
-                    currentIVVariableID: 'code0'
-                }
-            });
-            // Series are ordered by parameter code and have expected values.
-            expect(available).toEqual([
-                ['00060', {variableID: 'code0', description: 'code0 desc', selected: true, currentTimeSeriesCount: 1}],
-                ['00061', {variableID: 'code1', description: 'code1 desc', selected: false, currentTimeSeriesCount: 1}],
-                ['00062', {variableID: 'code2', description: 'code2 desc', selected: false, currentTimeSeriesCount: 1}]
-            ]);
-        });
-    });
 
      describe('plotSeriesSelectTable', () => {
         let tableDivSelection;
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/audible-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/audible-data.js
new file mode 100644
index 0000000000000000000000000000000000000000..607799bb52b951b8043938210d43daec3986ccbe
--- /dev/null
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/audible-data.js
@@ -0,0 +1,59 @@
+/*
+ * Note the audible interface is not currently enabled and will likely need a major implementation
+ * The current patterns of putting the selectors in a separate module from rendering code and
+ * updating the selectors to use is* or get* pattern.
+ */
+import {scaleLinear} from 'd3-scale';
+import {createSelector} from 'reselect';
+
+import {getTimeSeries} from '../../../selectors/time-series-selector';
+
+import {tsCursorPointsSelector} from './cursor';
+import {getMainYScale} from './scales';
+
+/*
+ * Returns a Redux selector function that returns true if the audible interface is playing.
+ */
+export const isAudiblePlaying = state => state.ivTimeSeriesStat.audiblePlayId !== null;
+
+const getAudibleYScale = createSelector(
+    getMainYScale,
+    (yScale) => {
+        return scaleLinear()
+            .domain(yScale.domain())
+            .range([80, 1500]);
+    }
+);
+
+/*
+ * Returns a Redux selector function which retrieves an array of time series points where the
+ * value can be used for pitches.
+ */
+export const getAudiblePoints = createSelector(
+    getTimeSeries,
+    tsCursorPointsSelector('current'),
+    tsCursorPointsSelector('compare'),
+    getAudibleYScale,
+    (allTimeSeries, currentPoints, comparePoints, yScale) => {
+        // Set null points for all time series, so we can turn audio for those
+        // points off when toggling to other time series.
+        let points = Object.keys(allTimeSeries).reduce((points, tsID) => {
+            points[tsID] = null;
+            return points;
+        }, {});
+
+        // Get the pitches for the current-year points
+        points = Object.keys(currentPoints).reduce((points, tsID) => {
+            const pt = currentPoints[tsID];
+            points[tsID] = yScale(pt.value);
+            return points;
+        }, points);
+
+        // Get the pitches for the compare-year points
+        return Object.keys(comparePoints).reduce((points, tsID) => {
+            const pt = comparePoints[tsID];
+            points[tsID] = yScale(pt.value);
+            return points;
+        }, points);
+    }
+);
\ No newline at end of file
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/axes.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js
similarity index 76%
rename from assets/src/scripts/monitoring-location/components/hydrograph/axes.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js
index 199f5bc9ae88ffd9646b972b2fc9799df8d80922..386818c3eb041fa6ab395a6caf4485772e650eb4 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/axes.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js
@@ -2,14 +2,15 @@ import {axisBottom, axisLeft, axisRight} from 'd3-axis';
 import memoize from 'fast-memoize';
 import {createSelector} from 'reselect';
 
-import {generateTimeTicks} from '../../../d3-rendering/tick-marks';
-import {getCurrentDateRangeKind, getCurrentParmCd} from '../../selectors/time-series-selector';
-import {convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../../utils';
+import {generateTimeTicks} from '../../../../d3-rendering/tick-marks';
+import {getCurrentDateRangeKind, getCurrentParmCd} from '../../../selectors/time-series-selector';
+import {convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../../../utils';
 
 import {getYTickDetails} from './domain';
 import {getLayout} from './layout';
 import {getXScale, getBrushXScale, getYScale, getSecondaryYScale} from './scales';
-import {yLabelSelector, secondaryYLabelSelector, tsTimeZoneSelector, TEMPERATURE_PARAMETERS} from './time-series';
+import {getYLabel, getSecondaryYLabel, getTsTimeZone, TEMPERATURE_PARAMETERS} from './time-series-data';
+
 
 const createXAxis = function(xScale,  period, ianaTimeZone) {
     const [startMillis, endMillis] = xScale.domain();
@@ -32,16 +33,15 @@ const createXAxis = function(xScale,  period, ianaTimeZone) {
  * @param {String} ianaTimeZone - Internet Assigned Numbers Authority designation for a time zone
  * @return {Object} {xAxis, yAxis, secondardYaxis} - D3 Axis
  */
-export const createAxes = function(xScale, yScale, secondaryYScale, yTickSize, parmCd, period, ianaTimeZone) {
+const createAxes = function(xScale, yScale, secondaryYScale, yTickDetails, yTickSize, parmCd, period, ianaTimeZone) {
     // Create x-axis
     const xAxis = createXAxis(xScale, period, ianaTimeZone);
 
     // Create y-axis
-    const tickDetails = getYTickDetails(yScale.domain(), parmCd);
     const yAxis = axisLeft()
         .scale(yScale)
-        .tickValues(tickDetails.tickValues)
-        .tickFormat(tickDetails.tickFormat)
+        .tickValues(yTickDetails.tickValues)
+        .tickFormat(yTickDetails.tickFormat)
         .tickSizeInner(yTickSize)
         .tickPadding(3)
         .tickSizeOuter(0);
@@ -60,7 +60,7 @@ export const createAxes = function(xScale, yScale, secondaryYScale, yTickSize, p
 
     if (secondaryYScale !== null) {
         let secondaryAxisTicks;
-        const primaryAxisTicks = tickDetails.tickValues;
+        const primaryAxisTicks = yTickDetails.tickValues;
         if (TEMPERATURE_PARAMETERS.celsius.includes(parmCd)) {
             secondaryAxisTicks = primaryAxisTicks.map(celsius => convertCelsiusToFahrenheit(celsius));
         } else if (TEMPERATURE_PARAMETERS.fahrenheit.includes(parmCd)) {
@@ -72,35 +72,36 @@ export const createAxes = function(xScale, yScale, secondaryYScale, yTickSize, p
 };
 
 /**
- * Selector that returns the brush x axis
+ * Returns a Redux selector function that returns the brush x axis
  */
 export const getBrushXAxis = createSelector(
     getBrushXScale('current'),
-    tsTimeZoneSelector,
+    getTsTimeZone,
     getCurrentDateRangeKind,
     (xScale, ianaTimeZone, period) => createXAxis(xScale, period, ianaTimeZone)
 );
 
 /**
- * Returns data necessary to render the graph axes.
- * @return {Object}
+ * Returns a Redux Selection that returns an object with xAxis, yAxis, and secondaryYAxis properties
  */
 export const getAxes = memoize(kind => createSelector(
     getXScale(kind, 'current'),
     getYScale(kind),
     getSecondaryYScale(kind),
+    getYTickDetails,
     getLayout(kind),
-    yLabelSelector,
-    tsTimeZoneSelector,
+    getYLabel,
+    getTsTimeZone,
     getCurrentParmCd,
     getCurrentDateRangeKind,
-    secondaryYLabelSelector,
-    (xScale, yScale, secondaryYScale, layout, plotYLabel, ianaTimeZone, parmCd, currentDateRange, plotSecondaryYLabel) => {
+    getSecondaryYLabel,
+    (xScale, yScale, secondaryYScale, yTickDetails, layout, plotYLabel, ianaTimeZone, parmCd, currentDateRange, plotSecondaryYLabel) => {
         return {
             ...createAxes(
                 xScale,
                 yScale,
                 secondaryYScale,
+                yTickDetails,
                 -layout.width + layout.margin.right,
                 parmCd,
                 currentDateRange,
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/cursor.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/cursor.js
new file mode 100644
index 0000000000000000000000000000000000000000..a88a2e10e3279b5063bf84e46dfc627f13e1f178
--- /dev/null
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/cursor.js
@@ -0,0 +1,97 @@
+import memoize from 'fast-memoize';
+import {createSelector} from 'reselect';
+
+import {getNearestTime} from '../../../../utils';
+
+import {getCurrentMethodID} from '../../../selectors/time-series-selector';
+
+import {getCurrentVariablePointsByTsId} from '../drawing-data';
+import {getMainXScale, getMainYScale} from './scales';
+import {isVisible} from './time-series-data';
+
+
+export const getCursorOffset = createSelector(
+    getMainXScale('current'),
+    state => state.ivTimeSeriesState.ivGraphCursorOffset,
+    (xScale, cursorOffset) => {
+        // If cursorOffset is false, don't show it
+        if (cursorOffset === false) {
+            return null;
+        // If cursorOffset is otherwise unset, default to the last offset
+        } else if (!cursorOffset) {
+            const domain = xScale.domain();
+            return domain[1] - domain[0];
+        } else {
+            return cursorOffset;
+        }
+    }
+);
+
+/**
+ * Returns a selector that, for a given tsKey:
+ * Returns the time corresponding to the current cursor offset.
+ * @param  {String} tsKey
+ * @return {Date}
+ */
+export const getCursorTime = memoize(tsKey => createSelector(
+    getCursorOffset,
+    getMainXScale(tsKey),
+    (cursorOffset, xScale) => {
+        return cursorOffset ? new Date(xScale.domain()[0] + cursorOffset) : null;
+    }
+));
+
+/*
+ * Returns a Redux selector function that returns the time series data point nearest
+ * the tooltip focus time for the current time series with the current variable and current method
+ * @param {Object} state - Redux store
+ * @param String} tsKey - Time series key
+ * @return {Object}
+ */
+export const getTsCursorPoints = memoize(tsKey => createSelector(
+    getCurrentVariablePointsByTsId(tsKey),
+    getCurrentMethodID,
+    getCursorTime(tsKey),
+    isVisible(tsKey),
+    (timeSeries, currentMethodId, cursorTime, isVisible) => {
+        if (!cursorTime || !isVisible) {
+            return {};
+        }
+        return Object.keys(timeSeries).reduce((data, tsId) => {
+            if (timeSeries[tsId].length && parseInt(tsId.split(':')[0]) === currentMethodId) {
+                const datum = getNearestTime(timeSeries[tsId], cursorTime);
+                data[tsId] = {
+                    ...datum,
+                    tsKey: tsKey
+                };
+            }
+            return data;
+        }, {});
+    }));
+
+/*
+ * Returns a function that returns the time series data point nearest the
+ * tooltip focus time for the given time series key. Only returns those points
+ * where the y-value is finite; no use in making a point if y is Infinity.
+ *
+ * @param {Object} state - Redux store
+ * @param String} tsKey - Time series key
+ * @return {Object}
+ */
+export const getTooltipPoints = memoize(tsKey => createSelector(
+    getMainXScale(tsKey),
+    getMainYScale,
+    getTsCursorPoints(tsKey),
+    (xScale, yScale, cursorPoints) => {
+        return Object.keys(cursorPoints).reduce((tooltipPoints, tsID) => {
+            const cursorPoint = cursorPoints[tsID];
+            if (isFinite(yScale(cursorPoint.value))) {
+                tooltipPoints.push({
+                    x: xScale(cursorPoint.dateTime),
+                    y: yScale(cursorPoint.value)
+                });
+            }
+            return tooltipPoints;
+        }, []);
+    }
+));
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/cursor.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/cursor.spec.js
similarity index 85%
rename from assets/src/scripts/monitoring-location/components/hydrograph/cursor.spec.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/cursor.spec.js
index 536337f24dc62cd53f047a39e3845c0e8150ce50..0f4702b38ae2e80b90c73603ddf25d922fff80e7 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/cursor.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/cursor.spec.js
@@ -1,7 +1,7 @@
-import {configureStore} from '../../store';
-import {Actions} from '../../store/instantaneous-value-time-series-state';
+import {configureStore} from '../../../store';
+import {Actions} from '../../../store/instantaneous-value-time-series-state';
 
-import {tsCursorPointsSelector, cursorOffsetSelector} from './cursor';
+import {getTsCursorPoints, getCursorOffset, getTooltipPoints} from './cursor';
 
 let DATA = [12, 13, 14, 15, 16].map(hour => {
     return {
@@ -330,9 +330,9 @@ const TEST_STATE_ONE_VAR = {
 
 describe('monitoring-location/components/hydrograph/cursor module', () => {
 
-    describe('tsCursorPointsSelector', () => {
+    describe('getTsCursorPoints', () => {
         it('Should return last time with non-masked value if the cursor offset is null', function() {
-            expect(tsCursorPointsSelector('compare')(TEST_STATE_ONE_VAR)).toEqual({
+            expect(getTsCursorPoints('compare')(TEST_STATE_ONE_VAR)).toEqual({
                 '69928:compare:P7D': {
                     dateTime: 1514995200000,
                     qualifiers: ['P'],
@@ -340,7 +340,7 @@ describe('monitoring-location/components/hydrograph/cursor module', () => {
                     tsKey: 'compare'
                 }
             });
-            expect(tsCursorPointsSelector('current')(TEST_STATE_ONE_VAR)).toEqual({
+            expect(getTsCursorPoints('current')(TEST_STATE_ONE_VAR)).toEqual({
                 '69928:current:P7D': {
                     dateTime: 1514995200000,
                     qualifiers: ['P'],
@@ -359,8 +359,8 @@ describe('monitoring-location/components/hydrograph/cursor module', () => {
                 }
             };
 
-            expect(tsCursorPointsSelector('current')(state)['69928:current:P7D'].value).toEqual(14);
-            expect(tsCursorPointsSelector('compare')(state)['69928:compare:P7D'].value).toEqual(14);
+            expect(getTsCursorPoints('current')(state)['69928:current:P7D'].value).toEqual(14);
+            expect(getTsCursorPoints('compare')(state)['69928:compare:P7D'].value).toEqual(14);
         });
 
         it('Selects the nearest point for the current variable streamflow', () => {
@@ -373,7 +373,7 @@ describe('monitoring-location/components/hydrograph/cursor module', () => {
                     ivGraphCursorOffset: 16 * 60 * 1000
                 }
             };
-            expect(tsCursorPointsSelector('current')(newState)).toEqual({
+            expect(getTsCursorPoints('current')(newState)).toEqual({
                 '69929:current:P7D': {
                     value: 2,
                     qualifiers: ['P'],
@@ -394,7 +394,7 @@ describe('monitoring-location/components/hydrograph/cursor module', () => {
                 }
             };
 
-            expect(tsCursorPointsSelector('current')(newState)).toEqual({
+            expect(getTsCursorPoints('current')(newState)).toEqual({
                 '69930:current:P7D': {
                     value: 0.03,
                     qualifiers: ['P'],
@@ -405,7 +405,7 @@ describe('monitoring-location/components/hydrograph/cursor module', () => {
         });
     });
 
-    describe('cursorOffsetSelector', () => {
+    describe('getCursorOffset', () => {
         let store;
         beforeEach(() => {
             store = configureStore(TEST_STATE_ONE_VAR);
@@ -413,13 +413,53 @@ describe('monitoring-location/components/hydrograph/cursor module', () => {
 
         it('returns null when false', () => {
             store.dispatch(Actions.setIVGraphCursorOffset(false));
-            expect(cursorOffsetSelector(store.getState())).toBe(null);
+            expect(getCursorOffset(store.getState())).toBe(null);
         });
 
         it('returns last point when null', () => {
             store.dispatch(Actions.setIVGraphCursorOffset(null));
             const cursorRange = DATA[4].dateTime - DATA[0].dateTime;
-            expect(cursorOffsetSelector(store.getState())).toBe(cursorRange);
+            expect(getCursorOffset(store.getState())).toBe(cursorRange);
         });
     });
+
+    describe('tooltipPointsSelector', () => {
+        const id = (val) => val;
+
+        it('should return the requested time series focus time', () => {
+            expect(tooltipPointsSelector('current').resultFunc(id, id, {
+                '00060:current': {
+                    dateTime: '1date',
+                    value: 1
+                },
+                '00060:compare': {
+                    dateTime: '2date',
+                    value: 2
+                }
+            })).toEqual([{
+                x: '1date',
+                y: 1
+            }, {
+                x: '2date',
+                y: 2
+            }]);
+        });
+
+        it('should exclude values that are infinite', () => {
+            expect(getTooltipPoints('current').resultFunc(id, id, {
+                '00060:current': {
+                    dateTime: '1date',
+                    value: Infinity
+                },
+                '00060:compare': {
+                    dateTime: '2date',
+                    value: 2
+                }
+            })).toEqual([{
+                x: '2date',
+                y: 2
+            }]);
+        });
+    });
+
 });
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/domain.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js
similarity index 61%
rename from assets/src/scripts/monitoring-location/components/hydrograph/domain.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js
index 6e8bd631127eb6d25d409a46a49335cd889d224a..69d4243065e727a016d9f7259bf285be1577c2ec 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/domain.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js
@@ -2,11 +2,11 @@ import {extent, ticks} from 'd3-array';
 import {format} from 'd3-format';
 import {createSelector} from 'reselect';
 
-import config from '../../../config';
-import {mediaQuery} from '../../../utils';
-import {getCurrentParmCd} from '../../selectors/time-series-selector';
+import config from '../../../../config';
+import {mediaQuery} from '../../../../utils';
+import {getCurrentParmCd} from '../../../selectors/time-series-selector';
 
-import {visiblePointsSelector} from './drawing-data';
+import {getVisiblePoints} from '../drawing-data';
 
 
 const PADDING_RATIO = 0.2;
@@ -18,8 +18,11 @@ export const SYMLOG_PARMS = [
     '72137'
 ];
 
+/*
+ * The helper functions are exported as an aid to testing. Only the selectors are actually imported into other modules
+ */
 /**
- *  Return domain padded on both ends by paddingRatio.
+ *  Helper function which returns domain padded on both ends by paddingRatio.
  *  For positive domains, a zero-lower bound on the y-axis is enforced.
  *  @param {Array} domain - array of two numbers
  *  @param {Boolean} lowerBoundPOW10 - using log scale
@@ -52,50 +55,6 @@ export const extendDomain = function (domain, lowerBoundPOW10) {
     ];
 };
 
-
-export const getYDomain = function (pointArrays, currentVarParmCd) {
-    let yExtent;
-    let scaleDomains = [];
-
-    // Calculate max and min for data
-    for (const points of pointArrays) {
-        if (points.length === 0) {
-            continue;
-        }
-        const finitePts = points.map(pt => pt.value).filter(val => isFinite(val));
-        let ptExtent = extent(finitePts);
-        if (ptExtent[0] === ptExtent[1]) {
-            // when both the lower and upper values of
-            // extent are the same, the domain of the
-            // extent is from -Infinity to +Infinity;
-            // this isn't useful for creation of data
-            // points, so add this broadens the extent
-            // a bit for single point series
-            if (ptExtent[0]) {
-                ptExtent = [ptExtent[0] - ptExtent[0] / 2, ptExtent[0] + ptExtent[0] / 2];
-            } else { // ptExtent of 0 so just set to a constant
-                ptExtent = [0, 1];
-            }
-        }
-        scaleDomains.push(ptExtent);
-    }
-    if (scaleDomains.length > 0) {
-        const flatDomains = [].concat(...scaleDomains).filter(val => isFinite(val));
-        if (flatDomains.length > 0) {
-            yExtent = [Math.min(...flatDomains), Math.max(...flatDomains)];
-        }
-    }
-
-    // Add padding to the extent and handle empty data sets.
-    if (yExtent) {
-        yExtent = extendDomain(yExtent, SYMLOG_PARMS.indexOf(currentVarParmCd) > -1);
-    } else {
-        yExtent = [0, 1];
-    }
-    return yExtent;
-};
-
-
 /**
  * Helper function that finds highest negative value (or lowest positive value) in array of tick values, then returns
  * that number's absolute value
@@ -124,7 +83,7 @@ export const getLowestAbsoluteValueOfTickValues = function(tickValues) {
  * value of negative y-axis values
  * @returns {array} additionalTickValues, set of new y-axis tick values that will fill tick mark gaps on log scale graphs
  */
-export const generateAdditionalTickValues = function(lowestTickValueOfLogScale) {
+const generateAdditionalTickValues = function(lowestTickValueOfLogScale) {
     let additionalTickValues = [];
     while (lowestTickValueOfLogScale > 2) {
         lowestTickValueOfLogScale = Math.ceil(lowestTickValueOfLogScale / 2);
@@ -186,7 +145,7 @@ export const generateNegativeTicks = function(tickValues, additionalTickValues)
 
 
 /**
- * Function creates a new set of tick values that will fill in gaps in log scale ticks, then combines this new set with the
+ * Help function creates a new set of tick values that will fill in gaps in log scale ticks, then combines this new set with the
  * original set of tick marks.
  * @param {array} tickValues, the list of y-axis tick values
  * @param {array} yDomain, an array of two values, the lower and upper extent of the y-axis
@@ -211,59 +170,97 @@ export const getFullArrayOfAdditionalTickMarks = function(tickValues, yDomain) {
    return fullArrayOfTickMarks;
 };
 
-
-/**
- * Helper function which generates y tick values for a scale
- * @param {Array} yDomain - Two element array representing the domain on the yscale.
- * @param {Array} parmCd - parameter code for time series that is being generated.
- * @returns {Array} of tick values
+/*
+ * Returns a Redux selector function that returns the yExtent for the currently
+ * visible points
  */
-export const getYTickDetails = function (yDomain, parmCd) {
-    const isSymlog = SYMLOG_PARMS.indexOf(parmCd) > -1;
-
-    let tickValues = ticks(yDomain[0], yDomain[1], Y_TICK_COUNT);
-
-    // When there are too many log scale ticks they will overlap--reduce the number in proportion to the number of ticks
-    // For example, if there are 37 tick marks, every 4 ticks will be used... if there are 31 tick marks, every 3 ticks
-    // will be used. Screens smaller than the USWDS defined medium screen will use fewer tick marks than larger screens.
-    if (isSymlog) {
-        // add additional ticks and labels to log scales as needed
-        tickValues = getFullArrayOfAdditionalTickMarks(tickValues, yDomain);
-        // remove ticks if there are too many of them
-        let lengthLimit = 20;
-        let divisor = 10;
-        if (!mediaQuery(config.USWDS_MEDIUM_SCREEN)) {
-            lengthLimit = 10;
-            divisor = 5;
+export const getYDomain = createSelector(
+    getVisiblePoints,
+    getCurrentParmCd,
+    (pointArrays, currentVarParmCd) => {
+        let yExtent;
+        let scaleDomains = [];
+
+        // Calculate max and min for data
+        for (const points of pointArrays) {
+            if (points.length === 0) {
+                continue;
+            }
+            const finitePts = points.map(pt => pt.value).filter(val => isFinite(val));
+            let ptExtent = extent(finitePts);
+            if (ptExtent[0] === ptExtent[1]) {
+                // when both the lower and upper values of
+                // extent are the same, the domain of the
+                // extent is from -Infinity to +Infinity;
+                // this isn't useful for creation of data
+                // points, so add this broadens the extent
+                // a bit for single point series
+                if (ptExtent[0]) {
+                    ptExtent = [ptExtent[0] - ptExtent[0] / 2, ptExtent[0] + ptExtent[0] / 2];
+                } else { // ptExtent of 0 so just set to a constant
+                    ptExtent = [0, 1];
+                }
+            }
+            scaleDomains.push(ptExtent);
         }
-        if (tickValues.length > lengthLimit) {
-            tickValues = tickValues
-                .sort((a, b) => a - b)
-                .filter((_, index) => {
-                    return !(index % Math.round(tickValues.length/divisor));
-                });
+        if (scaleDomains.length > 0) {
+            const flatDomains = [].concat(...scaleDomains).filter(val => isFinite(val));
+            if (flatDomains.length > 0) {
+                yExtent = [Math.min(...flatDomains), Math.max(...flatDomains)];
+            }
         }
-    }
 
-    // If all ticks are integers, don't display right of the decimal place.
-    // Otherwise, format with two decimal points.
-    const tickFormat = tickValues.filter(t => !Number.isInteger(t)).length ? '.2f' : 'd';
-    return {
-        tickValues,
-        tickFormat: format(tickFormat)
-    };
-};
-
-
-const yDomainSelector = createSelector(
-    visiblePointsSelector,
-    getCurrentParmCd,
-    getYDomain
+        // Add padding to the extent and handle empty data sets.
+        if (yExtent) {
+            yExtent = extendDomain(yExtent, SYMLOG_PARMS.indexOf(currentVarParmCd) > -1);
+        } else {
+            yExtent = [0, 1];
+        }
+        return yExtent;
+    }
 );
 
-
-export const tickSelector = createSelector(
-    yDomainSelector,
+/*
+ * Returns a Redux selector function that returns an Object with two properties:
+ *      @prop tickValues {Array of Number}
+ *      @prop tickFormat {Array of String} - formatted tickValues
+ */
+export const getYTickDetails = createSelector(
+    getYDomain,
     getCurrentParmCd,
-    getYTickDetails
+    (yDomain, parmCd) => {
+        const isSymlog = SYMLOG_PARMS.indexOf(parmCd) > -1;
+
+        let tickValues = ticks(yDomain[0], yDomain[1], Y_TICK_COUNT);
+
+        // When there are too many log scale ticks they will overlap--reduce the number in proportion to the number of ticks
+        // For example, if there are 37 tick marks, every 4 ticks will be used... if there are 31 tick marks, every 3 ticks
+        // will be used. Screens smaller than the USWDS defined medium screen will use fewer tick marks than larger screens.
+        if (isSymlog) {
+            // add additional ticks and labels to log scales as needed
+            tickValues = getFullArrayOfAdditionalTickMarks(tickValues, yDomain);
+            // remove ticks if there are too many of them
+            let lengthLimit = 20;
+            let divisor = 10;
+            if (!mediaQuery(config.USWDS_MEDIUM_SCREEN)) {
+                lengthLimit = 10;
+                divisor = 5;
+            }
+            if (tickValues.length > lengthLimit) {
+                tickValues = tickValues
+                    .sort((a, b) => a - b)
+                    .filter((_, index) => {
+                        return !(index % Math.round(tickValues.length / divisor));
+                    });
+            }
+        }
+
+        // If all ticks are integers, don't display right of the decimal place.
+        // Otherwise, format with two decimal points.
+        const tickFormat = tickValues.filter(t => !Number.isInteger(t)).length ? '.2f' : 'd';
+        return {
+            tickValues: tickValues,
+            tickFormat: format(tickFormat)
+        };
+    }
 );
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/domain.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.spec.js
similarity index 98%
rename from assets/src/scripts/monitoring-location/components/hydrograph/domain.spec.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.spec.js
index 40c80f49112706b109e00e23826e59499300adea..5eee2781a1897ed9e4c991a4e1cbcdd4ea99b478 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/domain.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.spec.js
@@ -9,7 +9,7 @@ import {
 } from './domain';
 
 
-describe('monitoring-location/componens/hydrograph/domain module', () => {
+describe('monitoring-location/componens/hydrograph/selectors/domain module', () => {
     describe('extendDomain', () => {
         it('lower bounds are calculated based on order of magnitude with the parameter, upper bound 20%', () => {
             const lowValDomain = extendDomain([50, 1000], true);
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/drawing-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/drawing-data.js
similarity index 91%
rename from assets/src/scripts/monitoring-location/components/hydrograph/drawing-data.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/drawing-data.js
index 75e6fb115871f8f45422c9619693f675c5415d7e..aed539361449ad195585164f8ee741389ab2013d 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/drawing-data.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/drawing-data.js
@@ -1,3 +1,9 @@
+/*
+ * One helper function is exported and used in some of the rendering code.
+ * It would be good to refactor this but I think ultimately it will likely wait until
+ * we have a new IV data service that follows similar patterns to what is returned by
+ * the daily value statistical service.
+ */
 import {format} from 'd3-format';
 import memoize from 'fast-memoize';
 import find from 'lodash/find';
@@ -60,12 +66,26 @@ const transformToCumulative = function(points) {
     });
 };
 
+/*
+ * Helper function that returns an object which identifies which classes to use for the point.
+ * This is used within a few of the rendering modules.
+ * @param {Object} point
+ * @return {Object}
+ */
+export const classesForPoint = function(point) {
+    return {
+        approved: point.qualifiers.indexOf('A') > -1,
+        estimated: point.qualifiers.indexOf('e') > -1 || point.qualifiers.indexOf('E') > -1
+    };
+};
+
+
 /* Factory function that returns a function that returns an object where the properties are ts IDs and the values
  * are array of point objects that can be used to render a time series graph.
  * @param {Object} state
  * @return {Object} where the keys are ts ids and the values are an Array of point Objects.
  */
-export const allPointsSelector = createSelector(
+export const getAllPoints= createSelector(
     getTimeSeries,
     getVariables,
     (timeSeries, variables) => {
@@ -89,9 +109,9 @@ export const allPointsSelector = createSelector(
  * @param {String} tsKey
  * @return {Object} of keys are tsId, values are Array of point Objects
  */
-export const pointsByTsKeySelector = memoize((tsKey, period) => createSelector(
+export const getPointsByTsKey = memoize((tsKey, period) => createSelector(
     getTsRequestKey(tsKey, period),
-    allPointsSelector,
+    getAllPoints,
     getTimeSeries,
     (tsRequestKey, points, timeSeries) => {
         let result = {};
@@ -109,8 +129,8 @@ export const pointsByTsKeySelector = memoize((tsKey, period) => createSelector(
  * @param {String} tsKey
  * @return Object
  */
-export const currentVariablePointsByTsIdSelector = memoize(tsKey => createSelector(
-    pointsByTsKeySelector(tsKey),
+export const getCurrentVariablePointsByTsId = memoize(tsKey => createSelector(
+    getPointsByTsKey(tsKey),
     getCurrentVariableTimeSeries(tsKey),
     (points, timeSeries) => {
         let result = {};
@@ -129,8 +149,8 @@ export const currentVariablePointsByTsIdSelector = memoize(tsKey => createSelect
  * @param {String} tsKey
  * @return Array of Array of points
  */
-export const currentVariablePointsSelector = memoize(tsKey => createSelector(
-    pointsByTsKeySelector(tsKey),
+export const getCurrentVariablePoints = memoize(tsKey => createSelector(
+    getPointsByTsKey(tsKey),
     getCurrentVariableTimeSeries(tsKey),
     (points, timeSeries) => {
         return timeSeries ? Object.keys(timeSeries).map((tsId) => points[tsId]) : [];
@@ -145,24 +165,13 @@ export const currentVariablePointsSelector = memoize(tsKey => createSelector(
  * @param  {String} tsKey     Time series key
  * @return {Array}            Array of array of points.
  */
-export const pointsSelector = memoize((tsKey) => createSelector(
-    pointsByTsKeySelector(tsKey),
+export const getPoints = memoize((tsKey) => createSelector(
+    getPointsByTsKey(tsKey),
     (points) => {
         return Object.values(points);
     }
 ));
 
-/*
- * Returns an object which identifies which classes to use for the point
- * @param {Object} point
- * @return {Object}
- */
-export const classesForPoint = function(point) {
-    return {
-        approved: point.qualifiers.indexOf('A') > -1,
-        estimated: point.qualifiers.indexOf('e') > -1 || point.qualifiers.indexOf('E') > -1
-    };
-};
 
 /*
  * @ return {Array of Arrays of Objects} where the properties are date (universal), and value
@@ -226,9 +235,9 @@ export const getCurrentVariableMedianStatPoints = createSelector(
  * @param  {Object} state     Redux store
  * @return {Array}            Array of point arrays.
  */
-export const visiblePointsSelector = createSelector(
-    currentVariablePointsSelector('current'),
-    currentVariablePointsSelector('compare'),
+export const getVisiblePointsSelector = createSelector(
+    getCurrentVariablePoints('current'),
+    getCurrentVariablePoints('compare'),
     getCurrentVariableMedianStatPoints,
     (state) => state.ivTimeSeriesState.showIVTimeSeries,
     (current, compare, median, showSeries) => {
@@ -272,7 +281,7 @@ const getLineClasses = function(pt, isCurrentMethod) {
  * @return {Function} which returns an array of objects.
  */
 export const getCurrentPointData = createSelector(
-    currentVariablePointsByTsIdSelector('current'),
+    getCurrentVariablePointsByTsId('current'),
     getCurrentMethodID,
     getCurrentVariable,
     getIanaTimeZone,
@@ -320,8 +329,8 @@ export const getCurrentPointData = createSelector(
  * @param  {String} tsKey Time series key
  * @return {Object}  Keys are ts Ids, values are  of array of line segments.
  */
-export const lineSegmentsSelector = memoize((tsKey, period) => createSelector(
-    pointsByTsKeySelector(tsKey, period),
+export const getLineSegments = memoize((tsKey, period) => createSelector(
+    getPointsByTsKey(tsKey, period),
     getCurrentMethodID,
     (tsPoints, currentMethodID) => {
         let seriesLines = {};
@@ -379,8 +388,8 @@ export const lineSegmentsSelector = memoize((tsKey, period) => createSelector(
  * Factory function creates a function that, for a given tsKey:
  * @return {Object} - Mapping of parameter code Array of line segments.
  */
-export const lineSegmentsByParmCdSelector = memoize((tsKey, period) => createSelector(
-    lineSegmentsSelector(tsKey, period),
+export const getLineSegmentsByParmCd = memoize((tsKey, period) => createSelector(
+    getLineSegments(tsKey, period),
     getTimeSeriesForTsKey(tsKey, period),
     getVariables,
     (lineSegmentsBySeriesID, timeSeriesMap, variables) => {
@@ -402,7 +411,7 @@ export const lineSegmentsByParmCdSelector = memoize((tsKey, period) => createSel
  */
 export const currentVariableLineSegmentsSelector = memoize(tsKey => createSelector(
     getCurrentVariableTimeSeries(tsKey),
-    lineSegmentsSelector(tsKey),
+    getLineSegments(tsKey),
     (seriesMap, linesMap) => {
         return Object.keys(seriesMap).reduce((visMap, sID) => {
                 visMap[sID] = linesMap[sID];
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/drawing-data.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/drawing-data.spec.js
similarity index 94%
rename from assets/src/scripts/monitoring-location/components/hydrograph/drawing-data.spec.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/drawing-data.spec.js
index 4dda6492dbde275ef02f784218853d036bfa4d5e..d52cffbbb4a55634e0d2005d4d80efd2ef87b036 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/drawing-data.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/drawing-data.spec.js
@@ -2,16 +2,16 @@
 import {DateTime} from 'luxon';
 
 import {
-    lineSegmentsSelector,
-    pointsSelector,
-    allPointsSelector,
-    pointsByTsKeySelector,
+    getLineSegments,
+    getPoints,
+    getAllPoints,
+    getPointsByTsKey,
     classesForPoint,
-    lineSegmentsByParmCdSelector,
-    currentVariableLineSegmentsSelector,
-    currentVariablePointsSelector,
-    currentVariablePointsByTsIdSelector,
-    visiblePointsSelector,
+    getLineSegmentsByParmCdSelector,
+    getCurrentVariableLineSegments,
+    getCurrentVariablePoints,
+    getCurrentVariablePointsByTsId,
+    getVisiblePoints,
     getCurrentVariableMedianStatPoints,
     MAX_LINE_POINT_GAP,
     getCurrentPointData
@@ -204,9 +204,9 @@ const TEST_DATA = {
 
 describe('monitoring-location/components/hydrograph/drawingData module', () => {
 
-    describe('allPointsSelector', () => {
+    describe('getAllPoints', () => {
 
-        const result = allPointsSelector(TEST_DATA);
+        const result = getAllPoints(TEST_DATA);
         it('Return three time series', () => {
             expect(Object.keys(result).length).toBe(3);
             expect(result['69928:00060']).toBeDefined();
@@ -223,7 +223,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
 
         it('Return the empty object if there are no time series', () =>  {
-            expect(allPointsSelector({ivTimeSeriesData: {}})).toEqual({});
+            expect(getAllPoints({ivTimeSeriesData: {}})).toEqual({});
         });
 
         it('Resets the accumulator for precip if null value is encountered', () => {
@@ -265,13 +265,13 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
                 }
             };
 
-            expect(allPointsSelector(newTestData)['69930:00045'].map((point) => point.value)).toEqual([0.01, 0.03, null, 0.04]);
+            expect(getAllPoints(newTestData)['69930:00045'].map((point) => point.value)).toEqual([0.01, 0.03, null, 0.04]);
         });
     });
 
-    describe('pointsByTsKeySelector', () => {
+    describe('getPointsByTsKey', () => {
         it('Return the points array for the ts Key selector', () => {
-            const result = pointsByTsKeySelector('current')(TEST_DATA);
+            const result = getPointsByTsKey('current')(TEST_DATA);
 
             expect(Object.keys(result).length).toBe(2);
             expect(result['69928:00060']).toBeDefined();
@@ -279,33 +279,33 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
 
         it('return the empty object if no time series for series', () => {
-            expect(pointsByTsKeySelector('current:P30D:00010')(TEST_DATA)).toEqual({});
+            expect(getPointsByTsKey('current:P30D:00010')(TEST_DATA)).toEqual({});
         });
     });
 
-    describe('currentVariablePointsByTsIdSelector', () => {
+    describe('getCurrentVariablePointsByTsId', () => {
        it('Return the current variable for the tsKey', () => {
-           const result = currentVariablePointsByTsIdSelector('current')(TEST_DATA);
+           const result = getCurrentVariablePointsByTsId('current')(TEST_DATA);
 
            expect(result['69928:00060']).toBeDefined();
            expect(result['69928:00060']).toEqual(TEST_DATA.ivTimeSeriesData.timeSeries['69928:00060'].points);
        });
 
        it('Return an empty array if the tsKey has no time series with the current variable', () => {
-           expect(currentVariablePointsByTsIdSelector('compare')(TEST_DATA)).toEqual({});
+           expect(getCurrentVariablePointsByTsId('compare')(TEST_DATA)).toEqual({});
        });
     });
 
-    describe('currentVariablePointsSelector', () => {
+    describe('getCurrentVariablePoints', () => {
        it('Return the current variable for the tsKey', () => {
-           const result = currentVariablePointsSelector('current')(TEST_DATA);
+           const result = getCurrentVariablePoints('current')(TEST_DATA);
 
            expect(result.length).toBe(1);
            expect(result[0]).toEqual(TEST_DATA.ivTimeSeriesData.timeSeries['69928:00060'].points);
        });
 
        it('Return an empty array if the tsKey has no time series with the current variable', () => {
-           expect(currentVariablePointsSelector('compare')(TEST_DATA)).toEqual([]);
+           expect(getCurrentVariablePoints('compare')(TEST_DATA)).toEqual([]);
        });
     });
 
@@ -351,7 +351,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
 
     describe('line segment selector', () => {
         it('should separate on approved', () => {
-            expect(lineSegmentsSelector('current')({
+            expect(getLineSegments('current')({
                 ...TEST_DATA,
                 ivTimeSeriesData: {
                     ...TEST_DATA.ivTimeSeriesData,
@@ -420,7 +420,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
 
         it('should separate on estimated', () => {
-            expect(lineSegmentsSelector('current')({
+            expect(getLineSegments('current')({
                 ...TEST_DATA,
                 ivTimeSeriesData: {
                     ...TEST_DATA.ivTimeSeriesData,
@@ -494,7 +494,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
 
         it('should separate out masked values', () => {
-            expect(lineSegmentsSelector('current')({
+            expect(getLineSegments('current')({
                 ...TEST_DATA,
                 ivTimeSeriesData: {
                     ...TEST_DATA.ivTimeSeriesData,
@@ -584,7 +584,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
                 new Date(3 * MAX_LINE_POINT_GAP + 1),
                 new Date(3 * MAX_LINE_POINT_GAP + 2)
             ];
-            expect(lineSegmentsSelector('current')({
+            expect(getLineSegments('current')({
                 ...TEST_DATA,
                 ivTimeSeriesData: {
                     ...TEST_DATA.ivTimeSeriesData,
@@ -662,7 +662,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
                 new Date(3 * MAX_LINE_POINT_GAP + 1),
                 new Date(3 * MAX_LINE_POINT_GAP + 2)
             ];
-            expect(lineSegmentsSelector('current')({
+            expect(getLineSegments('current')({
                 ...TEST_DATA,
                 ivTimeSeriesData: {
                     ...TEST_DATA.ivTimeSeriesData,
@@ -723,7 +723,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
 
         it('Should not set currentMethod to true if method is selected', () => {
-            expect(lineSegmentsSelector('current')({
+            expect(getLineSegments('current')({
                 ...TEST_DATA,
                 ivTimeSeriesState : {
                     ...TEST_DATA.ivTimeSeriesState,
@@ -803,9 +803,9 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
     });
 
-    describe('lineSegmentsByParmCdSelector', () => {
+    describe('getLineSegmentsByParmCdSelector', () => {
         it('Should return two mappings for current time series', () => {
-            const result = lineSegmentsByParmCdSelector('current')(TEST_DATA);
+            const result = getLineSegmentsByParmCdSelector('current')(TEST_DATA);
 
             expect(Object.keys(result).length).toBe(2);
             expect(result['00060']).toBeDefined();
@@ -813,22 +813,22 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
     });
 
-    describe('currentVariableLineSegmentsSelector', () => {
+    describe('getCurrentVariableLineSegments', () => {
         it('Should return a single time series for current', () => {
-            const result = currentVariableLineSegmentsSelector('current')(TEST_DATA);
+            const result = getCurrentVariableLineSegments('current')(TEST_DATA);
 
             expect(Object.keys(result).length).toBe(1);
             expect(result['69928:00060']).toBeDefined();
         });
 
         it('Should return an empty object for the compare time series', () => {
-            expect(currentVariableLineSegmentsSelector('compare')(TEST_DATA)).toEqual({});
+            expect(getCurrentVariableLineSegments('compare')(TEST_DATA)).toEqual({});
         });
     });
 
-    describe('pointsSelector', () => {
+    describe('getPoints', () => {
         it('works with a single collection and two time series', () => {
-            expect(pointsSelector('current')({
+            expect(getPoints('current')({
                 ivTimeSeriesData: {
                     requests: {
                         current: {
@@ -887,7 +887,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         });
     });
 
-    describe('visiblePointsSelector', () => {
+    describe('getVisiblePoints', () => {
         const testData = {
             ...TEST_DATA,
             ivTimeSeriesState: {
@@ -901,7 +901,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
         };
 
         it('Return two arrays', () => {
-           expect(visiblePointsSelector(testData).length).toBe(2);
+           expect(getVisiblePoints(testData).length).toBe(2);
         });
 
         it('Expects one array if only median is not visible', () => {
@@ -917,7 +917,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
                 }
             };
 
-            expect(visiblePointsSelector(newTestData).length).toBe(1);
+            expect(getVisiblePoints(newTestData).length).toBe(1);
         });
 
         it('Expects an empty array if no visible series has the current variable', () => {
@@ -929,7 +929,7 @@ describe('monitoring-location/components/hydrograph/drawingData module', () => {
                 }
             };
 
-            expect(visiblePointsSelector(newTestData).length).toBe(0);
+            expect(getVisiblePoints(newTestData).length).toBe(0);
         });
     });
 
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/layout.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.js
similarity index 84%
rename from assets/src/scripts/monitoring-location/components/hydrograph/layout.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.js
index a3048f0b03bb20f454096dc5693855bb979959da..a59f62f64958692a4a6f07b3eb719eeb78e96e76 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/layout.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.js
@@ -4,13 +4,13 @@
 import memoize from 'fast-memoize';
 import {createSelector} from 'reselect';
 
-import config from '../../../config';
-import {mediaQuery} from '../../../utils';
+import config from '../../../../config';
+import {mediaQuery} from '../../../../utils';
 
-import {getCurrentParmCd} from '../../selectors/time-series-selector';
+import {getCurrentParmCd} from '../../../selectors/time-series-selector';
 
-import {tickSelector} from './domain';
-import {TEMPERATURE_PARAMETERS} from './time-series';
+import {getYTickDetails} from './domain';
+import {TEMPERATURE_PARAMETERS} from './time-series-data';
 
 
 export const ASPECT_RATIO = 1 / 2;
@@ -53,13 +53,13 @@ export const SPARK_LINE_DIM = {
 export const getLayout = memoize(kind => createSelector(
     (state) => state.ui.width,
     (state) => state.ui.windowWidth,
-    tickSelector,
+    getYTickDetails,
     getCurrentParmCd,
-    (width, windowWidth, tickDetails,parmCd) => {
+    (width, windowWidth, yTickDetails,parmCd) => {
         const isDesktop = mediaQuery(config.USWDS_SITE_MAX_WIDTH);
         const height = kind === 'BRUSH' ? isDesktop ? BRUSH_HEIGHT : BRUSH_MOBILE_HEIGHT : width * ASPECT_RATIO;
         const margin = isDesktop ? MARGIN : MARGIN_SMALL_DEVICE;
-        const tickLengths = tickDetails.tickValues.map(v => tickDetails.tickFormat(v).length);
+        const tickLengths = yTickDetails.tickValues.map(v => yTickDetails.tickFormat(v).length);
         const approxLabelLength = Math.max(...tickLengths) * 10;
         const isTemperatureParameter =
             TEMPERATURE_PARAMETERS.celsius.concat(TEMPERATURE_PARAMETERS.fahrenheit).includes(parmCd);
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/layout.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.spec.js
similarity index 100%
rename from assets/src/scripts/monitoring-location/components/hydrograph/layout.spec.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.spec.js
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/legend-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/legend-data.js
new file mode 100644
index 0000000000000000000000000000000000000000..21532fdad19db7b52785dcff943adb94c22cffb4
--- /dev/null
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/legend-data.js
@@ -0,0 +1,170 @@
+import {set} from 'd3-collection';
+import memoize from 'fast-memoize';
+import {createSelector} from 'reselect';
+
+import {defineLineMarker, defineRectangleMarker, defineTextOnlyMarker} from '../../../../d3-rendering/markers';
+
+import {getWaterwatchFloodLevels, isWaterwatchVisible} from '../../../selectors/flood-data-selector';
+import {getCurrentVariableMedianMetadata} from '../../../selectors/median-statistics-selector';
+
+import {getCurrentVariableLineSegments, HASH_ID, MASK_DESC} from './drawing-data';
+
+const TS_LABEL = {
+    'current': 'Current: ',
+    'compare': 'Last year: ',
+    'median': 'Median: '
+};
+
+/*
+ * Returns a Redux Selector function which returns an Object that represents the unique
+ * classes that are visible for the tsKey
+ *      @prop {Boolean} default
+ *      @prop {Boolean} approved
+ *      @prop {Boolean} estimated
+ *      @prop {D3 set} dataMask
+ */
+const getUniqueClasses = memoize(tsKey => createSelector(
+    getCurrentVariableLineSegments(tsKey),
+    (tsLineSegments) => {
+        let classes = [].concat(...Object.values(tsLineSegments)).map((line) => line.classes);
+        return {
+            default: classes.some((cls) => !cls.approved && !cls.estimated && !cls.dataMask),
+            approved: classes.some((cls) => cls.approved),
+            estimated: classes.some((cls) => cls.estimated),
+            dataMasks: set(classes.map((cls) => cls.dataMask).filter((mask) => {
+                return mask;
+            }))
+        };
+    }
+));
+
+/**
+ * Returns a Redux selector function that returns an object of attributes to be used
+ * to generate the legend markers. The properties will be undefined if not visible
+ *      @prop current {Object} - see getUniqueClasses
+ *      @prop compare {Object} - see getUniqueClasses
+ *      @prop median {Object} - median meta data - each property represents a time series for the current parameter code
+ *      @prop floodLevels {Object} -
+ */
+const getLegendDisplay = createSelector(
+    (state) => state.ivTimeSeriesState.showIVTimeSeries,
+    getCurrentVariableMedianMetadata,
+    getUniqueClasses('current'),
+    getUniqueClasses('compare'),
+    isWaterwatchVisible,
+    getWaterwatchFloodLevels,
+    (showSeries, medianSeries, currentClasses, compareClasses, showWaterWatch, floodLevels) => {
+        return {
+            current: showSeries.current ? currentClasses : undefined,
+            compare: showSeries.compare ? compareClasses : undefined,
+            median: showSeries.median ? medianSeries : undefined,
+            floodLevels: showWaterWatch ? floodLevels : undefined
+        };
+    }
+);
+
+const getTsMarkers = function(tsKey, uniqueClasses) {
+    let tsMarkers;
+    const maskMarkers = uniqueClasses.dataMasks.values().map((mask) => {
+        const maskName = MASK_DESC[mask];
+        const tsClass = `${maskName.replace(' ', '-').toLowerCase()}-mask`;
+        const fill = `url(#${HASH_ID[tsKey]})`;
+        return defineRectangleMarker(null, `mask ${tsClass}`, maskName, fill);
+    });
+
+    let lineMarkers = [];
+    if (uniqueClasses.default) {
+        lineMarkers.push(defineLineMarker(null, `line-segment ts-${tsKey}`, 'Provisional'));
+    }
+    if (uniqueClasses.approved) {
+        lineMarkers.push(defineLineMarker(null, `line-segment approved ts-${tsKey}`, 'Approved'));
+    }
+    if (uniqueClasses.estimated) {
+        lineMarkers.push(defineLineMarker(null, `line-segment estimated ts-${tsKey}`, 'Estimated'));
+    }
+
+    if (lineMarkers.length || maskMarkers.length) {
+        tsMarkers = [defineTextOnlyMarker(TS_LABEL[tsKey]), ...lineMarkers, ...maskMarkers];
+    }
+    return tsMarkers;
+};
+
+/*
+ * @param {Object} medianMetData
+ * @return {Array of Array} - each subarray rpresents the markes for a time series median data
+ */
+const getMedianMarkers = function(medianMetaData) {
+    return Object.values(medianMetaData).map((stats, index) => {
+        // Get the unique non-null years, in chronological order
+        let years = [];
+        if (stats.beginYear) {
+            years.push(stats.beginYear);
+        }
+        if (stats.endYear && stats.beginYear !== stats.endYear) {
+            years.push(stats.endYear);
+        }
+        const dateText = years.join(' - ');
+
+        const descriptionText = stats.methodDescription ? `${stats.methodDescription} ` : '';
+        const classes = `median-data-series median-step median-step-${index % 6}`;
+        const label = `${descriptionText}${dateText}`;
+
+        return [defineTextOnlyMarker(TS_LABEL.median), defineLineMarker(null, classes, label)];
+    });
+};
+
+const getFloodLevelMarkers = function(floodLevels) {
+    const FLOOD_LEVEL_DISPLAY = {
+        actionStage: {
+            label: 'Action Stage',
+            class: 'action-stage'
+        },
+        floodStage: {
+            label: 'Flood Stage',
+            class: 'flood-stage'
+        },
+        moderateFloodStage: {
+            label: 'Moderate Flood Stage',
+            class: 'moderate-flood-stage'
+        },
+        majorFloodStage: {
+            label: 'Major Flood Stage',
+            class: 'major-flood-stage'
+        }
+    };
+    return Object.keys(floodLevels).map((stage) => {
+        return [
+            defineTextOnlyMarker(FLOOD_LEVEL_DISPLAY[stage].label),
+            defineLineMarker(
+                null,
+                `waterwatch-data-series ${FLOOD_LEVEL_DISPLAY[stage].class}`,
+                `${floodLevels[stage]} ft`)
+        ];
+    });
+};
+
+
+/*
+ * Factory function  that returns an array of array of markers to be used for the
+ * time series graph legend
+ * @return {Array of Array} of markers - each sub array represents an row of markers
+ */
+export const getLegendMarkerRows = createSelector(
+    getLegendDisplay,
+    (displayItems) => {
+        const markerRows = [];
+        const currentTsMarkerRow = displayItems.current ? getTsMarkers('current', displayItems.current) : undefined;
+        const compareTsMarkerRow = displayItems.compare ? getTsMarkers('compare', displayItems.compare) : undefined;
+        const medianMarkerRows = displayItems.median ? getMedianMarkers(displayItems.median) : [];
+        const floodMarkerRows = displayItems.floodLevels ? getFloodLevelMarkers(displayItems.floodLevels) : [];
+
+        if (currentTsMarkerRow) {
+            markerRows.push(currentTsMarkerRow);
+        }
+        if (compareTsMarkerRow) {
+            markerRows.push(compareTsMarkerRow);
+        }
+        markerRows.push(...medianMarkerRows, ...floodMarkerRows);
+        return markerRows;
+    }
+);
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/legend-data.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/legend-data.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..6a8ddac57204a1d6680345bc044d230dd0c99b9a
--- /dev/null
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/legend-data.spec.js
@@ -0,0 +1,173 @@
+import {lineMarker, rectangleMarker, textOnlyMarker} from '../../../../d3-rendering/markers';
+
+import {getLegendMarkerRows} from './legend-data';
+
+describe('monitoring-location/components/hydrograph/selectors/legend-data', () => {
+    const TEST_DATA = {
+        ivTimeSeriesData: {
+            timeSeries: {
+                '00060:current': {
+                    tsKey: 'current:P7D',
+                    startTime: new Date('2018-03-06T15:45:00.000Z'),
+                    endTime: new Date('2018-03-13T13:45:00.000Z'),
+                    variable: '45807197',
+                    points: [{
+                        value: 10,
+                        qualifiers: ['P'],
+                        approved: false,
+                        estimated: false
+                    }, {
+                        value: null,
+                        qualifiers: ['P', 'ICE'],
+                        approved: false,
+                        estimated: false
+                    }, {
+                        value: null,
+                        qualifiers: ['P', 'FLD'],
+                        approved: false,
+                        estimated: false
+                    }]
+                },
+
+                '00060:compare': {
+                    tsKey: 'compare:P7D',
+                    startTime: new Date('2018-03-06T15:45:00.000Z'),
+                    endTime: new Date('2018-03-06T15:45:00.000Z'),
+                    variable: '45807202',
+                    points: [{
+                        value: 1,
+                        qualifiers: ['A'],
+                        approved: false,
+                        estimated: false
+                    }, {
+                        value: 2,
+                        qualifiers: ['A'],
+                        approved: false,
+                        estimated: false
+                    }, {
+                        value: 3,
+                        qualifiers: ['E'],
+                        approved: false,
+                        estimated: false
+                    }]
+                }
+            },
+            variables: {
+                '45807197': {
+                    variableCode: {value: '00060'},
+                    variableName: 'Streamflow',
+                    variableDescription: 'Discharge, cubic feet per second',
+                    oid: '45807197'
+                },
+                '45807202': {
+                    variableCode: {value: '00065'},
+                    variableName: 'Gage height',
+                    oid: '45807202'
+                }
+            }
+        },
+        statisticsData: {
+            median: {
+                '00060': {
+                    '1': [{
+                        month_nu: '2',
+                        day_nu: '25',
+                        p50_va: '43',
+                        begin_yr: '1970',
+                        end_yr: '2017',
+                        loc_web_ds: 'This method'
+                    }]
+                }
+            }
+        },
+        ivTimeSeriesState: {
+            currentIVVariableID: '45807197',
+            currentIVDateRangeKind: 'P7D',
+            showIVTimeSeries: {
+                current: true,
+                compare: true,
+                median: true
+            }
+        },
+        floodData: {
+            floodLevels: {
+                site_no: '07144100',
+                action_stage: '20',
+                flood_stage: '22',
+                moderate_flood_stage: '25',
+                major_flood_stage: '26'
+            }
+        }
+    };
+
+    describe('getLegendMarkerRows', () => {
+
+        it('Should return no markers if no time series to show', () => {
+            let newData = {
+                ...TEST_DATA,
+                ivTimeSeriesData: {
+                    ...TEST_DATA.ivTimeSeriesData,
+                    timeSeries: {}
+                },
+                statisticsData: {},
+                floodState: {}
+            };
+
+            expect(getLegendMarkerRows(newData)).toEqual([]);
+        });
+
+        it('Should return markers for the selected variable', () => {
+            const result = getLegendMarkerRows(TEST_DATA);
+
+            expect(result.length).toBe(2);
+            expect(result[0].length).toBe(4);
+            expect(result[0][0].type).toEqual(textOnlyMarker);
+            expect(result[0][1].type).toEqual(lineMarker);
+            expect(result[0][2].type).toEqual(rectangleMarker);
+            expect(result[0][3].type).toEqual(rectangleMarker);
+            expect(result[1].length).toBe(2);
+            expect(result[1][0].type).toEqual(textOnlyMarker);
+            expect(result[1][1].type).toEqual(lineMarker);
+        });
+
+        it('Should return markers for a different selected variable', () => {
+            const newData = {
+                ...TEST_DATA,
+                ivTimeSeriesState: {
+                    ...TEST_DATA.ivTimeSeriesState,
+                    currentIVVariableID: '45807202'
+                }
+            };
+            const result = getLegendMarkerRows(newData);
+
+            expect(result.length).toBe(5);
+            expect(result[0].length).toBe(3);
+            expect(result[0][0].type).toEqual(textOnlyMarker);
+            expect(result[0][1].type).toEqual(lineMarker);
+            expect(result[0][2].type).toEqual(lineMarker);
+        });
+
+        it('Should return markers only for time series shown', () => {
+            const newData = {
+                ...TEST_DATA,
+                ivTimeSeriesState: {
+                    ...TEST_DATA.ivTimeSeriesState,
+                    showIVTimeSeries: {
+                        'current': true,
+                        'compare': false,
+                        'median': false
+                    }
+                }
+            };
+
+            const result = getLegendMarkerRows(newData);
+
+            expect(result.length).toBe(1);
+            expect(result[0].length).toBe(4);
+            expect(result[0][0].type).toEqual(textOnlyMarker);
+            expect(result[0][1].type).toEqual(lineMarker);
+            expect(result[0][2].type).toEqual(rectangleMarker);
+            expect(result[0][3].type).toEqual(rectangleMarker);
+        });
+    });
+});
\ No newline at end of file
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js
new file mode 100644
index 0000000000000000000000000000000000000000..400cba08e6d989410e656c35313118c8bfd31938
--- /dev/null
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.js
@@ -0,0 +1,41 @@
+import {createSelector} from 'reselect';
+
+import {sortedParameters} from '../../../../utils';
+
+import {getCurrentVariableID, getTimeSeries, getVariables} from '../../../selectors/time-series-selector';
+
+/**
+ * Returns a Redux selector function which returns an sorted array of metadata
+ * for each available parameter code. Each object has the following properties:
+ *      @prop {String} variableID
+ *      @prop {String} parameterCode
+ *      @prop {String} description
+ *      @prop {Boolean} selected - True if this is the currently selected parameter
+ *      @prop {Number} timeSeriesCount - count of unique time series for this parameter
+ */
+export const getAvailableParameterCodes = createSelector(
+    getVariables,
+    getTimeSeries,
+    getCurrentVariableID,
+    (variables, timeSeries, currentVariableID) => {
+        if (!variables) {
+            return [];
+        }
+
+        const seriesList = Object.values(timeSeries);
+        const availableVariableIds = seriesList.map(x => x.variable);
+        return sortedParameters(variables)
+            .filter(variable => availableVariableIds.includes(variable.oid))
+            .map((variable) => {
+                return {
+                    variableID: variable.oid,
+                    parameterCode: variable.variableCode.value,
+                    description: variable.variableDescription,
+                    selected: currentVariableID === variable.oid,
+                    timeSeriesCount: seriesList.filter(ts => {
+                        return ts.tsKey === 'current:P7D' && ts.variable === variable.oid;
+                    }).length
+                };
+            });
+    }
+);
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..f61db3bc44b8fd5fd26ef3ee47abfb0a3e2d3de8
--- /dev/null
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/parameter-data.spec.js
@@ -0,0 +1,169 @@
+
+import {getAvailableParameterCodes} from './parameter-data';
+
+describe('monitoring-location/components/hydrograph/selectors/parameter-data', () => {
+    describe('getAvailableParameterCodes', () => {
+        it('sets attributes correctly when all series have data points', () => {
+            const available = getAvailableParameterCodes({
+                ivTimeSeriesData: {
+                    timeSeries: {
+                        'current:00060': {description: '00060', tsKey: 'current:P7D', variable: 'code0', points: [{x: 1, y: 2}]},
+                        'current:00061': {description: '00061', tsKey: 'current:P7D', variable: 'code1', points: [{x: 2, y: 3}]},
+                        'current:00062': {description: '00062', tsKey: 'current:P7D', variable: 'code2', points: [{x: 3, y: 4}]},
+                        'compare:00061': {description: '00061', tsKey: 'compare:P7D', variable: 'code1', points: [{x: 1, y: 17}]},
+                        'compare:00062': {description: '00062', tsKey: 'compare:P7D', variable: 'code2', points: [{x: 2, y: 18}]},
+                        'compare:00063': {description: '00063', tsKey: 'compare:P7D', variable: 'code3', points: [{x: 3, y: 46}]}
+                    },
+                    variables: {
+                        'code0': {
+                            oid: 'code0',
+                            variableDescription: 'code0 desc',
+                            variableCode: {
+                                value: '00060'
+                            }
+                        },
+                        'code1': {
+                            oid: 'code1',
+                            variableDescription: 'code1 desc',
+                            variableCode: {
+                                value: '00061'
+                            }
+                        },
+                        'code2': {
+                            oid: 'code2',
+                            variableDescription: 'code2 desc',
+                            variableCode: {
+                                value: '00062'
+                            }
+                        },
+                        'code3': {
+                            oid: 'code3',
+                            variableDescription: 'code3 desc',
+                            variableCode: {
+                                value: '00063'
+                            }
+                        }
+                    }
+                },
+                ivTimeSeriesState: {
+                    currentIVVariableID: 'code0'
+                }
+            });
+            // Series are ordered by parameter code and have expected values.
+            expect(available).toEqual([
+                ['00060', {variableID: 'code0', description: 'code0 desc', selected: true, currentTimeSeriesCount: 1}],
+                ['00061', {variableID: 'code1', description: 'code1 desc', selected: false, currentTimeSeriesCount: 1}],
+                ['00062', {variableID: 'code2', description: 'code2 desc', selected: false, currentTimeSeriesCount: 1}]
+            ]);
+        });
+
+        it('sets attributes correctly when not all series have data points', () => {
+            const available = getAvailableParameterCodes({
+                ivTimeSeriesData: {
+                    timeSeries: {
+                        'current:00060': {description: '00060', tsKey: 'current:P7D', variable: 'code0', points: [{x: 1, y: 2}]},
+                        'current:00061': {description: '00061', tsKey: 'current:P7D', variable: 'code1', points: [{x: 2, y: 3}]},
+                        'current:00062': {description: '00062', tsKey: 'current:P7D', variable: 'code2', points: [{x: 3, y: 4}]},
+                        'compare:00061': {description: '00061', tsKey: 'compare:P7D', variable: 'code1', points: []},
+                        'compare:00062': {description: '00062', tsKey: 'compare:P7D', variable: 'code2', points: [{x: 2, y: 18}]},
+                        'compare:00063': {description: '00063', tsKey: 'compare:P7D', variable: 'code3', points: [{x: 3, y: 46}]}
+                    },
+                    variables: {
+                        'code0': {
+                            oid: 'code0',
+                            variableDescription: 'code0 desc',
+                            variableCode: {
+                                value: '00060'
+                            }
+                        },
+                        'code1': {
+                            oid: 'code1',
+                            variableDescription: 'code1 desc',
+                            variableCode: {
+                                value: '00061'
+                            }
+                        },
+                        'code2': {
+                            oid: 'code2',
+                            variableDescription: 'code2 desc',
+                            variableCode: {
+                                value: '00062'
+                            }
+                        },
+                        'code3': {
+                            oid: 'code3',
+                            variableDescription: 'code3 desc',
+                            variableCode: {
+                                value: '00063'
+                            }
+                        }
+                    }
+                },
+                ivTimeSeriesState: {
+                    currentIVVariableID: 'code0'
+                }
+            });
+            // Series are ordered by parameter code and have expected values.
+            expect(available).toEqual([
+                ['00060', {variableID: 'code0', description: 'code0 desc', selected: true, currentTimeSeriesCount: 1}],
+                ['00061', {variableID: 'code1', description: 'code1 desc', selected: false, currentTimeSeriesCount: 1}],
+                ['00062', {variableID: 'code2', description: 'code2 desc', selected: false, currentTimeSeriesCount: 1}]
+            ]);
+        });
+
+        it('time series without data points are considered available', () => {
+            const available = getAvailableParameterCodes({
+                ivTimeSeriesData: {
+                    timeSeries: {
+                        'current:00060': {description: '00060', tsKey: 'current:P7D', variable: 'code0', points: [{x: 1, y: 2}]},
+                        'current:00061': {description: '00061', tsKey: 'current:P7D', variable: 'code1', points: []},
+                        'current:00062': {description: '00062', tsKey: 'current:P7D', variable: 'code2', points: [{x: 3, y: 4}]},
+                        'compare:00061': {description: '00061', tsKey: 'compare:P7D', variable: 'code1', points: []},
+                        'compare:00062': {description: '00062', tsKey: 'compare:P7D', variable: 'code2', points: [{x: 2, y: 18}]},
+                        'compare:00063': {description: '00063', tsKey: 'compare:P7D', variable: 'code3', points: [{x: 3, y: 46}]}
+                    },
+                    variables: {
+                        'code0': {
+                            oid: 'code0',
+                            variableDescription: 'code0 desc',
+                            variableCode: {
+                                value: '00060'
+                            }
+                        },
+                        'code1': {
+                            oid: 'code1',
+                            variableDescription: 'code1 desc',
+                            variableCode: {
+                                value: '00061'
+                            }
+                        },
+                        'code2': {
+                            oid: 'code2',
+                            variableDescription: 'code2 desc',
+                            variableCode: {
+                                value: '00062'
+                            }
+                        },
+                        'code3': {
+                            oid: 'code3',
+                            variableDescription: 'code3 desc',
+                            variableCode: {
+                                value: '00063'
+                            }
+                        }
+                    }
+                },
+                ivTimeSeriesState: {
+                    currentIVVariableID: 'code0'
+                }
+            });
+            // Series are ordered by parameter code and have expected values.
+            expect(available).toEqual([
+                ['00060', {variableID: 'code0', description: 'code0 desc', selected: true, currentTimeSeriesCount: 1}],
+                ['00061', {variableID: 'code1', description: 'code1 desc', selected: false, currentTimeSeriesCount: 1}],
+                ['00062', {variableID: 'code2', description: 'code2 desc', selected: false, currentTimeSeriesCount: 1}]
+            ]);
+        });
+    });
+
+});
\ No newline at end of file
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/scales.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js
similarity index 86%
rename from assets/src/scripts/monitoring-location/components/hydrograph/scales.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js
index 02bb6df1999e223762bebbf976f1fdeb19ff911e..dc62f286f9927cb5b9648d02aa1232d4c5daa4ec 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/scales.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js
@@ -2,13 +2,13 @@ import {scaleLinear, scaleSymlog} from 'd3-scale';
 import memoize from 'fast-memoize';
 import {createSelector} from 'reselect';
 
-import {getVariables, getCurrentParmCd, getRequestTimeRange, getTimeSeriesForTsKey} from '../../selectors/time-series-selector';
-import {convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../../utils';
+import {getVariables, getCurrentParmCd, getRequestTimeRange, getTimeSeriesForTsKey} from '../../../selectors/time-series-selector';
+import {convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../../../utils';
 
 import {getYDomain, SYMLOG_PARMS} from './domain';
-import {visiblePointsSelector, pointsByTsKeySelector} from './drawing-data';
+import {getPointsByTsKey} from './drawing-data';
 import {getLayout} from './layout';
-import {TEMPERATURE_PARAMETERS} from './time-series';
+import {TEMPERATURE_PARAMETERS} from './time-series-data';
 
 const REVERSE_AXIS_PARMS = [
     '72019',
@@ -20,6 +20,10 @@ const REVERSE_AXIS_PARMS = [
     '72148'
 ];
 
+/* The two create* functions are helper functions. They are exported primariy
+ * for ease of testing
+ */
+
 /**
  * Create an x-scale oriented on the left
  * @param {Array} timeRange - Object containing the start and end times.
@@ -101,10 +105,9 @@ export const getBrushXScale = (tsKey) => getXScale('BRUSH', tsKey);
  */
 export const getYScale = memoize(kind => createSelector(
     getLayout(kind),
-    visiblePointsSelector,
+    getYDomain,
     getCurrentParmCd,
-    (layout, pointArrays, currentVarParmCd) => {
-        const yDomain = getYDomain(pointArrays, currentVarParmCd);
+    (layout, yDomain, currentVarParmCd) => {
         return createYScale(currentVarParmCd, yDomain, layout.height - (layout.margin.top + layout.margin.bottom));
     }
 ));
@@ -114,11 +117,10 @@ export const getBrushYScale = getYScale('BRUSH');
 
 export const getSecondaryYScale = memoize(kind => createSelector(
     getLayout(kind),
-    visiblePointsSelector,
+    getYDomain,
     getCurrentParmCd,
-    (layout, pointArrays, currentVarParmCd) => {
-        const yDomain = getYDomain(pointArrays, currentVarParmCd);
-        let convertedYDomain = [0, 1];
+    (layout, yDomain, currentVarParmCd) => {
+        let convertedYDomain;
         if (TEMPERATURE_PARAMETERS.celsius.includes(currentVarParmCd)) {
             convertedYDomain = yDomain.map(celsius => convertCelsiusToFahrenheit(celsius));
         } else if (TEMPERATURE_PARAMETERS.fahrenheit.includes(currentVarParmCd)) {
@@ -139,8 +141,8 @@ export const getSecondaryYScale = memoize(kind => createSelector(
  * @param  {String} tsKey             Time series key
  * @return {Object} - keys are parmCd and values are array of array of points
  */
-const parmCdPointsSelector = memoize((tsKey, period) => createSelector(
-    pointsByTsKeySelector(tsKey, period),
+const getParmCdPoints = memoize((tsKey, period) => createSelector(
+    getPointsByTsKey(tsKey, period),
     getTimeSeriesForTsKey(tsKey, period),
     getVariables,
     (tsPoints, timeSeries, variables) => {
@@ -160,8 +162,8 @@ const parmCdPointsSelector = memoize((tsKey, period) => createSelector(
  * Returns x and y scales for all "current" time series.
  * @type {Object}   Mapping of parameter code to time series list.
  */
-export const timeSeriesScalesByParmCdSelector = memoize((tsKey, period, dimensions) => createSelector(
-    parmCdPointsSelector(tsKey, period),
+export const getTimeSeriesScalesByParmCd= memoize((tsKey, period, dimensions) => createSelector(
+    getParmCdPoints(tsKey, period),
     getRequestTimeRange(tsKey, period),
     (pointsByParmCd, requestTimeRange) => {
         return Object.keys(pointsByParmCd).reduce((tsScales, parmCd) => {
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/scales.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.spec.js
similarity index 100%
rename from assets/src/scripts/monitoring-location/components/hydrograph/scales.spec.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.spec.js
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/time-series.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js
similarity index 71%
rename from assets/src/scripts/monitoring-location/components/hydrograph/time-series.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js
index 632e20e038f9658096be114cc7b382919b0ccaab..e0f13af46586f988c126ed3111920edf9d3e1b6e 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/time-series.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js
@@ -5,8 +5,8 @@ import {createSelector} from 'reselect';
 import {
     getRequestTimeRange, getCurrentVariable, getTimeSeriesForTsKey, getCurrentParmCd, getCurrentMethodID,
     getMethods
-} from '../../selectors/time-series-selector';
-import {getIanaTimeZone} from '../../selectors/time-zone-selector';
+} from '../../../selectors/time-series-selector';
+import {getIanaTimeZone} from '../../../selectors/time-zone-selector';
 
 
 export const TEMPERATURE_PARAMETERS = {
@@ -47,7 +47,6 @@ export const hasTimeSeriesWithPoints = memoize((tsKey, period) => createSelector
         return seriesWithPoints.length > 0;
 }));
 
-
 /**
  * Factory function creates a function that:
  * Returns the current show state of a time series.
@@ -55,22 +54,23 @@ export const hasTimeSeriesWithPoints = memoize((tsKey, period) => createSelector
  * @param  {String}  tsKey Time series key
  * @return {Boolean}           Show state of the time series
  */
-export const isVisibleSelector = memoize(tsKey => (state) => {
+export const isVisible = memoize(tsKey => (state) => {
     return state.ivTimeSeriesState.showIVTimeSeries[tsKey];
 });
 
 
-
 /**
- * @return {String}     The label for the y-axis
+ * Returns a Redux selector function which returns the label to be used for the Y axis
  */
-export const yLabelSelector = createSelector(
+export const getYLabel = createSelector(
     getCurrentVariable,
     variable => variable ? variable.variableDescription : ''
 );
 
-
-export const secondaryYLabelSelector = createSelector(
+/*
+ * Returns a Redux selector function which returns the label to be used for the secondary y axis
+ */
+export const getSecondaryYLabel= createSelector(
     getCurrentParmCd,
     parmCd => {
         let secondaryYLabel = null;
@@ -85,9 +85,9 @@ export const secondaryYLabelSelector = createSelector(
 
 
 /**
- * @return {String}     The title to include in the hyrdograph, will include method description if defined.
+ * Returns a Redux selector function which returns the title to be used for the hydrograph
  */
-export const titleSelector = createSelector(
+export const getTitle = createSelector(
     getCurrentVariable,
     getCurrentMethodID,
     getMethods,
@@ -101,11 +101,10 @@ export const titleSelector = createSelector(
 );
 
 
-/**
- * @return {String}     Description for the currently display set of time
- *                      series
+/*
+ * Returns a Redux selector function which returns the description of the hydrograph
  */
-export const descriptionSelector = createSelector(
+export const getDescription = createSelector(
     getCurrentVariable,
     getRequestTimeRange('current', 'P7D'),
     getIanaTimeZone,
@@ -120,14 +119,18 @@ export const descriptionSelector = createSelector(
 );
 
 /**
- * Select the time zone. If the time zone is null, use `local` as the time zone
- *
- * @ return {String} - IANA time zone
- *
+ * Returns a Redux selector function which returns the iana time zone or local if none is set
  */
-export const tsTimeZoneSelector = createSelector(
+export const getTsTimeZone= createSelector(
     getIanaTimeZone,
     ianaTimeZone => {
         return ianaTimeZone !== null ? ianaTimeZone : 'local';
     }
 );
+
+export const getQualifiers = state => state.ivTimeSeriesData.qualifiers;
+
+export const getCurrentVariableUnitCode = createSelector(
+    getCurrentVariable,
+    variable => variable ? variable.unit.unitCode : null
+);
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/time-series.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.spec.js
similarity index 89%
rename from assets/src/scripts/monitoring-location/components/hydrograph/time-series.spec.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.spec.js
index f71402f1bdc8e0dc778847144047e747a073b6b3..31e5d442b88f8821578c64997ea25d88b385e50a 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/time-series.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.spec.js
@@ -1,6 +1,6 @@
 import {
-    hasTimeSeriesWithPoints, isVisibleSelector, yLabelSelector, titleSelector,
-    descriptionSelector, tsTimeZoneSelector, secondaryYLabelSelector} from './time-series';
+    hasTimeSeriesWithPoints, isVisible, getYLabel, getTitle,
+    getDescription, getTsTimeZone, getSecondaryYLabel} from './time-series-data';
 
 
 const TEST_DATA = {
@@ -227,7 +227,7 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
         });
     });
 
-    describe('isVisibleSelector', () => {
+    describe('isVisible', () => {
         it('Returns whether the time series is visible', () => {
             const store = {
                 ivTimeSeriesState: {
@@ -239,19 +239,19 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
                 }
             };
 
-            expect(isVisibleSelector('current')(store)).toBe(true);
-            expect(isVisibleSelector('compare')(store)).toBe(false);
-            expect(isVisibleSelector('median')(store)).toBe(true);
+            expect(isVisible('current')(store)).toBe(true);
+            expect(isVisible('compare')(store)).toBe(false);
+            expect(isVisible('median')(store)).toBe(true);
         });
     });
 
     describe('yLabelSelector', () => {
         it('Returns string to be used for labeling the y axis', () => {
-            expect(yLabelSelector(TEST_DATA)).toBe('Discharge, cubic feet per second');
+            expect(getYLabel(TEST_DATA)).toBe('Discharge, cubic feet per second');
         });
 
         it('Returns empty string if no variable selected', () => {
-            expect(yLabelSelector({
+            expect(getYLabel({
                 ...TEST_DATA,
                 ivTimeSeriesState: {
                     ...TEST_DATA.ivTimeSeriesState,
@@ -261,9 +261,9 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
         });
     });
 
-    describe('secondaryYLabelSelector', () => {
+    describe('getSecondaryYLabel', () => {
         it('returns a secondary label when a celsius parameter is selected', () => {
-             expect(secondaryYLabelSelector({
+             expect(getSecondaryYLabel({
                  ...TEST_DATA,
                  ivTimeSeriesState: {
                      ...TEST_DATA.ivTimeSeriesState,
@@ -273,7 +273,7 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
         });
 
         it('returns a secondary label when a fahrenheit parameter is selected', () => {
-             expect(secondaryYLabelSelector({
+             expect(getSecondaryYLabel({
                  ...TEST_DATA,
                  ivTimeSeriesState: {
                      ...TEST_DATA.ivTimeSeriesState,
@@ -283,16 +283,16 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
         });
 
         it('does not return a secondary when a parameter when a non-temperature parameter is selected', () => {
-             expect(secondaryYLabelSelector(TEST_DATA)).toBeNull();
+             expect(getSecondaryYLabel(TEST_DATA)).toBeNull();
         });
     });
 
-    describe('titleSelector', () => {
+    describe('getTitle', () => {
         it('Returns the string to used for graph title', () => {
-            expect(titleSelector(TEST_DATA)).toBe('Streamflow');
+            expect(getTitle(TEST_DATA)).toBe('Streamflow');
         });
         it('Returns the title string with the method description appended', () => {
-            expect(titleSelector({
+            expect(getTitle({
                 ...TEST_DATA,
                 ivTimeSeriesState: {
                     ...TEST_DATA.ivTimeSeriesState,
@@ -301,7 +301,7 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
             })).toBe('Streamflow' + ', ' + '4.1 ft from riverbed (middle)');
         });
         it('Returns empty string if no variable selected', () => {
-            expect(titleSelector({
+            expect(getTitle({
                 ...TEST_DATA,
                 ivTimeSeriesState: {
                     ...TEST_DATA.ivTimeSeriesState,
@@ -311,9 +311,9 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
         });
     });
 
-    describe('descriptionSelector', () => {
+    describe('getDescription', () => {
         it('Returns a description with the date for the current times series', () => {
-            const result = descriptionSelector(TEST_DATA);
+            const result = getDescription(TEST_DATA);
 
             expect(result).toContain('Discharge, cubic feet per second');
             expect(result).toContain('1/2/2017');
@@ -321,24 +321,24 @@ describe('monitoring-location/components/hydrograph/time-series module', () => {
         });
     });
 
-    describe('tsTimeZoneSelector', () => {
+    describe('getTsTimeZone', () => {
 
         it('Returns local if series is empty', () => {
-            const result = tsTimeZoneSelector({
+            const result = getTsTimeZone({
                 series: {}
             });
             expect(result).toEqual('local');
         });
 
         it('Returns local if timezone is null', () => {
-            const result = tsTimeZoneSelector({
+            const result = getTsTimeZone({
                 ianaTimeZone: null
             });
             expect(result).toEqual('local');
         });
 
         it('Returns the IANA timezone NWIS and IANA agree', () => {
-            const result = tsTimeZoneSelector({
+            const result = getTsTimeZone({
                 ianaTimeZone: 'America/New_York'
             });
             expect(result).toEqual('America/New_York');
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/time-series-graph.js b/assets/src/scripts/monitoring-location/components/hydrograph/time-series-graph.js
index 37939cd82b37dad2fdbea30909c5d8b3be192af1..c0696481b32cdc4e13b4052674ac5fd93dd8bc55 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/time-series-graph.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/time-series-graph.js
@@ -9,18 +9,18 @@ import {link} from '../../../lib/d3-redux';
 import {mediaQuery}  from '../../../utils';
 
 import {getAgencyCode, getMonitoringLocationName} from '../../selectors/time-series-selector';
-import {waterwatchVisible, getWaterwatchFloodLevels} from '../../selectors/flood-data-selector';
+import {isWaterwatchVisible, getWaterwatchFloodLevels} from '../../selectors/flood-data-selector';
 
-import {getAxes}  from './axes';
+import {getAxes}  from './selectors/axes';
 import {
-    currentVariableLineSegmentsSelector,
+    getCurrentVariableLineSegments,
     getCurrentVariableMedianStatPoints,
     HASH_ID
 } from './drawing-data';
-import {getMainLayout} from './layout';
-import {getMainXScale, getMainYScale, getBrushXScale} from './scales';
-import {descriptionSelector, isVisibleSelector, titleSelector} from './time-series';
-import {drawDataLines} from './time-series-data';
+import {getMainLayout} from './selectors/layout';
+import {getMainXScale, getMainYScale, getBrushXScale} from './selectors/scales';
+import {descriptionSelector, isVisibleSelector, titleSelector} from './selectors/time-series-data';
+import {drawDataLines} from './time-series-lines';
 import {drawTooltipFocus, drawTooltipText}  from './tooltip';
 
 const addDefsPatterns = function(elem) {
@@ -236,7 +236,7 @@ export const drawTimeSeriesGraph = function(elem, store, siteNo, showMLName, sho
         .call(link(store, appendAxes, getAxes()))
         .call(link(store, drawDataLines, createStructuredSelector({
             visible: isVisibleSelector('current'),
-            tsLinesMap: currentVariableLineSegmentsSelector('current'),
+            tsLinesMap: getCurrentVariableLineSegments('current'),
             xScale: getMainXScale('current'),
             yScale: getMainYScale,
             tsKey: () => 'current',
@@ -244,7 +244,7 @@ export const drawTimeSeriesGraph = function(elem, store, siteNo, showMLName, sho
         })))
         .call(link(store, drawDataLines, createStructuredSelector({
             visible: isVisibleSelector('compare'),
-            tsLinesMap: currentVariableLineSegmentsSelector('compare'),
+            tsLinesMap: getCurrentVariableLineSegments('compare'),
             xScale: getMainXScale('compare'),
             yScale: getMainYScale,
             tsKey: () => 'compare',
@@ -257,7 +257,7 @@ export const drawTimeSeriesGraph = function(elem, store, siteNo, showMLName, sho
             seriesPoints: getCurrentVariableMedianStatPoints
         })))
        .call(link(store, plotAllFloodLevelPoints, createStructuredSelector({
-            visible: waterwatchVisible,
+            visible: isWaterwatchVisible,
             xscale: getBrushXScale('current'),
             yscale: getMainYScale,
             seriesPoints: getWaterwatchFloodLevels
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/time-series-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/time-series-lines.js
similarity index 100%
rename from assets/src/scripts/monitoring-location/components/hydrograph/time-series-data.js
rename to assets/src/scripts/monitoring-location/components/hydrograph/time-series-lines.js
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.js b/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.js
index 14aa7d2e340c7c6d4d6fa0cef60357a39e763778..10eb1ad81a1d5b92ed0758f4601788d711573388 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.js
@@ -1,7 +1,6 @@
 import {set} from 'd3-collection';
 import {select} from 'd3-selection';
 import {transition} from 'd3-transition';
-import memoize from 'fast-memoize';
 import {DateTime} from 'luxon';
 import {createSelector, createStructuredSelector} from 'reselect';
 
@@ -11,43 +10,16 @@ import {drawFocusOverlay, drawFocusCircles, drawFocusLine} from '../../../d3-ren
 import {link} from '../../../lib/d3-redux';
 import {mediaQuery, convertCelsiusToFahrenheit, convertFahrenheitToCelsius} from '../../../utils';
 
-import {getCurrentVariable, getCurrentParmCd} from '../../selectors/time-series-selector';
+import {getCurrentParmCd} from '../../selectors/time-series-selector';
 import {Actions} from '../../store/instantaneous-value-time-series-state';
 
-import {cursorTimeSelector, tsCursorPointsSelector} from './cursor';
+import {getCursorTime, getTsCursorPoints, getTooltipPoints} from './selectors/cursor';
 import {classesForPoint, MASK_DESC} from './drawing-data';
-import {getMainLayout} from './layout';
-import {getMainXScale, getMainYScale} from './scales';
-import {tsTimeZoneSelector, TEMPERATURE_PARAMETERS} from './time-series';
+import {getMainLayout} from './selectors/layout';
+import {getMainXScale, getMainYScale} from './selectors/scales';
+import {getTsTimeZone, getQualifiers, getCurrentVariableUnitCode, TEMPERATURE_PARAMETERS} from './selectors/time-series-data';
 
 
-/*
- * Returns a function that returns the time series data point nearest the
- * tooltip focus time for the given time series key. Only returns those points
- * where the y-value is finite; no use in making a point if y is Infinity.
- *
- * @param {Object} state - Redux store
- * @param String} tsKey - Time series key
- * @return {Object}
- */
-export const tooltipPointsSelector = memoize(tsKey => createSelector(
-    getMainXScale(tsKey),
-    getMainYScale,
-    tsCursorPointsSelector(tsKey),
-    (xScale, yScale, cursorPoints) => {
-        return Object.keys(cursorPoints).reduce((tooltipPoints, tsID) => {
-            const cursorPoint = cursorPoints[tsID];
-            if (isFinite(yScale(cursorPoint.value))) {
-                tooltipPoints.push({
-                    x: xScale(cursorPoint.dateTime),
-                    y: yScale(cursorPoint.value)
-                });
-            }
-            return tooltipPoints;
-        }, []);
-    }
-));
-
 const getTooltipText = function(datum, qualifiers, unitCode, ianaTimeZone, currentParmCd) {
     let label = '';
     if (datum && qualifiers) {
@@ -84,13 +56,6 @@ const getTooltipText = function(datum, qualifiers, unitCode, ianaTimeZone, curre
     return label;
 };
 
-const qualifiersSelector = state => state.ivTimeSeriesData.qualifiers;
-
-const unitCodeSelector = createSelector(
-    getCurrentVariable,
-    variable => variable ? variable.unit.unitCode : null
-);
-
 const createTooltipTextGroup = function (elem, {currentPoints, comparePoints, qualifiers, unitCode, ianaTimeZone, layout, currentParmCd}, textGroup) {
     // Find the width of the between the y-axis and margin and set the tooltip margin based on that number
     const adjustMarginOfTooltips = function (elem) {
@@ -187,12 +152,12 @@ const createTooltipTextGroup = function (elem, {currentPoints, comparePoints, qu
  */
 export const drawTooltipText = function (elem, store) {
     elem.call(link(store, createTooltipTextGroup, createStructuredSelector({
-        currentPoints: tsCursorPointsSelector('current'),
-        comparePoints: tsCursorPointsSelector('compare'),
-        qualifiers: qualifiersSelector,
-        unitCode: unitCodeSelector,
+        currentPoints: getTsCursorPoints('current'),
+        comparePoints: getTsCursorPoints('compare'),
+        qualifiers: getQualifiers,
+        unitCode: getCurrentVariableUnitCode,
         layout: getMainLayout,
-        ianaTimeZone: tsTimeZoneSelector,
+        ianaTimeZone: getTsTimeZone,
         currentParmCd: getCurrentParmCd
     })));
 };
@@ -207,12 +172,12 @@ export const drawTooltipFocus = function(elem, store) {
     elem.call(link(store, drawFocusLine, createStructuredSelector({
         xScale: getMainXScale('current'),
         yScale: getMainYScale,
-        cursorTime: cursorTimeSelector('current')
+        cursorTime: getCursorTime('current')
     })));
 
     elem.call(link(store, drawFocusCircles, createSelector(
-        tooltipPointsSelector('current'),
-        tooltipPointsSelector('compare'),
+        getTooltipPoints('current'),
+        getTooltipPoints('compare'),
         (current, compare) => {
             return current.concat(compare);
         }
@@ -247,4 +212,4 @@ export const drawTooltipCursorSlider = function(elem, store) {
             xScale: getMainXScale('current'),
             layout: getMainLayout
         }), store, Actions.setIVGraphCursorOffset));
-};
\ No newline at end of file
+};
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.spec.js b/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.spec.js
index 927ea1a2ef6d40882939ee6f11afecf4cc1da8be..7b32995aa28acf21b706e6f2f1d6aa886cfc8888 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.spec.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/tooltip.spec.js
@@ -171,45 +171,6 @@ describe('monitoring-location/components/hydrograph/tooltip module', () => {
         }
     };
 
-    describe('tooltipPointsSelector', () => {
-        const id = (val) => val;
-
-        it('should return the requested time series focus time', () => {
-            expect(tooltipPointsSelector('current').resultFunc(id, id, {
-                '00060:current': {
-                    dateTime: '1date',
-                    value: 1
-                },
-                '00060:compare': {
-                    dateTime: '2date',
-                    value: 2
-                }
-            })).toEqual([{
-                x: '1date',
-                y: 1
-            }, {
-                x: '2date',
-                y: 2
-            }]);
-        });
-
-        it('should exclude values that are infinite', () => {
-            expect(tooltipPointsSelector('current').resultFunc(id, id, {
-                '00060:current': {
-                    dateTime: '1date',
-                    value: Infinity
-                },
-                '00060:compare': {
-                    dateTime: '2date',
-                    value: 2
-                }
-            })).toEqual([{
-                x: '2date',
-                y: 2
-            }]);
-        });
-    });
-
     describe('drawTooltipText', () => {
         let div;
         beforeEach(() => {
diff --git a/assets/src/scripts/monitoring-location/selectors/flood-data-selector.js b/assets/src/scripts/monitoring-location/selectors/flood-data-selector.js
index 058add78228e6669eaba7d34aa9a21c70f457cdb..ed4cdc1c00234c8d7b887bb28184bb53d61ad705 100644
--- a/assets/src/scripts/monitoring-location/selectors/flood-data-selector.js
+++ b/assets/src/scripts/monitoring-location/selectors/flood-data-selector.js
@@ -29,7 +29,7 @@ export const hasWaterwatchData = createSelector(
 /*
  * Provides a function which returns True if waterwatch flood levels should be visible.
  */
-export const waterwatchVisible = createSelector(
+export const isWaterwatchVisible = createSelector(
     hasWaterwatchData,
     getCurrentParmCd,
     (hasFloodLevels, paramCd) =>
diff --git a/assets/src/scripts/monitoring-location/selectors/flood-data-selector.spec.js b/assets/src/scripts/monitoring-location/selectors/flood-data-selector.spec.js
index efd7367fdba623c5d66ec968f5f3c0c1adac7d08..c1e663c66b974bac108d9c88a3b7a8a16510656a 100644
--- a/assets/src/scripts/monitoring-location/selectors/flood-data-selector.spec.js
+++ b/assets/src/scripts/monitoring-location/selectors/flood-data-selector.spec.js
@@ -1,5 +1,5 @@
 import {getFloodStageHeight, hasFloodData, getFloodGageHeightStageIndex,
-    hasWaterwatchData, getWaterwatchFloodLevels, waterwatchVisible} from './flood-data-selector';
+    hasWaterwatchData, getWaterwatchFloodLevels, isWaterwatchVisible} from './flood-data-selector';
 
 describe('monitoring-location/selectors/flood-data-selector', () => {
 
@@ -119,9 +119,9 @@ describe('monitoring-location/selectors/flood-data-selector', () => {
         });
     });
 
-       describe('waterwatchVisible', () => {
+       describe('isWaterwatchVisible', () => {
         it('Return false if waterwatch flood levels should not be visible due to parameter code', () =>{
-            expect(waterwatchVisible({
+            expect(isWaterwatchVisible({
                 floodData: {
                     floodLevels: {
                         site_no: '07144100',
@@ -145,7 +145,7 @@ describe('monitoring-location/selectors/flood-data-selector', () => {
         });
 
         it('Return false if waterwatch flood levels should not be visible due to no flood levels', () =>{
-            expect(waterwatchVisible({
+            expect(isWaterwatchVisible({
                 floodData: {
                     floodLevels: null
                 },
@@ -163,7 +163,7 @@ describe('monitoring-location/selectors/flood-data-selector', () => {
         });
 
          it('Return true if waterwatch flood levels should be visible', () =>{
-            expect(waterwatchVisible({
+            expect(isWaterwatchVisible({
                  floodData: {
                      floodLevels: {
                          site_no: '07144100',
diff --git a/assets/src/scripts/utils.js b/assets/src/scripts/utils.js
index 0d5d3520ee03c6ec54655b0624af7e2a93d250d8..8bf615cac64fd91595868512e91c12f9c745471f 100644
--- a/assets/src/scripts/utils.js
+++ b/assets/src/scripts/utils.js
@@ -228,8 +228,8 @@ export const convertCelsiusToFahrenheit = function(celsius) {
 
 /*
  * Return the variables sorted with the ones we care about first
- * @param {Array of String}
- * @return {Array of String}
+ * @param {Array of variable Object}
+ * @return {Array of variable Object}
  */
 export const sortedParameters = function (variables) {
     const PARAM_PERTINENCE = {