diff --git a/assets/src/scripts/components/dailyValueHydrograph/legend.js b/assets/src/scripts/components/dailyValueHydrograph/legend.js index 5b9e591da7952c000404aa7add6da21ea7383f8f..a094cf05b7bb9199a516a7f7f5e1bf8fa7d136ef 100644 --- a/assets/src/scripts/components/dailyValueHydrograph/legend.js +++ b/assets/src/scripts/components/dailyValueHydrograph/legend.js @@ -1,55 +1,14 @@ // functions to facilitate DV legend creation for a d3 plot -import memoize from 'fast-memoize'; -import {createSelector, createStructuredSelector} from 'reselect'; +import {createStructuredSelector} from 'reselect'; -import {defineLineMarker, defineTextOnlyMarker} from '../../d3-rendering/markers'; +import {defineLineMarker} from '../../d3-rendering/markers'; import {getLayout} from './selectors/layout'; -import {getCurrentTimeSeriesLineSegments} from './selectors/time-series-lines'; +import {getLegendMarkerRows} from './selectors/legend-data'; import config from '../../config'; import { mediaQuery } from '../../utils'; import {link} from '../../lib/d3-redux'; -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) - ]; - if (currentMarkers.length) { - legendMarkers.push([ - defineTextOnlyMarker('', null, 'ts-legend-current-text'), - ...currentMarkers - ]); - } - } - - return legendMarkers; -}; /** * Create a simple legend @@ -125,55 +84,11 @@ export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) { }; -const uniqueClassesSelector = memoize(tsKey => createSelector( - getCurrentTimeSeriesLineSegments, - (tsLineSegments) => { - let result = { - default: false, - approved: false, - estimated: false - }; - tsLineSegments.forEach((segment) => { - result.approved = result.approved || segment.approvals.includes('Approved'); - result.estimated = result.estimated || segment.approvals.includes('Estimated'); - result.default = result.default || segment.approvals.length === 0; - }); - return result; - } -)); - - -/** - * Select attributes from the state useful for legend creation - */ -const legendDisplaySelector = createSelector( - (state) => state.timeSeriesState.showSeries, - uniqueClassesSelector('current'), - uniqueClassesSelector('compare'), - (showSeries, medianSeries, currentClasses) => { - return { - current: showSeries.current ? currentClasses : 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) -); - - export const drawTimeSeriesLegend = function(elem, store) { elem.append('div') .classed('hydrograph-container', true) .call(link(store, drawSimpleLegend, createStructuredSelector({ - legendMarkerRows: legendMarkerRowsSelector, + legendMarkerRows: getLegendMarkerRows, layout: getLayout }))); }; diff --git a/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.js b/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.js new file mode 100644 index 0000000000000000000000000000000000000000..c132a76d09a92970133352e31fbc08b225656938 --- /dev/null +++ b/assets/src/scripts/components/dailyValueHydrograph/selectors/legend-data.js @@ -0,0 +1,74 @@ +import {createSelector} from 'reselect'; +import {getCurrentTimeSeriesLineSegments} from './time-series-data'; +import {defineLineMarker, defineTextOnlyMarker} from '../../../d3-rendering/markers'; + + +const tsLineMarkers = function(lineClasses) { + let result = []; + + if (lineClasses.default) { + result.push(defineLineMarker(null, `line-segment ts-dv`, 'Provisional')); + } + if (lineClasses.approved) { + result.push(defineLineMarker(null, `line-segment approved ts-dv`, 'Approved')); + } + if (lineClasses.estimated) { + result.push(defineLineMarker(null, `line-segment estimated ts-dv`, 'Estimated')); + } + return result; +}; + + +/** + * create elements for the legend in the svg + * + * @param {Object} displayItems - Object containing Object containing default, estimated and approved properties. + * @return {Array} - Returns an array of markers. + */ +const createLegendMarkers = function(displayItems) { + const legendMarkers = []; + + if (displayItems) { + const currentMarkers = [ + ...tsLineMarkers(displayItems) + ]; + if (currentMarkers.length) { + legendMarkers.push([ + defineTextOnlyMarker('', null, 'ts-legend-current-text'), + ...currentMarkers + ]); + } + } + return legendMarkers; +}; + + +export const getUniqueClasses = createSelector( + getCurrentTimeSeriesLineSegments, + (tsLineSegments) => { + let result = { + default: false, + approved: false, + estimated: false + }; + tsLineSegments.forEach((segment) => { + result.approved = result.approved || segment.approvals.includes('Approved'); + result.estimated = result.estimated || segment.approvals.includes('Estimated'); + result.default = result.default || segment.approvals.length === 0; + }); + return result; + } +); + + +/* + * 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 getLegendMarkerRows = createSelector( + getUniqueClasses, + displayItems => { + return createLegendMarkers(displayItems) + } +); \ No newline at end of file diff --git a/assets/src/scripts/components/dailyValueHydrograph/selectors/time-series-lines.js b/assets/src/scripts/components/dailyValueHydrograph/selectors/time-series-lines.js deleted file mode 100644 index 3cff0b714821947706962b4c0f88b2f988863e55..0000000000000000000000000000000000000000 --- a/assets/src/scripts/components/dailyValueHydrograph/selectors/time-series-lines.js +++ /dev/null @@ -1,63 +0,0 @@ -import {DateTime} from 'luxon'; -import isEqual from 'lodash/isEqual'; -import {createSelector} from 'reselect'; - -import {getCurrentObservationsTimeSeries} from '../../../selectors/observations-selector'; - -const TWO_DAYS = 1000 * 60 * 60 * 24 * 2; // In milliseconds - -/* - * Returns selector function which returns an array of Objects which include the data needed to render the line. - * The time series data is broken into line segments. The points in a line segment will be shown as continuous and - * the data has the same approvals. - * Each Object contains the following properties - * @prop {Array of Object} points - each object has date (in milliseconds) and value {Number} properties - * @prop {Array of String} approvals - The approvals for this line segment - * The time series - */ -export const getCurrentTimeSeriesLineSegments = createSelector( - getCurrentObservationsTimeSeries, - (timeSeries) => { - if (!timeSeries) { - return []; - } - - const timeStepInMillis = timeSeries.properties.timeStep.map((t) => new DateTime.fromISO(t, {zone: 'UTC'}).toMillis()); - - let lineSegments = []; - let previousDate = timeStepInMillis[0]; - let previousApprovals = timeSeries.properties.approvals[0]; - let segment = { - points: [], - approvals: timeSeries.properties.approvals[0] - }; - timeStepInMillis.forEach(function(date, index) { - const resultValue = parseFloat(timeSeries.properties.result[index]); - const hasGap = date - previousDate >= TWO_DAYS; - const hasDifferentApprovals = !isEqual(timeSeries.properties.approvals[index], previousApprovals); - if (hasDifferentApprovals && !hasGap) { - // Add the current point to the last segment so that line is continuous - segment.points.push({ - value: resultValue, - date: date - }); - } - - if (hasGap || hasDifferentApprovals) { - lineSegments.push(segment); - segment = { - points: [], - approvals: timeSeries.properties.approvals[index] - }; - previousApprovals = timeSeries.properties.approvals[index]; - } - segment.points.push({ - value: parseFloat(resultValue), - date: date - }); - previousDate = date; - }); - lineSegments.push(segment); - return lineSegments; - } -); \ No newline at end of file