|
|
|
@ -67,8 +67,8 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
|
|
|
|
|
initLineGenerator(): void { |
|
|
|
|
this.lineGenerator = this.d3.line() |
|
|
|
|
.x(d => this.xScale(d[1])) |
|
|
|
|
.y(d => this.yScale(d[0])); |
|
|
|
|
.x(d => this.xScale(d[0])) |
|
|
|
|
.y(d => this.yScale(d[1])); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public appendData(data: [number, number][]): void { |
|
|
|
@ -93,8 +93,8 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
if(this.series[idx].renderDots === true) { |
|
|
|
|
this.metricContainer.selectAll(`.metric-circle-${idx}`) |
|
|
|
|
.data(this.series[idx].datapoints) |
|
|
|
|
.attr('cx', d => this.xScale(d[1])) |
|
|
|
|
.attr('cy', d => this.yScale(d[0])); |
|
|
|
|
.attr('cx', d => this.xScale(d[0])) |
|
|
|
|
.attr('cy', d => this.yScale(d[1])); |
|
|
|
|
|
|
|
|
|
this._renderDots([data[idx]], idx); |
|
|
|
|
} |
|
|
|
@ -114,8 +114,8 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
.attr('fill', this.getSerieColor(serieIdx)) |
|
|
|
|
.attr('r', METRIC_CIRCLE_RADIUS) |
|
|
|
|
.style('pointer-events', 'none') |
|
|
|
|
.attr('cx', d => this.xScale(d[1])) |
|
|
|
|
.attr('cy', d => this.yScale(d[0])); |
|
|
|
|
.attr('cx', d => this.xScale(d[0])) |
|
|
|
|
.attr('cy', d => this.yScale(d[1])); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_renderLines(datapoints: number[][], serieIdx: number): void { |
|
|
|
@ -153,10 +153,10 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
.data(dataPairs) |
|
|
|
|
.enter() |
|
|
|
|
.append('line') |
|
|
|
|
.attr('x1', d => this.xScale(d[0][1])) |
|
|
|
|
.attr('x2', d => this.xScale(d[1][1])) |
|
|
|
|
.attr('y1', d => this.yScale(d[0][0])) |
|
|
|
|
.attr('y2', d => this.yScale(d[1][0])) |
|
|
|
|
.attr('x1', d => this.xScale(d[0][0])) |
|
|
|
|
.attr('x2', d => this.xScale(d[1][0])) |
|
|
|
|
.attr('y1', d => this.yScale(d[0][1])) |
|
|
|
|
.attr('y2', d => this.yScale(d[1][1])) |
|
|
|
|
.attr('stroke-opacity', 0.7) |
|
|
|
|
.style('stroke-width', 1) |
|
|
|
|
.style('stroke', d => { |
|
|
|
@ -178,52 +178,6 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
if(metricOptions.renderDots === true) { |
|
|
|
|
this._renderDots(datapoints, metricOptions.serieIdx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let upperBoundDatapoints = []; |
|
|
|
|
let lowerBoundDatapoints = []; |
|
|
|
|
if( |
|
|
|
|
this.options.bounds !== undefined && |
|
|
|
|
this.options.bounds.upper !== undefined && |
|
|
|
|
this.options.bounds.lower !== undefined |
|
|
|
|
) { |
|
|
|
|
this.series.forEach(serie => { |
|
|
|
|
if(serie.target === this.formatedBound(this.options.bounds.upper, metricOptions.target)) { |
|
|
|
|
upperBoundDatapoints = serie.datapoints; |
|
|
|
|
} |
|
|
|
|
if(serie.target === this.formatedBound(this.options.bounds.lower, metricOptions.target)) { |
|
|
|
|
lowerBoundDatapoints = serie.datapoints; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(upperBoundDatapoints.length > 0 && lowerBoundDatapoints.length > 0) { |
|
|
|
|
const zip = (arr1, arr2) => arr1.map((k, i) => [k[0],k[1], arr2[i][0]]); |
|
|
|
|
const data = zip(upperBoundDatapoints, lowerBoundDatapoints); |
|
|
|
|
|
|
|
|
|
this.metricContainer.append('path') |
|
|
|
|
.datum(data) |
|
|
|
|
.attr('fill', metricOptions.color) |
|
|
|
|
.attr('stroke', 'none') |
|
|
|
|
.attr('opacity', '0.3') |
|
|
|
|
.attr('d', this.d3.area() |
|
|
|
|
.x((d: number[]) => this.xScale(d[1])) |
|
|
|
|
.y0((d: number[]) => this.yScale(d[0])) |
|
|
|
|
.y1((d: number[]) => this.yScale(d[2])) |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(metricOptions.confidence > 0) { |
|
|
|
|
this.metricContainer.append('path') |
|
|
|
|
.datum(datapoints) |
|
|
|
|
.attr('fill', metricOptions.color) |
|
|
|
|
.attr('stroke', 'none') |
|
|
|
|
.attr('opacity', '0.3') |
|
|
|
|
.attr('d', this.d3.area() |
|
|
|
|
.x((d: [number, number]) => this.xScale(d[1])) |
|
|
|
|
.y0((d: [number, number]) => this.yScale(d[0] + metricOptions.confidence)) |
|
|
|
|
.y1((d: [number, number]) => this.yScale(d[0] - metricOptions.confidence)) |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
updateCrosshair(): void { |
|
|
|
@ -324,16 +278,16 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getClosestIndex(datapoints: [number, number][], xValue: number, yValue: number): number { |
|
|
|
|
let columnIdx; // 0 for y value, 1 for x value
|
|
|
|
|
let columnIdx; // 1 for y value, 0 for x value
|
|
|
|
|
let value; // xValue ot y Value
|
|
|
|
|
switch(this.options.crosshair.orientation) { |
|
|
|
|
case CrosshairOrientation.HORIZONTAL: |
|
|
|
|
columnIdx = 0; |
|
|
|
|
value = yValue; |
|
|
|
|
value = xValue; |
|
|
|
|
break; |
|
|
|
|
case CrosshairOrientation.VERTICAL: |
|
|
|
|
columnIdx = 1; |
|
|
|
|
value = xValue; |
|
|
|
|
value = yValue; |
|
|
|
|
break; |
|
|
|
|
case CrosshairOrientation.BOTH: |
|
|
|
|
// TODO: maybe use voronoi
|
|
|
|
@ -364,7 +318,7 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getValueInterval(columnIdx: number): number | undefined { |
|
|
|
|
// columnIdx: 0 for y, 1 for x
|
|
|
|
|
// columnIdx: 1 for y, 0 for x
|
|
|
|
|
// inverval: x/y value interval between data points
|
|
|
|
|
// TODO: move it to base/state instead of timeInterval
|
|
|
|
|
const intervals = _.map(this.series, serie => { |
|
|
|
@ -429,8 +383,8 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const yPosition = this.yScale(closestDatapoint[0]); |
|
|
|
|
const xPosition = this.xScale(closestDatapoint[1]); |
|
|
|
|
const xPosition = this.xScale(closestDatapoint[0]); |
|
|
|
|
const yPosition = this.yScale(closestDatapoint[1]); |
|
|
|
|
this.moveCrosshairCircle(xPosition, yPosition, serieIdx); |
|
|
|
|
|
|
|
|
|
points.push({ |
|
|
|
@ -448,20 +402,20 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
|
|
|
|
|
if(useOutOfRange === false) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
let columnIdx; // 0 for y value, 1 for x value
|
|
|
|
|
let columnIdx; // 1 for y value, 0 for x value
|
|
|
|
|
let value; // xValue ot y Value
|
|
|
|
|
switch(this.options.crosshair.orientation) { |
|
|
|
|
case CrosshairOrientation.HORIZONTAL: |
|
|
|
|
case CrosshairOrientation.VERTICAL: |
|
|
|
|
columnIdx = 0; |
|
|
|
|
value = yValue; |
|
|
|
|
value = xValue; |
|
|
|
|
break; |
|
|
|
|
case CrosshairOrientation.VERTICAL: |
|
|
|
|
case CrosshairOrientation.HORIZONTAL: |
|
|
|
|
columnIdx = 1; |
|
|
|
|
value = xValue; |
|
|
|
|
value = yValue; |
|
|
|
|
break; |
|
|
|
|
case CrosshairOrientation.BOTH: |
|
|
|
|
// TODO: maybe use voronoi
|
|
|
|
|
columnIdx = 0; |
|
|
|
|
columnIdx = 1; |
|
|
|
|
value = yValue; |
|
|
|
|
default: |
|
|
|
|
throw new Error(`Unknown type of crosshair orientaion: ${this.options.crosshair.orientation}`); |
|
|
|
|