Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Support monthly indicators in line graphs #107

Merged
merged 3 commits into from
Oct 25, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 10 additions & 18 deletions src/app/charts/line-graph.component.ts
Original file line number Diff line number Diff line change
@@ -40,14 +40,12 @@ export class LineGraphComponent implements OnInit, OnDestroy {
private yScale; // D3 scale in Y
private htmlElement; // Host HTMLElement
private valueline; // Base for a chart line
private xRange: Array<string>; // Min, max date range
private xRange: Array<Date>; // Min, max date range
private xData: Array<number>; // Stores x axis data as integers rather than dates, necessary for trendline math
private yData: Array<number>; // Stores primary y axis data, multi-use
private trendData: Array<DataPoint>; // Formatted data for the trendline
private timeFormat: string; // Date formatting for x axis labels (e.g, '%Y-%m')
private scrubber; // Lump of scrubber elements
private scrubberLine; // Scrubber element, independent
private timeOptions: any;
private timeFormat: string;
private id: string; // Randomly generated int # used to distinguish the chart for drawing and isolated chart scrubber
// Not a perfect solution should the random int and indicator be the same
// However, this is quite unlikely (1/10000, and even less likely by way of app use)
@@ -61,10 +59,6 @@ export class LineGraphComponent implements OnInit, OnDestroy {
constructor(private element: ElementRef, private chartService: ChartService) {
this.htmlElement = this.element.nativeElement;
this.host = D3.select(this.element.nativeElement);
this.timeOptions = {
'yearly': '%Y',
'daily': '%Y-%m-%d'
}
}

@HostListener('window:resize', ['$event'])
@@ -129,16 +123,12 @@ export class LineGraphComponent implements OnInit, OnDestroy {
private filterData(): void {
// Preserves parent data by fresh copying indicator data that will undergo processing
let clippedData = _.cloneDeep(_.find(this.data, obj => obj.indicator.name === this.indicator.name));
if (clippedData) {
this.timeFormat = this.timeOptions[clippedData.time_agg];
}
_.has(clippedData, 'data') ? this.extractedData = clippedData['data'] : this.extractedData = [];
// Remove empty day in non-leap years (affects only daily data)
if (this.extractedData[365] && this.extractedData[365]['date'] == null) {
this.extractedData.pop();
}
// Parse out avg data for ease of use later
this.yData = _.map(this.extractedData, d => d.values.avg);
this.timeFormat = clippedData.time_format;
}

/* Will setup the chart basics */
@@ -163,9 +153,11 @@ export class LineGraphComponent implements OnInit, OnDestroy {

// Set axis and line scales
private setLineScales(): void {
// Time scales only recognize annual and daily data
var parseTime = D3.timeParse(this.timeFormat);
this.extractedData.forEach(d => d.date = parseTime(d.date));
// Sort data by date ascending
this.extractedData.sort(function(a, b) {return +a.date - +b.date;});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me: That seems like a kludge, can we do better?
The Google: microsoft/TypeScript#2361 (comment)
Me: 😱

Coercion it is!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol javascript.

// Parse out avg data for ease of use later
this.yData = _.map(this.extractedData, d => d.values.avg);

this.xRange = D3.extent(this.extractedData, d => d.date);
this.xScale.domain(this.xRange);

@@ -249,9 +241,9 @@ export class LineGraphComponent implements OnInit, OnDestroy {
var y1 = leastSquaresCoeff[0] + leastSquaresCoeff[1];
var x2 = this.xRange[0];
var y2 = leastSquaresCoeff[0] * this.xData.length + leastSquaresCoeff[1];
this.trendData = [{'date': x1, 'value': y2}, {'date': x2, 'value': y1}];
let trendData = [{'date': x1, 'value': y2}, {'date': x2, 'value': y1}];
// Add trendline
this.drawLine(this.trendData, 'trendline');
this.drawLine(trendData, 'trendline');
}
}

13 changes: 7 additions & 6 deletions src/app/models/chart.ts
Original file line number Diff line number Diff line change
@@ -34,18 +34,19 @@ export class ChartData {
indicator: string;
data: MultiDataPoint[];
time_agg: string[];
time_format: string;
}

export class DataPoint {
date: string;
value: number;
date: Date;
value: number;
}

export class MultiDataPoint {
date: string;
date: Date;
values: {
'avg': Number,
'min': Number,
'max': Number
'avg': number,
'min': number,
'max': number
};
}
16 changes: 14 additions & 2 deletions src/app/services/chart.service.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { ChartData, MultiDataPoint } from '../models/chart';

import * as moment from 'moment';
import * as _ from 'lodash';
import * as D3 from 'd3';

/*
* Chart Service
@@ -19,6 +20,13 @@ export class ChartService {
public multiChartScrubberInfoObservable = this._multiChartScrubberInfo.asObservable();
public multiChartScrubberHoverObservable = this._multiChartScrubberHover.asObservable();


private timeOptions = {
'yearly': '%Y',
'daily': '%Y-%m-%d',
'monthly': '%Y-%m'
};

constructor() {}

// receive and ship mousemove event
@@ -51,9 +59,12 @@ export class ChartService {
_.each(data, obj => {
let indicatorData: MultiDataPoint[] = [];
let indicator = obj.indicator;
let timeFormat = this.timeOptions[indicator.time_aggregation];
let parseTime = D3.timeParse(timeFormat);

_.each(obj.data, (values, key) => {
indicatorData.push({
'date': key,
'date': parseTime(key),
'values': values
} as MultiDataPoint);
});
@@ -63,7 +74,8 @@ export class ChartService {
chartData.push({
'indicator': indicator,
'data': indicatorData,
'time_agg': indicator.time_aggregation
'time_agg': indicator.time_aggregation,
'time_format': timeFormat
} as ChartData);
}
});