Skip to content
Snippets Groups Projects
legend.js 4.83 KiB
Newer Older
// functions to facilitate legend creation for a d3 plot
const { createSelector } = require('reselect');
const { defineLineMarker, defineCircleMarker } = require('./markers');
const { CIRCLE_RADIUS } = require('./layout');

Yan, Andrew N.'s avatar
Yan, Andrew N. committed
/**
 * Create a simple horizontal legend
 *
 * @param svg
 * @param legendMarkers
Yan, Andrew N.'s avatar
Yan, Andrew N. committed
 * @param startingXPosition
 * @param markerYPosition
 * @param textYPosition
 * @param markerGroupOffset
 * @param markerTextOffset
 */
function drawSimpleLegend(svg,
                          legendMarkers,
Yan, Andrew N.'s avatar
Yan, Andrew N. committed
                          startingXPosition=0,
                          markerYPosition=-4,
                          textYPosition=0,
                          markerGroupOffset=40,
                          markerTextOffset=10) {
    let legend = svg
        .append('g')
        .attr('class', 'legend');

    let svgBBox = svg.node().getBBox();

    let previousMarkerGroup;

    for (let legendMarker of legendMarkers) {
        let xPosition;
        let previousMarkerGroupBox;
        let detachedMarker;
        if (previousMarkerGroup == null) {
            xPosition = startingXPosition;
        }
        else {
            previousMarkerGroupBox = previousMarkerGroup.node().getBBox();
            xPosition = previousMarkerGroupBox.x + previousMarkerGroupBox.width + markerGroupOffset;
        }
        let markerType = legendMarker.type;
        let legendGroup = legend.append('g')
            .attr('class', 'legend-marker');
        if (legendMarker.groupId) {
            legendGroup.attr('id', legendMarker.groupId);
        }
Yan, Andrew N.'s avatar
Yan, Andrew N. committed
        let markerArgs = {
            r: legendMarker.r ? legendMarker.r : null,
            x: xPosition,
            y: markerYPosition,
            length: 20,
            domId: legendMarker.domId,
            domClass: legendMarker.domClass
        };
        // add the marker to the svg
        detachedMarker = markerType(markerArgs);
        legendGroup.node().appendChild(detachedMarker.node());
        // add text for the legend marker
        let detachedMarkerBBox = detachedMarker.node().getBBox();
        legendGroup.append('text')
            .attr('x', detachedMarkerBBox.x + detachedMarkerBBox.width + markerTextOffset)
            .attr('y', textYPosition)
            .text(legendMarker.text);

Yan, Andrew N.'s avatar
Yan, Andrew N. committed
        previousMarkerGroup = legendGroup;
    }
    // center the legend group in the svg
    let legendBBox = legend.node().getBBox();
Yan, Andrew N.'s avatar
Yan, Andrew N. committed
    const svgWidth = width ? width : svgBBox.width;
    const legendXPosition = (svgWidth - legendBBox.width) / 2;
Yan, Andrew N.'s avatar
Yan, Andrew N. committed
    legend.attr('transform', `translate(${legendXPosition}, ${svgBBox.height-15})`);
}

/**
 * create elements for the legend in the svg
 *
 * @param dataPlotElements
 */
const createLegendMarkers = function(dataPlotElements) {
    let text;
    let marker;
    let legendMarkers = [];
    for (let dataItem of dataPlotElements.dataItems) {
        if (dataItem === 'compare' || dataItem === 'current') {
            text = 'Current Year';
            let domId = `ts-${dataItem}`;
            let svgGroup = `${dataItem}-line-marker`;
            if (dataItem === 'compare') {
                text = 'Last Year';
            }
            marker = defineLineMarker(domId, 'line', text, svgGroup);
        }
        else if (dataItem === 'medianStatistics') {
            text = 'Median Discharge';
            let beginYear = dataPlotElements.metadata.statistics.beginYear;
            let endYear = dataPlotElements.metadata.statistics.endYear;
            if (beginYear && endYear) {
                text = `${text} ${beginYear} - ${endYear}`;
            }
            marker = defineCircleMarker(CIRCLE_RADIUS, null, 'median-data-series', text, 'median-circle-marker');
        }
        else {
            marker = null;
        }
        if (marker) {
            legendMarkers.push(marker);
        }
    }
    return legendMarkers;
};
Yan, Andrew N.'s avatar
Yan, Andrew N. committed

/**
 * Select attributes from the state useful for legend creation
 */
const legendDisplaySelector = createSelector(
    (state) => state.showSeries,
    (state) => state.statisticalMetaData,
    (showSeries, statisticalMetaData) => {
        let shownSeries = [];
        let dataPlotElements = {};
        let text;
        let marker;
        for (let key in showSeries) {
            if (showSeries.hasOwnProperty(key)) {
Yan, Andrew N.'s avatar
Yan, Andrew N. committed
                if (showSeries[key]) {
                    shownSeries.push(key);

        dataPlotElements.dataItems = shownSeries;
        dataPlotElements.metadata = {
            statistics: {
                beginYear: statisticalMetaData.beginYear ? statisticalMetaData.beginYear : undefined,
                endYear: statisticalMetaData.endYear ? statisticalMetaData.endYear : undefined
        };
        return dataPlotElements;
module.exports = {drawSimpleLegend, createLegendMarkers, legendDisplaySelector};