Skip to content
Snippets Groups Projects
parameters.js 7.78 KiB
Newer Older
  • Learn to ignore specific revisions
  • Blake Draper's avatar
    Blake Draper committed
    import {line} from 'd3-shape';
    import {select} from 'd3-selection';
    
    import config from 'ui/config';
    
    
    import {appendTooltip} from 'd3render/tooltips';
    
    import {Actions} from 'ml/store/instantaneous-value-time-series-data';
    
    import {MASK_DESC} from './selectors/drawing-data';
    import {SPARK_LINE_DIM, CIRCLE_RADIUS_SINGLE_PT} from './selectors/layout';
    
    Yan, Andrew N.'s avatar
    Yan, Andrew N. committed
    /**
     * Draw a sparkline in a selected SVG element
     *
    
     * @param {Object} svgSelection
     * @param {Array} of line segment Objects - seriesLineSegments
     * @param {Object} scales - has x property for x scale and y property for y scale
    
    Yan, Andrew N.'s avatar
    Yan, Andrew N. committed
     */
    
    Blake Draper's avatar
    Blake Draper committed
    export const addSparkLine = function(svgSelection, {seriesLineSegments, scales}) {
    
        if (seriesLineSegments.length === 0) {
            return;
        }
    
    Blake Draper's avatar
    Blake Draper committed
            .x(function(d) {
    
                return scales.x(d.dateTime);
            })
    
    Blake Draper's avatar
    Blake Draper committed
            .y(function(d) {
    
                return scales.y(d.value);
            });
    
        const seriesDataMasks = seriesLineSegments.map(x => x.classes.dataMask);
        if (seriesDataMasks.includes(null)) {
            for (const lineSegment of seriesLineSegments) {
                if (lineSegment.classes.dataMask === null) {
    
                    if (lineSegment.points.length === 1) {
                        svgSelection.append('circle')
                            .data(lineSegment.points)
                            .classed('spark-point', true)
    
    Blake Draper's avatar
    Blake Draper committed
                            .attr('r', CIRCLE_RADIUS_SINGLE_PT/2)
    
                            .attr('cx', d => scales.x(d.dateTime))
                            .attr('cy', d => scales.y(d.value));
                    } else {
    
    Blake Draper's avatar
    Blake Draper committed
                        svgSelection.append('path')
    
                            .attr('d', spark(lineSegment.points))
                            .classed('spark-line', true);
                    }
    
    Briggs, Aaron Shane's avatar
    Briggs, Aaron Shane committed
            const centerElement = function(svgElement) {
    
                const elementWidth = svgElement.node().getBoundingClientRect().width;
                const xLocation = (SPARK_LINE_DIM.width - elementWidth) / 2;
                svgElement.attr('x', xLocation);
            };
    
            let svgText = svgSelection.append('text')
    
                .attr('x', 0)
                .attr('y', 0)
                .classed('sparkline-text', true);
    
            const maskDescs = seriesDataMasks.map(x => MASK_DESC[x.toLowerCase()]);
    
            const maskDesc = maskDescs.length === 1 ? maskDescs[0] : 'Masked';
    
            const maskDescWords = maskDesc.split(' ');
    
            if (maskDescWords.length > 1) {
    
                Array.from(maskDescWords.entries()).forEach(x => {
    
    Blake Draper's avatar
    Blake Draper committed
                    const yPosition = 15 + x[0]*12;
    
                    const maskText = x[1];
    
                    let tspan = svgText.append('tspan')
                        .attr('x', 0)
                        .attr('y', yPosition)
    
                    centerElement(svgText);
                    centerElement(tspan);
    
            } else {
                svgText.text(maskDesc)
                    .attr('y', '20');
    
                centerElement(svgText);
    
    /**
     * Draws a table with clickable rows of time series parameter codes. Selecting
     * a row changes the active parameter code.
     * @param  {Object} elem                        d3 selection
     * @param  {String} siteno
    
     * @param  {Object} availableParameterCodes        parameter metadata to display
    
     * @param  {Object} lineSegmentsByParmCd        line segments for each parameter code
     * @param  {Object} timeSeriesScalesByParmCd    scales for each parameter code
     */
    
    Briggs, Aaron Shane's avatar
    Briggs, Aaron Shane committed
    export const plotSeriesSelectTable = function(elem,
    
    Blake Draper's avatar
    Blake Draper committed
        {
    
            lineSegmentsByParmCd,
            timeSeriesScalesByParmCd
    
    Briggs, Aaron Shane's avatar
    Briggs, Aaron Shane committed
        }, store) {
    
        // Get the position of the scrolled window before removing it so it can be set to the same value.
    
        const lastTable = elem.select('#select-time-series table');
    
        const scrollTop = lastTable.size() ? lastTable.property('scrollTop') : null;
    
        elem.select('#select-time-series').remove();
    
        const columnHeaders = ['   ', 'Parameter', 'Preview', '#', 'Period of Record', 'WaterAlert'];
    
        const tableContainer = elem.append('div')
    
            .attr('id', 'select-time-series');
    
            .attr('id', 'select-time-series-label')
    
            .text('Select a time series');
    
        const table = tableContainer.append('table')
    
            .classed('usa-table', true)
            .classed('usa-table--borderless', true)
    
            .attr('aria-labelledby', 'select-time-series-label')
    
            .attr('role', 'listbox');
    
    Blake Draper's avatar
    Blake Draper committed
                .selectAll('th')
                .data(columnHeaders)
                .enter().append('th')
    
    Blake Draper's avatar
    Blake Draper committed
                    .attr('scope', 'col')
                    .text(d => d);
    
        table.append('tbody')
    
    Blake Draper's avatar
    Blake Draper committed
            .attr('class', 'usa-fieldset')
    
            .attr('id', param => `time-series-select-table-row-${param.parameterCode}`)
    
    Blake Draper's avatar
    Blake Draper committed
            .attr('ga-on', 'click')
            .attr('ga-event-category', 'selectTimeSeries')
    
    Blake Draper's avatar
    Blake Draper committed
            .attr('ga-event-action', (param) => `time-series-parmcd-${param.parameterCode}`)
    
    Blake Draper's avatar
    Blake Draper committed
            .attr('role', 'option')
    
    Blake Draper's avatar
    Blake Draper committed
            .classed('selected', param => param.selected)
            .attr('aria-selected', param => param.selected)
    
    Blake Draper's avatar
    Blake Draper committed
                if (!param.selected) {
                    store.dispatch(Actions.updateIVCurrentVariableAndRetrieveTimeSeries(siteno, param.variableID));
    
    Blake Draper's avatar
    Blake Draper committed
                }
            })
            .call(tr => {
    
                let paramSelectCol = tr.append('td');
    
    Blake Draper's avatar
    Blake Draper committed
                paramSelectCol.append('input')
    
                    .attr('id', param => `time-series-select-radio-button-${param.parameterCode}`)
    
    Blake Draper's avatar
    Blake Draper committed
                    .attr('type', 'radio')
    
                    .attr('name', 'param-select-radio-input')
                    .attr('class', 'usa-radio__input')
    
    Blake Draper's avatar
    Blake Draper committed
                    .attr('value', param => `${param.variableID}`)
    
                    .property('checked', param => param.selected ? true : null);
                paramSelectCol.append('label')
                   .attr('class', 'usa-radio__label');
    
    Blake Draper's avatar
    Blake Draper committed
                let paramCdCol = tr.append('th')
    
    Blake Draper's avatar
    Blake Draper committed
                    .attr('scope', 'row');
    
    Blake Draper's avatar
    Blake Draper committed
                paramCdCol.append('span')
                    .text(param => param.description)
                    .call(appendTooltip, param => `Parameter code: ${param.parameterCode}`);
    
    Blake Draper's avatar
    Blake Draper committed
                tr.append('td')
                    .append('svg')
                    .attr('width', SPARK_LINE_DIM.width.toString())
                    .attr('height', SPARK_LINE_DIM.height.toString());
                tr.append('td')
    
    Blake Draper's avatar
    Blake Draper committed
                    .text(param => param.timeSeriesCount);
    
    Blake Draper's avatar
    Blake Draper committed
                tr.append('td')
                    .style('white-space', 'nowrap')
    
    Blake Draper's avatar
    Blake Draper committed
                    .text(param => `${config.uvPeriodOfRecord[param.parameterCode].begin_date} to ${config.uvPeriodOfRecord[param.parameterCode].end_date}`);
    
                tr.append('td')
                    .append('a')
                        .attr('href', param => `${config.WATERALERT_SUBSCRIPTION}/?site_no=${siteno}&parm=${param.parameterCode}`)
    
    Briggs, Aaron Shane's avatar
    Briggs, Aaron Shane committed
                        .attr('class', 'usa-tooltip')
                        .attr('data-position', 'left')
                        .attr('data-classes', 'width-full tablet:width-auto')
                        .attr('title', 'Subscribe to text or email alerts based on thresholds that you set')
    
                        .text('Subscribe');
    
    Blake Draper's avatar
    Blake Draper committed
            });
    
    
    Briggs, Aaron Shane's avatar
    Briggs, Aaron Shane committed
        table.selectAll('tbody svg').each(function(d) {
    
    Yan, Andrew N.'s avatar
    Yan, Andrew N. committed
            let selection = select(this);
    
    Blake Draper's avatar
    Blake Draper committed
            const paramCd = d.parameterCode;
            const lineSegments = lineSegmentsByParmCd[paramCd] ? lineSegmentsByParmCd[paramCd] : [];
    
            for (const seriesLineSegments of lineSegments) {
    
                selection.call(addSparkLine, {
                    seriesLineSegments: seriesLineSegments,
    
    Blake Draper's avatar
    Blake Draper committed
                    scales: timeSeriesScalesByParmCd[paramCd]
    
    Yan, Andrew N.'s avatar
    Yan, Andrew N. committed
        });