Skip to content

Commit

Permalink
Merge pull request #291 from iteratec/feature/legendLabelsInTimeSerie…
Browse files Browse the repository at this point in the history
…sChart

Feature: Legend labels in time series chart
  • Loading branch information
j0weiss authored Dec 3, 2019
2 parents 8a23f68 + 632efc4 commit 7198e46
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ osm-time-series-line-chart {
}
}

.summary-label-key {
font-weight: bold;
}

.summary-label {
font-weight: normal;
}

.legend-entry {
cursor: pointer;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import {EventResultSeriesDTO} from './event-result-series.model';
import {SummaryLabel} from "./summary-label.model";

export interface EventResultDataDTO {
series: EventResultSeriesDTO[];
summaryLabels: SummaryLabel[];
}

export class EventResultData implements EventResultDataDTO {
series: EventResultSeriesDTO[];
summaryLabels: SummaryLabel[];

constructor() {
this.series = [];
this.summaryLabels = [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface SummaryLabel {
key: string;
label: string;
}
105 changes: 88 additions & 17 deletions frontend/src/app/modules/time-series/services/line-chart.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {TimeSeriesPoint} from 'src/app/modules/time-series/models/time-series-po
import {parseDate} from 'src/app/utils/date.util';
import {getColorScheme} from 'src/app/enums/color-scheme.enum';
import {ChartCommons} from "../../../enums/chart-commons.enum";
import {SummaryLabel} from "../models/summary-label.model";

/**
* Generate line charts with ease and fun 😎
Expand All @@ -67,10 +68,9 @@ export class LineChartService {
private _height: number = 550 - this._margin.top - this._margin.bottom;
private _labelGroupHeight: number;
private _legendGroupTop: number = this._margin.top + this._height + 50;
private _legendGroupLeft: number = this._margin.left;
private _legendGroupHeight;
private _legendGroupColumnWidth;
private _legendGroupColumns;
private _legendGroupHeight: number;
private _legendGroupColumnWidth: number;
private _legendGroupColumns: number;
private legendDataMap: Object = {};
private brush: BrushBehavior<{}>;
private focusedLegendEntry: string;
Expand Down Expand Up @@ -118,6 +118,7 @@ this.calculateLegendDimensions();

this.addBrush(chart, xScale, yScale, data);
this.addLegendsToChart(chart, incomingData);
this.setSummaryLabel(chart, incomingData.summaryLabels);
this.addDataLinesToChart(chart, xScale, yScale, data);

this.bringMouseMarkerToTheFront(xScale, yScale);
Expand All @@ -141,12 +142,14 @@ this.calculateLegendDimensions();

let labelDataMap = {};
incomingData.series.forEach((data: EventResultSeriesDTO) => {
let label = data.identifier;
if (incomingData.summaryLabels.length > 0 && incomingData.summaryLabels[0].key != "measurand") {
data.identifier = this.translateMeasurand(data);
}
let key = this.generateKey(data);
labelDataMap[key] = {
text: label,
text: data.identifier,
key: key,
show: true,
show: true
}
});
this.legendDataMap = labelDataMap;
Expand All @@ -156,27 +159,40 @@ this.calculateLegendDimensions();
* Prepares the incoming data for drawing with D3.js
*/
private prepareData(incomingData: EventResultDataDTO): TimeSeries[] {

return incomingData.series.map((data: EventResultSeriesDTO) => {
let lineChartData: TimeSeries = new TimeSeries();
if (incomingData.summaryLabels.length > 0 && incomingData.summaryLabels[0].key != "measurand") {
data.identifier = this.translateMeasurand(data);
}
lineChartData.key = this.generateKey(data);

lineChartData.values = data.data.map((point: EventResultPointDTO) => {
let lineChartDataPoint: TimeSeriesPoint = new TimeSeriesPoint();
lineChartDataPoint.date = parseDate(point.date);
lineChartDataPoint.value = point.value;
lineChartDataPoint.tooltipText = data.jobGroup + ' | ' + data.measuredEvent + ' : '; // TODO Set exact label text when IT-2793 is implemented
lineChartDataPoint.tooltipText = data.identifier + ' : ';
return lineChartDataPoint;
});

return lineChartData;
});
}

private translateMeasurand(data: EventResultSeriesDTO): string {
let splitLabelList: string[] = data.identifier.split(' | ');
let splitLabel: string = this.translationService.instant('frontend.de.iteratec.isr.measurand.' + splitLabelList[0]);
if (!splitLabel.startsWith('frontend.de.iteratec.isr.measurand.')) {
splitLabelList[0] = splitLabel;
}
return splitLabelList.join(' | ');
}

private generateKey(data: EventResultSeriesDTO): string {
return data.jobGroup
+ data.measuredEvent
+ data.data.length;
let key: string = data.identifier.replace(/[^_a-zA-Z0-9-]/g, "");
if (new RegExp('[0-9]').test(key.charAt(0))) {
key = key.replace(/[0-9]/, '_');
}
return key;
}

/**
Expand All @@ -189,14 +205,69 @@ this.calculateLegendDimensions();
.attr('width', this._width + this._margin.left + this._margin.right)
.attr('height', 0);

svg.append('g')
.attr('id', 'header-group')
.attr('transform', `translate(${this._margin.left}, ${this._margin.top - 16})`);

let chart = svg.append('g') // g = grouping element; group all other stuff into the chart
.attr('id', 'time-series-chart-drawing-area')
.attr('transform', `translate(${this._margin.left}, ${this._margin.top})`); // translates the origin to the top left corner (default behavior of D3)

svg.append('g')
.attr('id', 'time-series-chart-legend')
.attr('class', 'legend-group')
.attr('transform', `translate(${this._legendGroupLeft}, ${this._legendGroupTop})`);
.attr('transform', `translate(${this._margin.left}, ${this._legendGroupTop})`);

return svg.append('g') // g = grouping element; group all other stuff into the chart
.attr('id', 'time-series-chart-drawing-area')
.attr('transform', 'translate(' + this._margin.left + ', ' + this._margin.top + ')'); // translates the origin to the top left corner (default behavior of D3)
return chart;
}

private setSummaryLabel(chart: D3Selection<D3BaseType, {}, D3ContainerElement, {}>, summaryLabels: SummaryLabel[]): void {
d3Select('g#header-group').selectAll('.summary-label-text').remove();
if (summaryLabels.length > 0) {
d3Select('#header-group')
.append('g')
.attr('class', 'summary-label-text')
.append('text')
.attr('id', 'summary-label-part0')
.attr('x', this._width / 2)
.attr('text-anchor', 'middle')
.attr('fill', '#555555');

summaryLabels.forEach((summaryLabel: SummaryLabel, index: number) => {
this.translationService
.get('frontend.de.iteratec.osm.timeSeries.chart.label.' + summaryLabel.key)
.pipe(take(1))
.subscribe((key: string) => {
if (summaryLabel.key == 'measurand') {
this.translationService
.get('frontend.de.iteratec.isr.measurand.' + summaryLabel.label)
.pipe(take(1))
.subscribe((label: string) => {
if (label.startsWith('frontend.de.iteratec.isr.measurand.')) {
label = summaryLabel.label
}
label = index < summaryLabels.length - 1 ? `${label} | ` : label;
this.addSummaryLabel(key, label, index);
});
} else {
const label: string = index < summaryLabels.length - 1 ? `${summaryLabel.label} | ` : summaryLabel.label;
this.addSummaryLabel(key, label, index);
}
});
});
chart.selectAll('.summary-label-text').remove();
}
}

private addSummaryLabel(key: string, label: string, index: number): void {
d3Select(`#summary-label-part${index}`)
.append('tspan')
.attr('id', `summary-label-part${index + 1}`)
.attr('class', 'summary-label-key')
.text(`${key}: `)
.append('tspan')
.attr('class', 'summary-label')
.text(label);
}

public startResize(svgElement: ElementRef): void {
Expand Down Expand Up @@ -538,7 +609,7 @@ this.calculateLegendDimensions();
})
// fade in
.transition().duration(500).style('opacity', (timeSeries: TimeSeries) => {
return (this.legendDataMap[timeSeries.key].show) ? '1' : '0.2';
return (this.legendDataMap[timeSeries.key].show) ? '1' : '0.1';
});

return resultingSelection;
Expand Down
8 changes: 8 additions & 0 deletions grails-app/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,9 @@ frontend.default.button.save=Save
frontend.default.button.cancel=Cancel
frontend.de.iteratec.osm.measurement.setup.title=Measurement Setup
frontend.de.iteratec.osm.applicationDashboard.kpi.title=Total Customer Satisfaction
frontend.de.iteratec.isr.measurand.PAGE_CONSTRUCTION_STARTED=Is it happening?
frontend.de.iteratec.isr.measurand.PAGE_SHOWS_USEFUL_CONTENT=Is it useful?
frontend.de.iteratec.isr.measurand.PAGE_IS_USABLE=Is it useable?
frontend.de.iteratec.isr.measurand.DOC_COMPLETE_TIME=Document Complete
frontend.de.iteratec.isr.measurand.DOM_TIME=DOM Time
frontend.de.iteratec.isr.measurand.FIRST_BYTE=First Byte
Expand Down Expand Up @@ -1274,3 +1277,8 @@ frontend.de.iteratec.osm.barchart.filter.noFilterAsc=Ascending
frontend.de.iteratec.osm.barchart.filter.label=Filter
frontend.de.iteratec.osm.barchart.filter.customerJourneyHeader=Customer Journey
frontend.de.iteratec.osm.timeSeries.loadTimes=Load times
frontend.de.iteratec.osm.timeSeries.chart.label.measurand=Measurand
frontend.de.iteratec.osm.timeSeries.chart.label.application=Application
frontend.de.iteratec.osm.timeSeries.chart.label.measuredEvent=Measured step
frontend.de.iteratec.osm.timeSeries.chart.label.location=Location
frontend.de.iteratec.osm.timeSeries.chart.label.connectivity=Connectivity
8 changes: 8 additions & 0 deletions grails-app/i18n/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,9 @@ frontend.default.button.save=Speichern
frontend.default.button.cancel=Abbrechen
frontend.de.iteratec.osm.measurement.setup.title=Setup Messungen
frontend.de.iteratec.osm.applicationDashboard.kpi.title=Gesamte Kundenzufriedenheit
frontend.de.iteratec.isr.measurand.PAGE_CONSTRUCTION_STARTED=Is it happening?
frontend.de.iteratec.isr.measurand.PAGE_SHOWS_USEFUL_CONTENT=Is it useful?
frontend.de.iteratec.isr.measurand.PAGE_IS_USABLE=Is it useable?
frontend.de.iteratec.isr.measurand.DOC_COMPLETE_TIME=Document Complete
frontend.de.iteratec.isr.measurand.DOM_TIME=DOM Time
frontend.de.iteratec.isr.measurand.FIRST_BYTE=First Byte
Expand Down Expand Up @@ -1248,3 +1251,8 @@ frontend.de.iteratec.osm.barchart.filter.noFilterAsc=Aufsteigend
frontend.de.iteratec.osm.barchart.filter.label=Filtern
frontend.de.iteratec.osm.barchart.filter.customerJourneyHeader=Customer Journey
frontend.de.iteratec.osm.timeSeries.loadTimes=Ladezeiten
frontend.de.iteratec.osm.timeSeries.chart.label.measurand=Messgröße
frontend.de.iteratec.osm.timeSeries.chart.label.application=Anwendung
frontend.de.iteratec.osm.timeSeries.chart.label.measuredEvent=Messschritt
frontend.de.iteratec.osm.timeSeries.chart.label.location=Location
frontend.de.iteratec.osm.timeSeries.chart.label.connectivity=Anbindung
Loading

0 comments on commit 7198e46

Please sign in to comment.