Skip to content
Snippets Groups Projects
Commit 3649d108 authored by Fry, Janell's avatar Fry, Janell
Browse files

IOW-307 Work-In-Progress DV legend

parent 492a3391
No related branches found
No related tags found
No related merge requests found
......@@ -9,6 +9,7 @@ import {drawErrorAlert, drawInfoAlert} from '../../d3-rendering/alerts';
import {drawLoadingIndicator} from '../../d3-rendering/loading-indicator';
import {drawTimeSeriesGraph} from './time-series-graph';
import {drawTimeSeriesLegend} from "./legend";
const TEMP_TIME_SERIES_ID = '36307c899ac14d2eac6956b1bf5ceb69';
......@@ -52,4 +53,10 @@ export const attachToNode = function (store,
}, hasCurrentObservationsTimeSeries))
.call(drawTimeSeriesGraph, store);
// Add DV legend
let graphContainer = nodeElem.select('.graph-container')
graphContainer.append('div')
.classed('dv-legend-controls-container', true)
.call(drawTimeSeriesLegend, store);
};
\ No newline at end of file
// 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 {getLayout} from './selectors/layout';
import { defineLineMarker, defineTextOnlyMarker, defineRectangleMarker } from '../hydrograph/markers';
import { currentVariableLineSegmentsSelector, HASH_ID, MASK_DESC } from '../hydrograph/drawing-data';
import config from '../../config';
import { mediaQuery } from '../../utils';
import {link} from '../../lib/d3-redux';
const TS_LABEL = {
'current': 'Current: '
};
const tsLineMarkers = function(tsKey, lineClasses) {
let result = [];
console.log('tsKey:'+tsKey);
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(TS_LABEL.current, null, 'ts-legend-current-text'),
...currentMarkers
]);
}
}
return legendMarkers;
};
/**
* Create a simple legend
*
* @param {Object} div - d3 selector where legend should be created
* @param {Object} legendMarkerRows - Array of rows. Each row should be an array of legend markers.
* @param {Object} layout - width and height of svg.
*/
export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) {
div.selectAll('.legend-svg').remove();
if (!legendMarkerRows.length || !layout) {
return;
}
const markerGroupXOffset = 15;
const verticalRowOffset = 18;
let svg = div.append('svg')
.attr('class', 'legend-svg');
let legend = svg
.append('g')
.attr('class', 'legend')
.attr('transform', `translate(${mediaQuery(config.USWDS_MEDIUM_SCREEN) ? layout.margin.left : 0}, 0)`);
legendMarkerRows.forEach((rowMarkers, rowIndex) => {
let xPosition = 0;
let yPosition = verticalRowOffset * (rowIndex + 1);
rowMarkers.forEach((marker) => {
let markerArgs = {
x: xPosition,
y: yPosition,
text: marker.text,
domId: marker.domId,
domClass: marker.domClass,
width: 20,
height: 10,
length: 20,
r: marker.r,
fill: marker.fill
};
console.log('DV: marker text:'+marker.text+ ' xPosition:'+xPosition+ ' yPosition:'+yPosition );
let markerGroup = marker.type(legend, markerArgs);
let markerGroupBBox;
// Long story short, firefox is unable to get the bounding box if
// the svg element isn't actually taking up space and visible. Folks on the
// internet seem to have gotten around this by setting `visibility:hidden`
// to hide things, but that would still mean the elements will take up space.
// which we don't want. So, here's some error handling for getBBox failures.
// This handling ends up not creating the legend, but that's okay because the
// graph is being shown anyway. A more detailed discussion of this can be found at:
// https://bugzilla.mozilla.org/show_bug.cgi?id=612118 and
// https://stackoverflow.com/questions/28282295/getbbox-of-svg-when-hidden.
try {
markerGroupBBox = markerGroup.node().getBBox();
xPosition = markerGroupBBox.x + markerGroupBBox.width + markerGroupXOffset;
console.log('DV: markerGroupBBox.x:'+markerGroupBBox.x+ ' markerGroupBBox.width:'+markerGroupBBox.width+ ' markerGroupXOffset:'+markerGroupXOffset);
console.log('DV: xPosition:'+xPosition);
} catch(error) {
// See above explanation
}
});
});
// Set the size of the containing svg node to the size of the legend.
let bBox;
try {
bBox = legend.node().getBBox();
} catch(error) {
return;
}
svg.attr('viewBox', `-4 0 ${layout.width} ${bBox.height + 20}`);
};
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),
approved: classes.some((cls) => cls.approved),
estimated: classes.some((cls) => cls.estimated),
};
}
));
/**
* Select attributes from the state useful for legend creation
*/
const legendDisplaySelector = createSelector(
(state) => state.timeSeriesState.showSeries,
uniqueClassesSelector('current'),
uniqueClassesSelector('compare'),
(showSeries, medianSeries, currentClasses, compareClasses) => {
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,
layout: getLayout
})));
};
......@@ -48,7 +48,7 @@ const tsLineMarkers = function(tsKey, lineClasses) {
* @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 respresnts a ts and contains an array of markers to show.
* @return {Object} - Each key represents a ts and contains an array of markers to show.
*/
const createLegendMarkers = function(displayItems) {
const legendMarkers = [];
......@@ -143,9 +143,12 @@ export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) {
width: 20,
height: 10,
length: 20,
r: marker.r ,
r: marker.r,
fill: marker.fill
};
//console.log('UV: marker text:'+marker.text+ ' xPosition:'+xPosition+ ' yPosition:'+yPosition );
let markerGroup = marker.type(legend, markerArgs);
let markerGroupBBox;
// Long story short, firefox is unable to get the bounding box if
......@@ -161,6 +164,9 @@ export const drawSimpleLegend = function(div, {legendMarkerRows, layout}) {
markerGroupBBox = markerGroup.node().getBBox();
xPosition = markerGroupBBox.x + markerGroupBBox.width + markerGroupXOffset;
//console.log('UV: markerGroupBBox.x:'+markerGroupBBox.x+ ' markerGroupBBox.width:'+markerGroupBBox.width+ ' markerGroupXOffset:'+markerGroupXOffset);
//console.log('UV: xPosition:'+xPosition);
} catch(error) {
// See above explanation
}
......
......@@ -212,6 +212,17 @@
}
}
.dv-legend-controls-container {
display: inline-block;
height: 85px;
font-size: .7em;
@include at-media('tablet') {
font-size: 1em;
}
}
.provisional-data-alert {
p {
height: 6em;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment