diff --git a/src/client/app/containers/RadarChartContainer.ts b/src/client/app/containers/RadarChartContainer.ts index 7109739ef..76a4eb413 100644 --- a/src/client/app/containers/RadarChartContainer.ts +++ b/src/client/app/containers/RadarChartContainer.ts @@ -11,56 +11,45 @@ import translate from '../utils/translate'; import Plot from 'react-plotly.js'; import Locales from '../types/locales'; import { DataType } from '../types/Datasources'; -//Make your own radarUnitLabel later. Lets see if this works first. -import { lineUnitLabel } from '../utils/graphics'; +// Make your own radarUnitLabel later. Lets see if this works first +import { barUnitLabel } from '../utils/graphics'; import { AreaUnitType, getAreaUnitConversion } from '../utils/getAreaUnitConversion'; function mapStateToProps(state: State) { const timeInterval = state.graph.timeInterval; const unitID = state.graph.selectedUnit; const datasets: any[] = []; + const barDuration = state.graph.barDuration; + //For largest and smallest usage in reading.reading. + let minR: number | undefined; + let maxR: number | undefined; // The unit label depends on the unit which is in selectUnit state. const graphingUnit = state.graph.selectedUnit; - // The current selected rate - /* Might want to go back and change this currentSelectedRate */ - const currentSelectedRate = state.graph.lineGraphRate; let unitLabel = ''; - let needsRateScaling = false; - // variables to determine the slider min and max - let minTimestamp: number | undefined; - let maxTimestamp: number | undefined; // If graphingUnit is -99 then none selected and nothing to graph so label is empty. // This will probably happen when the page is first loaded. if (graphingUnit !== -99) { const selectUnitState = state.units.units[state.graph.selectedUnit]; if (selectUnitState !== undefined) { - // Determine the y-axis label and if the rate needs to be scaled. - /** Might want to go back and change this lineUnitLabel function */ - const returned = lineUnitLabel(selectUnitState, currentSelectedRate, state.graph.areaNormalization, state.graph.selectedAreaUnit); - unitLabel = returned.unitLabel - needsRateScaling = returned.needsRateScaling; + // Determine the r-axis label. + unitLabel = barUnitLabel(selectUnitState, state.graph.areaNormalization, state.graph.selectedAreaUnit); } } - // The rate will be 1 if it is per hour (since state readings are per hour) or no rate scaling so no change. - const rateScaling = needsRateScaling ? currentSelectedRate.rate : 1; // Add all valid data from existing meters to the radar plot for (const meterID of state.graph.selectedMeters) { - const byMeterID = state.readings.radar.byMeterID[meterID]; - // Make sure have the meter data. If you already have the meter, unselect, change - // the timeInterval via another meter and then reselect then this new timeInterval - // may not yet be in state so verify with the second condition on the if. - // Note the second part may not be used based on next checks but do here since simple. - if (byMeterID !== undefined && byMeterID[timeInterval.toString()] !== undefined) { - const meterArea = state.meters.byMeterID[meterID].area; + const byMeterID = state.readings.bar.byMeterID[meterID]; + if (byMeterID !== undefined && byMeterID[timeInterval.toString()] !== undefined && + byMeterID[timeInterval.toString()][barDuration.toISOString()] !== undefined) { + let meterArea = state.meters.byMeterID[meterID].area; // We either don't care about area, or we do in which case there needs to be a nonzero area. if (!state.graph.areaNormalization || (meterArea > 0 && state.meters.byMeterID[meterID].areaUnit != AreaUnitType.none)) { // Convert the meter area into the proper unit if normalizing by area or use 1 if not so won't change reading values. - const areaScaling = state.graph.areaNormalization ? - meterArea * getAreaUnitConversion(state.meters.byMeterID[meterID].areaUnit, state.graph.selectedAreaUnit) : 1; - // Divide areaScaling into the rate so have complete scaling factor for readings. - const scaling = rateScaling / areaScaling; - const readingsData = byMeterID[timeInterval.toString()][unitID]; + if (state.graph.areaNormalization) { + // convert the meter area into the proper unit, if needed + meterArea *= getAreaUnitConversion(state.meters.byMeterID[meterID].areaUnit, state.graph.selectedAreaUnit); + } + const readingsData = byMeterID[timeInterval.toString()][barDuration.toISOString()][unitID]; if (readingsData !== undefined && !readingsData.isFetching) { const label = state.meters.byMeterID[meterID].identifier; const colorID = meterID; @@ -80,24 +69,29 @@ function mapStateToProps(state: State) { const st = moment.utc(reading.startTimestamp); // Time reading is in the middle of the start and end timestamp const timeReading = st.add(moment.utc(reading.endTimestamp).diff(st) / 2); - thetaData.push(timeReading.format('YYYY-MM-DD HH:mm:ss')); - const readingValue = reading.reading * scaling; + // thetaData.push(timeReading.format('YYYY-MM-DD HH:mm:ss')); + thetaData.push(timeReading.format('ddd, ll LTS')); + let readingValue = reading.reading; + if (state.graph.areaNormalization) { + readingValue /= meterArea; + } rData.push(readingValue); - hoverText.push(` ${timeReading.format('ddd, ll LTS')}
${label}: ${readingValue.toPrecision(6)} ${unitLabel}`); + // only display a range of dates for the hover text if there is more than one day in the range + let timeRange: string = `${moment.utc(reading.startTimestamp).format('ll')}`; + if (barDuration.asDays() != 1) { + // subtracting one extra day caused by day ending at midnight of the next day. + // Going from DB unit timestamp that is UTC so force UTC with moment, as usual. + timeRange += ` - ${moment.utc(reading.endTimestamp).subtract(1, 'days').format('ll')}`; + } + hoverText.push(` ${timeRange}
${label}: ${readingValue.toPrecision(6)} ${unitLabel}`); }); - /* - get the min and max timestamp of the meter, and compare it to the global values - TODO: If we know the interval and frequency of meter data, these calculations should be able to be simplified - */ - if (readings.length > 0) { - if (minTimestamp == undefined || readings[0]['startTimestamp'] < minTimestamp) { - minTimestamp = readings[0]['startTimestamp']; - } - if (maxTimestamp == undefined || readings[readings.length - 1]['endTimestamp'] >= maxTimestamp) { - // Need to add one extra reading interval to avoid range truncation. The max bound seems to be treated as non-inclusive - maxTimestamp = readings[readings.length - 1]['endTimestamp'] + (readings[0]['endTimestamp'] - readings[0]['startTimestamp']); - } + //Find the largest and smallest usage in rData. + if (minR == undefined || minR > Math.min(...rData)) { + minR = Math.min(...rData); + } + if (maxR == undefined || maxR < Math.max(...rData)) { + maxR = Math.max(...rData); } // This variable contains all the elements (x and y values, line type, etc.) assigned to the data parameter of the Plotly object @@ -120,25 +114,96 @@ function mapStateToProps(state: State) { } } - /** - * - *ADD GROUPS FOR RADAR!!!!!!!!!! - */ + //THIS DOES NOT WORK YET AND IT'S STILL USING RADAR READINGS I MOVED TO BAR READINGS + // for (const groupID of state.graph.selectedGroups) { + // const byGroupID = state.readings.line.byGroupID[groupID]; + // if (byGroupID !== undefined && byGroupID[timeInterval.toString()] !== undefined) { + // let groupArea = state.groups.byGroupID[groupID].area; + // if (!state.graph.areaNormalization || (groupArea > 0 && state.groups.byGroupID[groupID].areaUnit != AreaUnitType.none)) { + // if (state.graph.areaNormalization) { + // // convert the meter area into the proper unit, if needed + // groupArea *= getAreaUnitConversion(state.groups.byGroupID[groupID].areaUnit, state.graph.selectedAreaUnit); + // } + // const readingsData = byGroupID[timeInterval.toString()][unitID]; + // if (readingsData !== undefined && !readingsData.isFetching) { + // const label = state.groups.byGroupID[groupID].name; + // const colorID = groupID; + // if (readingsData.readings === undefined) { + // throw new Error('Unacceptable condition: readingsData.readings is undefined.'); + // } - // set the bounds for the slider - if (minTimestamp == undefined) { - minTimestamp = 0; - maxTimestamp = 0; - } - const root: any = document.getElementById('root'); - root.setAttribute('min-timestamp', minTimestamp); - root.setAttribute('max-timestamp', maxTimestamp); + // // Create two arrays for the x and y values. Fill the array with the data from the line readings + // const thetaData: string[] = []; + // const rData: number[] = []; + // const hoverText: string[] = []; + // const readings = _.values(readingsData.readings); + // // Check if reading needs scaling outside of the loop so only one check is needed + // // Results in more code but SLIGHTLY better efficiency :D + // if (needsRateScaling) { + // const rate = currentSelectedRate.rate; + // readings.forEach(reading => { + // // As usual, we want to interpret the readings in UTC. We lose the timezone as this as the start/endTimestamp + // // are equivalent to Unix timestamp in milliseconds. + // const st = moment.utc(reading.startTimestamp); + // // Time reading is in the middle of the start and end timestamp + // const timeReading = st.add(moment.utc(reading.endTimestamp).diff(st) / 2); + // // thetaData.push(timeReading.utc().format('YYYY-MM-DD HH:mm:ss')); + // thetaData.push(timeReading.utc().format('ddd, ll LTS')); + // rData.push(reading.reading * rate); + // hoverText.push(` ${timeReading.format('ddd, ll LTS')}
${label}: ${(reading.reading * rate).toPrecision(6)} ${unitLabel}`); + // }); + // } + // else { + // readings.forEach(reading => { + // // As usual, we want to interpret the readings in UTC. We lose the timezone as this as the start/endTimestamp + // // are equivalent to Unix timestamp in milliseconds. + // const st = moment.utc(reading.startTimestamp); + // // Time reading is in the middle of the start and end timestamp + // const timeReading = st.add(moment.utc(reading.endTimestamp).diff(st) / 2); + // // thetaData.push(timeReading.utc().format('YYYY-MM-DD HH:mm:ss')); + // thetaData.push(timeReading.utc().format('ddd, ll LTS')); + // let readingValue = reading.reading; + // if (state.graph.areaNormalization) { + // readingValue /= groupArea; + // } + // rData.push(readingValue); + // hoverText.push(` ${timeReading.format('ddd, ll LTS')}
${label}: ${readingValue.toPrecision(6)} ${unitLabel}`); + // }); + // } + + // //Find the largest and smallest usage in rData. + // if (minR == undefined || minR > Math.min(...rData)) { + // minR = Math.min(...rData); + // } + // if (maxR == undefined || maxR < Math.max(...rData)) { + // maxR = Math.max(...rData); + // } - // Use the min/max time found for the readings (and shifted as desired) as the - // x-axis range for the graph. - // Avoid pesky shifting timezones with utc. - const start = moment.utc(minTimestamp).toISOString(); // Need this???????????????????????????????????????????????????? - const end = moment.utc(maxTimestamp).toISOString(); + // // This variable contains all the elements (x and y values, line type, etc.) assigned to the data parameter of the Plotly object + // datasets.push({ + // name: label, + // x: thetaData, + // y: rData, + // text: hoverText, + // hoverinfo: 'text', + // type: 'scatterpolar', + // mode: 'lines', + // line: { + // shape: 'spline', + // width: 2, + // color: getGraphColor(colorID, DataType.Group) + // } + // }); + // } + // } + // } + // } + + // No range if minR or maxR is undefined. + if (minR == undefined || maxR == undefined) { + minR = 0; + maxR = 0; + } let layout: any; // Customize the layout of the plot @@ -180,7 +245,8 @@ function mapStateToProps(state: State) { polar: { radialaxis: { title: unitLabel, - range: [start, end], // Specifies the start and end points of visible part of graph + // Specifies the start and end points of the usage. + range: [minR, maxR], showgrid: true, gridcolor: '#ddd' }