diff --git a/assets/src/scripts/d3-rendering/axes.js b/assets/src/scripts/d3-rendering/axes.js index d564083a6b014da13b5ecf32c2eacfa9ebced603..d8d32629e182665816af1fb79dcdd3a2de737fdb 100644 --- a/assets/src/scripts/d3-rendering/axes.js +++ b/assets/src/scripts/d3-rendering/axes.js @@ -97,12 +97,14 @@ export const appendSecondaryYAxis = function(elem, {yAxis, layout, yTitle}) { * @prop {String} yTitle - label for the y-axis * @prop {String} secondaryYTitle - label for the secondary y-axis. */ -export const appendAxes = function(elem, {xAxis, yAxis, secondaryYAxis, layout, yTitle, secondaryYTitle}) { +export const appendAxes = function(elem, {xAxis, yAxis, secondaryYAxis, layout, yTitle, secondaryYTitle, secondaryParameter}) { elem.call(appendXAxis, {xAxis, layout}) - .call(appendYAxis, {yAxis, layout, yTitle}) - .call(appendSecondaryYAxis, { + .call(appendYAxis, {yAxis, layout, yTitle}); + if (secondaryParameter) { + elem.call(appendSecondaryYAxis, { yAxis: secondaryYAxis, layout: layout, yTitle: secondaryYTitle }); + } }; \ No newline at end of file diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js index 3811fb634df3fdde57256170b8cb4a3a9fcda12e..eafb5d2d66e139417676cdb07f4296274b6d6c0f 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/axes.js @@ -5,15 +5,14 @@ import {createSelector} from 'reselect'; import config from 'ui/config'; import {generateTimeTicks} from 'd3render/tick-marks'; +import {getIVParameter} from 'ml/selectors/hydrograph-data-selector'; - -import {getYTickDetails} from './domain'; +import {getSecondaryYTickDetails, getYTickDetails} from './domain'; import {getLayout} from './layout'; -import {getXScale, getBrushXScale, getYScale} from './scales'; +import {getXScale, getBrushXScale, getYScale, getSecondaryYScale} from './scales'; import {getPrimaryParameter} from './time-series-data'; - const createXAxis = function(xScale) { const [startMillis, endMillis] = xScale.domain(); const ticks = generateTimeTicks(startMillis, endMillis, config.locationTimeZone); @@ -33,7 +32,7 @@ const createXAxis = function(xScale) { * @param {Number} yTickSize Size of inner ticks for the y-axis * @return {Object} {xAxis, yAxis} - D3 Axis */ -const createAxes = function(xScale, yScale, yTickDetails, yTickSize) { +const createAxes = function(xScale, yScale, secondaryYScale, yTickDetails, secondaryYTickDetails, yTickSize) { // Create x-axis const xAxis = createXAxis(xScale); @@ -47,9 +46,9 @@ const createAxes = function(xScale, yScale, yTickDetails, yTickSize) { .tickSizeOuter(0); const secondaryYAxis = axisRight() - .scale(yScale) - .tickValues(yTickDetails.tickValues) - .tickFormat(yTickDetails.tickFormat) + .scale(secondaryYScale) + .tickValues(secondaryYTickDetails.tickValues) + .tickFormat(secondaryYTickDetails.tickFormat) .tickSizeInner(yTickSize) .tickPadding(3) .tickSizeOuter(0); @@ -71,19 +70,26 @@ export const getBrushXAxis = createSelector( export const getAxes = memoize(graphKind => createSelector( getXScale(graphKind, 'current'), getYScale(graphKind), + getSecondaryYScale(graphKind), getYTickDetails, + getSecondaryYTickDetails, getLayout(graphKind), getPrimaryParameter, - (xScale, yScale, yTickDetails, layout, parameter) => { + getIVParameter('secondary'), + (xScale, yScale, secondaryYScale, yTickDetails, secondaryYTickDetails, layout, parameter, secondaryParameter) => { return { ...createAxes( xScale, yScale, + secondaryYScale, yTickDetails, + secondaryYTickDetails, -layout.width + layout.margin.right ), layout: layout, - yTitle: parameter ? parameter.unit : '' + yTitle: parameter ? parameter.unit : '', + secondaryParameter: secondaryParameter ? secondaryParameter.parameterCode : '', + secondaryYTitle: secondaryParameter ? secondaryParameter.unit : '' }; } )); diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js index 06e88809fdbb0d1893e42d6d6ab78acf52424b5a..36aa60f4a8dbfea621b9eaa702ebc0f2d45ae8dc 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/domain.js @@ -5,9 +5,7 @@ import {createSelector} from 'reselect'; import config from 'ui/config'; import {mediaQuery} from 'ui/utils'; -import { - getPrimaryMedianStatisticsValueRange -} from 'ml/selectors/hydrograph-data-selector'; +import {getPrimaryMedianStatisticsValueRange, getIVParameter} from 'ml/selectors/hydrograph-data-selector'; import {getGroundwaterLevelDataRange} from './discrete-data'; import {getIVDataRange} from './iv-data'; @@ -155,7 +153,7 @@ export const generateNegativeTicks = function(tickValues, additionalTickValues) /** - * 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 + * Helper function that creates a new set of tick values to fill in gaps in log scale ticks, and 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,6 +209,24 @@ export const getPrimaryValueRange = createSelector( } ); + + +export const getSecondaryValueRange = createSelector( + getIVDataRange('secondary'), + getIVParameter('secondary'), + (secondaryParameterRange, secondaryParameter) => { + let result = [0, 1]; + if (secondaryParameterRange) { + result = [Math.min(...secondaryParameterRange), Math.max(...secondaryParameterRange)]; + + // Add padding to the extent and handle empty data sets. + result = extendDomain(secondaryParameterRange, useSymlog(secondaryParameter)); + } + return result; + } +); + + /* * Returns a Redux selector function that returns an Object with two properties: * @prop tickValues {Array of Number} @@ -253,3 +269,41 @@ export const getYTickDetails = createSelector( }; } ); + +export const getSecondaryYTickDetails = createSelector( + getSecondaryValueRange, + getIVParameter('secondary'), + (secondaryYDomain, parameter) => { + let tickValues = ticks(secondaryYDomain[0], secondaryYDomain[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 (useSymlog(parameter)) { + // add additional ticks and labels to log scales as needed + tickValues = getFullArrayOfAdditionalTickMarks(tickValues, secondaryYDomain); + // 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/selectors/layout.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.js index be30fd61d433024544c914962e68760387a740a0..170e9ae3af172a0c8afaf8351558b7f8061b6175 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/layout.js @@ -8,6 +8,7 @@ import config from 'ui/config'; import {mediaQuery} from 'ui/utils'; import {getYTickDetails} from './domain'; +import {getSelectedSecondaryParameterCode} from 'ml/selectors/hydrograph-state-selector'; export const ASPECT_RATIO = 1 / 2; export const ASPECT_RATIO_PERCENT = `${100 * ASPECT_RATIO}%`; @@ -41,7 +42,7 @@ export const SPARK_LINE_DIM = { * @prop {Number} - height - This is the height of the svg containing hydrograph * @prop {Number} - width - This is the width of the svg containing hydrgoraph * @prop {Object} - margin - Has top, bottom, left, right Number properties which define the margins - * with the enclosing svg for the hydrograph. Typically the area in the margins are used for axis + * with the enclosing svg for the hydrograph. Typically, the area in the margins are used for axis * labels and graph titles. * @param {String} kind - Either 'BRUSH' or 'MAIN'. If null 'MAIN' is assumed * @return {Selector function} @@ -50,7 +51,8 @@ export const getLayout = memoize(kind => createSelector( state => state.ui.width, state => state.ui.windowWidth, getYTickDetails, - (width, windowWidth, yTickDetails) => { + getSelectedSecondaryParameterCode, + (width, windowWidth, yTickDetails, secondaryParameterCode) => { 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; @@ -65,7 +67,7 @@ export const getLayout = memoize(kind => createSelector( bottom: margin.bottom, top: kind === 'BRUSH' ? 13 : margin.top, left: margin.left + approxLabelLength, - right: margin.right// + (hasRightYAxis ? approxLabelLength : 0) + right: margin.right + (secondaryParameterCode ? approxLabelLength : 0) } }; } diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js index c3415005356a22f491d2766d540941e751ea119b..4310e57fe45f867c7f6fb96612195e7caf0590fd 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/scales.js @@ -6,14 +6,13 @@ import config from 'ui/config.js'; import { getTimeRange } from 'ml/selectors/hydrograph-data-selector'; -import {getGraphBrushOffset} from 'ml/selectors/hydrograph-state-selector'; +import {getSelectedSecondaryParameterCode, getGraphBrushOffset} from 'ml/selectors/hydrograph-state-selector'; -import {SYMLOG_PARMS, getPrimaryValueRange} from './domain'; +import {SYMLOG_PARMS, getPrimaryValueRange, getSecondaryValueRange} from './domain'; import {getLayout} from './layout'; import {getPrimaryParameter} from './time-series-data'; - /* The two create* functions are helper functions. They are exported primarily * for ease of testing */ @@ -117,5 +116,22 @@ export const getYScale = memoize(graphKind => createSelector( } )); +/** + * Selector for secondary y-scale + * @param {Object} state Redux store + * @return {Function} D3 scale function + */ +export const getSecondaryYScale = memoize(graphKind => createSelector( + getLayout(graphKind), + getSecondaryValueRange, + getSelectedSecondaryParameterCode, + (layout, yDomain, secondaryParameterCode) => { + return createYScale(secondaryParameterCode ? secondaryParameterCode.parameterCode : '', yDomain, layout.height - (layout.margin.top + layout.margin.bottom)); + } +)); + + + + export const getMainYScale = getYScale(); export const getBrushYScale = getYScale('BRUSH'); diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js index 937dd6a1045fc1afb0208ee011a9606d7c760a9f..e980afa56df1a9158a492e0beaa06c8f83a44d73 100644 --- a/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js +++ b/assets/src/scripts/monitoring-location/components/hydrograph/selectors/time-series-data.js @@ -5,9 +5,7 @@ import {createSelector} from 'reselect'; import config from 'ui/config'; import {getSelectedGroundwaterLevels} from 'ml/selectors/groundwater-level-field-visits-selector'; -import {getIVData, getStatisticsData, getIVMethods, getIVParameter, - getTimeRange -} from 'ml/selectors/hydrograph-data-selector'; +import {getIVData, getStatisticsData, getIVMethods, getIVParameter, getTimeRange} from 'ml/selectors/hydrograph-data-selector'; import {getSelectedPrimaryIVMethodID, getSelectedSecondaryIVMethodID, getSelectedSecondaryParameterCode, isCompareIVDataVisible, isMedianDataVisible} from 'ml/selectors/hydrograph-state-selector'; const formatTime = function(timeInMillis) { 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 86e51e50e1ffdf0e6f3e1dcd7e8732a6c910b8cb..831ac768e5001fc47079002f88eea296453df45b 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 @@ -114,7 +114,6 @@ const drawTitle = function(elem, store, siteNo, agencyCode, sitename, showMLName titleDiv.append('div') .attr('class', 'primary-title') .call(link(store, (elem, {title, parameter}) => { - console.log('primary title div with parameter ', parameter); elem.html(title); if (showTooltip) { elem.call(appendInfoTooltip, parameter ? parameter.description : 'No description available', 'bottom');