Skip to content

Commit

Permalink
Merge branch 'develop' into feature/legendLabelsInTimeSeriesChart
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSteger authored Nov 19, 2019
2 parents 4f28141 + 8a23f68 commit 632efc4
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 36 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ buildscript {
}
}

version "5.3.2"
version "5.3.3"
group "OpenSpeedMonitor"

apply plugin: "eclipse"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/enums/chart-commons.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export enum ChartCommons {
CHART_HEADER_HEIGHT = 40,
COLOR_PREVIEW_SIZE = 10,
COLOR_PREVIEW_MARGIN = 5,
LABEL_HEIGHT = 30
LABEL_HEIGHT = 18
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,11 @@ osm-time-series-line-chart {
.summary-label {
font-weight: normal;
}

.legend-entry {
cursor: pointer;
}

.legend-text {
font-size: 12px;
}
99 changes: 70 additions & 29 deletions frontend/src/app/modules/time-series/services/line-chart.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {

import 'd3-transition';

import {brushX as d3BrushX} from 'd3-brush';
import {BrushBehavior, brushX as d3BrushX} from 'd3-brush';
import {EventResultDataDTO} from 'src/app/modules/time-series/models/event-result-data.model';
import {EventResultSeriesDTO} from 'src/app/modules/time-series/models/event-result-series.model';
import {EventResultPointDTO} from 'src/app/modules/time-series/models/event-result-point.model';
Expand All @@ -63,13 +63,17 @@ export class LineChartService {
// D3 margin conventions
// > With this convention, all subsequent code can ignore margins.
// see: https://bl.ocks.org/mbostock/3019563
private _margin = {top: 40, right: 70, bottom: 40, left: 60};
private _width = 600 - this._margin.left - this._margin.right;
private _height = 550 - this._margin.top - this._margin.bottom;
private _labelGroupHeight;
private _legendGroupTop = this._margin.top + this._height + 50;
private legendDataMap = {};
private brush;
private _margin: any = {top: 40, right: 70, bottom: 40, left: 60};
private _width: number = 600 - this._margin.left - this._margin.right;
private _height: number = 550 - this._margin.top - this._margin.bottom;
private _labelGroupHeight: number;
private _legendGroupTop: number = this._margin.top + this._height + 50;
private _legendGroupHeight: number;
private _legendGroupColumnWidth: number;
private _legendGroupColumns: number;
private legendDataMap: Object = {};
private brush: BrushBehavior<{}>;
private focusedLegendEntry: string;

// Map that holds all points clustered by their x-axis values
private _xAxisCluster: any = {};
Expand All @@ -78,7 +82,6 @@ export class LineChartService {
private _mouseEventCatcher: D3Selection<D3BaseType, {}, D3ContainerElement, {}>;
private _markerTooltip: D3Selection<HTMLDivElement, {}, D3ContainerElement, {}>;


constructor(private translationService: TranslateService) {
}

Expand All @@ -98,19 +101,17 @@ export class LineChartService {
* Draws a line chart for the given data into the given svg
*/
public drawLineChart(incomingData: EventResultDataDTO): void {

let data: TimeSeries[] = this.prepareData(incomingData);

if (data.length == 0) {
if (incomingData.series.length == 0) {
return;
}

let data: TimeSeries[] = this.prepareData(incomingData);
let chart: D3Selection<D3BaseType, {}, D3ContainerElement, {}> = d3Select('g#time-series-chart-drawing-area');
let xScale: D3ScaleTime<number, number> = this.getXScale(data);
let yScale: D3ScaleLinear<number, number> = this.getYScale(data);
this._labelGroupHeight = data.length * ChartCommons.LABEL_HEIGHT;
this.calculateLegendDimensions();
d3Select('osm-time-series-line-chart').transition().duration(500).style('visibility', 'visible');
d3Select('svg#time-series-chart').transition().duration(500).attr('height', this._height + this._labelGroupHeight + this._margin.top + this._margin.bottom);
d3Select('svg#time-series-chart').transition().duration(500).attr('height', this._height + this._legendGroupHeight + this._margin.top + this._margin.bottom);
d3Select('.x-axis').transition().call(this.updateXAxis, xScale);
d3Select('.y-axis').transition().call(this.updateYAxis, yScale, this._width, this._margin);
this.brush = d3BrushX().extent([[0, 0], [this._width, this._height]]);
Expand All @@ -135,6 +136,10 @@ export class LineChartService {
* Set the data for the legend after the incoming data is received
*/
public setLegendData(incomingData: EventResultDataDTO) {
if (incomingData.series.length == 0) {
return;
}

let labelDataMap = {};
incomingData.series.forEach((data: EventResultSeriesDTO) => {
if (incomingData.summaryLabels.length > 0 && incomingData.summaryLabels[0].key != "measurand") {
Expand Down Expand Up @@ -209,6 +214,7 @@ export class LineChartService {
.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._margin.left}, ${this._legendGroupTop})`);

Expand Down Expand Up @@ -314,6 +320,34 @@ export class LineChartService {
.nice();
}

private calculateLegendDimensions(): void {
let maximumLabelWidth: number = 1;
let labels = Object.keys(this.legendDataMap);

d3Select('g#time-series-chart-legend')
.append('g')
.attr('id', 'renderToCalculateMaxWidth')
.selectAll('.renderToCalculateMaxWidth')
.data(labels)
.enter()
.append('text')
.attr('class', 'legend-text')
.text(datum => this.legendDataMap[datum].text)
.each((datum, index, groups) => {
Array.from(groups).forEach((text) => {
if (text) {
maximumLabelWidth = Math.max(maximumLabelWidth, text.getBoundingClientRect().width)
}
});
});

d3Select('g#renderToCalculateMaxWidth').remove();

this._legendGroupColumnWidth = maximumLabelWidth + ChartCommons.COLOR_PREVIEW_SIZE + 30;
this._legendGroupColumns = Math.floor(this._width / this._legendGroupColumnWidth);
this._legendGroupHeight = Math.ceil(labels.length / this._legendGroupColumns) * ChartCommons.LABEL_HEIGHT + 30;
}

private getMaxValue(data: TimeSeries[]): number {
return d3Max(data, (dataItem: TimeSeries) => {
return d3Max(dataItem.values, (point: TimeSeriesPoint) => {
Expand Down Expand Up @@ -825,29 +859,36 @@ export class LineChartService {
.style('opacity', 0)
.remove()
)
.attr("transform", (datum, index) => this.position(index))
.attr("transform", (datum, index) => this.getPosition(index))
.on('click', (datum) => this.onMouseClick(datum, incomingData));
}

private position(index: number): string {
const columns = 3;
const columnWidth = 550;
const xMargin = 10;
const yMargin = 10;

const x = index % columns * columnWidth + xMargin;
const y = Math.floor(index / columns) * ChartCommons.LABEL_HEIGHT + yMargin;
private getPosition(index: number): string {
const x = index % this._legendGroupColumns * this._legendGroupColumnWidth;
const y = Math.floor(index / this._legendGroupColumns) * ChartCommons.LABEL_HEIGHT + 12;

return "translate(" + x + "," + y + ")";
}

private onMouseClick(labelKey: string, incomingData: EventResultDataDTO): void {
if (d3Event.metaKey) {
this.legendDataMap[labelKey].show ? this.legendDataMap[labelKey].show = false : this.legendDataMap[labelKey].show = true;
if (d3Event.metaKey || d3Event.ctrlKey) {
this.legendDataMap[labelKey].show = !this.legendDataMap[labelKey].show;
} else {
Object.keys(this.legendDataMap).forEach((legend) => {
this.legendDataMap[legend].show = legend === labelKey;
});
if (labelKey == this.focusedLegendEntry) {
Object.keys(this.legendDataMap).forEach((legend) => {
this.legendDataMap[legend].show = true;
});
this.focusedLegendEntry = "";
} else {
Object.keys(this.legendDataMap).forEach((legend) => {
if (legend === labelKey) {
this.legendDataMap[legend].show = true;
this.focusedLegendEntry = legend;
} else {
this.legendDataMap[legend].show = false;
}
});
}
}
this.drawLineChart(incomingData);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class CsiBenchmarkController extends ExceptionHandlerController {
}

String selectedCsiType = params.csiType == "visuallyComplete" ? 'csByWptVisuallyCompleteInPercent' : 'csByWptDocCompleteInPercent'
List<JobGroup> allJobGroups = JobGroup.findAllByNameInList(cmd.jobGroups)
List<JobGroup> allJobGroups = JobGroup.findAllByIdInList(cmd.jobGroups)

CsiAggregationInterval interval = CsiAggregationInterval.findByIntervalInMinutes(CsiAggregationInterval.DAILY)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ class PageComparisonController extends ExceptionHandlerController {
BarchartDatum comparativeSeries = mapToSeriesFor(aggregation.comperativeAggregation)
return new BarchartSeries(
stacked: false,
dimensionalUnit: aggregation.baseAggregation.selectedMeasurand.getMeasurandGroup().unit.label,
dimensionalUnit: aggregation.baseAggregation.unit,
data: [baseSeries, comparativeSeries]
)
}

private BarchartDatum mapToSeriesFor(BarchartAggregation aggregation) {
return new BarchartDatum(
measurand: i18nService.msg("de.iteratec.isr.measurand.${aggregation.selectedMeasurand.name}", aggregation.selectedMeasurand.name),
measurand: i18nService.msg("de.iteratec.isr.measurand.${aggregation.measurandName}", aggregation.measurandName),
value: aggregation.value,
aggregationValue: aggregation.aggregationValue,
grouping: "${aggregation.jobGroup.name} | ${aggregation.page.name}")
Expand Down
4 changes: 2 additions & 2 deletions grails-app/views/csiBenchmark/show.gsp
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@
data: {
from: selectedTimeFrame[0].toISOString(),
to: selectedTimeFrame[1].toISOString(),
selectedJobGroups: JSON.stringify($.map($("#folderSelectHtmlId option:selected"), function (e) {
return $(e).text()
jobGroups: JSON.stringify($.map($("#folderSelectHtmlId option:selected"), function (e) {
return parseInt(e.value);
})),
csiType: $('input[name=csiTypeRadios]:checked').val()
},
Expand Down

0 comments on commit 632efc4

Please sign in to comment.