libs/d3/src/lib/d3-timeseries-graph/controls/d3-graph-pan-zoom-interaction/d3-graph-pan-zoom-interaction.component.ts
selector | n52-d3-graph-pan-zoom-interaction |
styleUrls | ./d3-graph-pan-zoom-interaction.component.scss |
Properties |
|
Methods |
|
constructor(graphId: D3GraphId, graphs: D3Graphs, graphHelper: D3GraphHelperService)
|
||||||||||||
Parameters :
|
Public adjustBackground | ||||||||||||||||||
adjustBackground(background: d3.Selection
|
||||||||||||||||||
Parameters :
Returns :
void
|
Public dragEndBackground |
dragEndBackground()
|
Returns :
void
|
Public dragMoveBackground |
dragMoveBackground()
|
Returns :
void
|
Public dragStartBackground |
dragStartBackground()
|
Returns :
void
|
Private drawDragRectangle | ||||||||||||
drawDragRectangle(d3GraphElem: d3.Selection
|
||||||||||||
Function that configurates and draws the rectangle.
Parameters :
Returns :
void
|
Private getxDomain | ||||||||||||||||||||
getxDomain(start: number, end: number, graphExtent: D3GraphExtent, preparedData: any)
|
||||||||||||||||||||
Function that returns the timestamp of provided x diagram coordinates.
Parameters :
|
Public graphInitialized | ||||||
graphInitialized(graph: D3TimeseriesGraphComponent)
|
||||||
Parameters :
Returns :
void
|
Private panEndHandler |
panEndHandler()
|
Function that ends the dragging control.
Returns :
void
|
Private panMoveHandler |
panMoveHandler()
|
Function that controlls the panning (dragging) of the graph.
Returns :
void
|
Private panStartHandler |
panStartHandler()
|
Function starting the drag handling for the diagram.
Returns :
void
|
Private resetDrag |
resetDrag()
|
Function that disables the drawing rectangle control.
Returns :
void
|
Public zoomEndBackground |
zoomEndBackground()
|
Returns :
void
|
Private zoomEndHandler | ||||||||||||
zoomEndHandler(timespan: Timespan, graphExtent: D3GraphExtent, preparedData: any)
|
||||||||||||
Function that ends the zoom handling and calculates the via zoom selected time interval.
Parameters :
Returns :
void
|
Private zoomHandler | ||||||||||||
zoomHandler(d3GraphElem: d3.Selection
|
||||||||||||
Function that draws a rectangle when zoom is started and the mouse is moving.
Parameters :
Returns :
void
|
Public zoomMoveBackground |
zoomMoveBackground()
|
Returns :
void
|
Public zoomStartBackground |
zoomStartBackground()
|
Returns :
void
|
Private zoomStartHandler | |||||||||
zoomStartHandler(timespan: Timespan, backgroundElem: d3.Selection
|
|||||||||
Function that starts the zoom handling.
Parameters :
Returns :
void
|
Public Optional adjustYAxis | ||||||
adjustYAxis(axis: YAxis)
|
||||||
Inherited from
D3TimeseriesGraphControl
|
||||||
Defined in
D3TimeseriesGraphControl:77
|
||||||
Parameters :
Returns :
void
|
Public Abstract graphInitialized | ||||||
graphInitialized(graph: D3TimeseriesGraphComponent)
|
||||||
Inherited from
D3TimeseriesGraphControl
|
||||||
Defined in
D3TimeseriesGraphControl:75
|
||||||
Parameters :
Returns :
any
|
Public ngAfterViewInit |
ngAfterViewInit()
|
Inherited from
D3TimeseriesGraphControl
|
Defined in
D3TimeseriesGraphControl:63
|
Returns :
void
|
Public ngOnDestroy |
ngOnDestroy()
|
Inherited from
D3TimeseriesGraphControl
|
Defined in
D3TimeseriesGraphControl:71
|
Returns :
void
|
Private background |
Type : d3.Selection<SVGSVGElement | any | any | any>
|
Private d3Graph |
Type : D3TimeseriesGraphComponent
|
Private dragCurrent |
Type : [number, number]
|
Private dragging |
Type : boolean
|
Private draggingMove |
Type : boolean
|
Private dragMoveRange |
Type : [number, number]
|
Private dragMoveStart |
Type : number
|
Private dragRect |
Type : any
|
Private dragRectG |
Type : any
|
Private dragStart |
Type : [number, number]
|
Private dragTimeStart |
Type : number
|
Private graph |
Type : d3.Selection<SVGSVGElement | any | any | any>
|
Private graphExtent |
Type : D3GraphExtent
|
Private isHoverable |
Type : boolean
|
Private plotWhileDrag |
Type : boolean
|
Private preparedData |
Type : InternalDataEntry[]
|
Private timespan |
Type : Timespan
|
Private xAxisRangeOrigin |
Type : any
|
Default value : []
|
Private xAxisRangePan |
Type : [number, number]
|
import { Component } from '@angular/core';
import { Timespan } from '@helgoland/core';
import * as d3 from 'd3';
import { D3GraphHelperService } from '../../../helper/d3-graph-helper.service';
import { D3GraphId } from '../../../helper/d3-graph-id.service';
import { D3Graphs } from '../../../helper/d3-graphs.service';
import { InternalDataEntry } from '../../../model/d3-general';
import { D3GraphExtent, D3TimeseriesGraphControl } from '../../d3-timeseries-graph-control';
import { D3TimeseriesGraphComponent } from '../../d3-timeseries-graph.component';
@Component({
selector: 'n52-d3-graph-pan-zoom-interaction',
template: '',
styleUrls: ['./d3-graph-pan-zoom-interaction.component.scss']
})
export class D3GraphPanZoomInteractionComponent extends D3TimeseriesGraphControl {
private dragging: boolean;
private dragStart: [number, number];
private dragCurrent: [number, number];
private draggingMove: boolean;
private dragMoveStart: number;
private dragMoveRange: [number, number];
private dragTimeStart: number;
private plotWhileDrag: boolean;
private isHoverable: boolean;
private dragRect: any;
private dragRectG: any;
private xAxisRangeOrigin: any = [];
private xAxisRangePan: [number, number];
private d3Graph: D3TimeseriesGraphComponent;
private timespan: Timespan;
private graphExtent: D3GraphExtent;
private background: d3.Selection<SVGSVGElement, any, any, any>;
private graph: d3.Selection<SVGSVGElement, any, any, any>;
private preparedData: InternalDataEntry[];
constructor(
protected graphId: D3GraphId,
protected graphs: D3Graphs,
protected graphHelper: D3GraphHelperService
) {
super(graphId, graphs, graphHelper);
}
public graphInitialized(graph: D3TimeseriesGraphComponent) {
this.d3Graph = graph;
}
public adjustBackground(
background: d3.Selection<SVGSVGElement, any, any, any>,
graphExtent: D3GraphExtent,
preparedData: InternalDataEntry[],
graph: d3.Selection<SVGSVGElement, any, any, any>,
timespan: Timespan
) {
this.timespan = timespan;
this.graphExtent = graphExtent;
this.background = background;
this.graph = graph;
this.preparedData = preparedData;
}
public zoomStartBackground() {
this.zoomStartHandler(this.timespan, this.background);
}
public zoomMoveBackground() {
this.zoomHandler(this.graph, this.background, this.graphExtent);
}
public zoomEndBackground() {
this.zoomEndHandler(this.timespan, this.graphExtent, this.preparedData);
}
public dragStartBackground() {
this.panStartHandler();
}
public dragMoveBackground() {
this.panMoveHandler();
}
public dragEndBackground() {
this.panEndHandler();
}
/**
* Function starting the drag handling for the diagram.
*/
private panStartHandler() {
this.dragTimeStart = new Date().valueOf();
this.draggingMove = false;
this.dragMoveStart = d3.event.x;
this.dragMoveRange = [this.timespan.from, this.timespan.to];
this.isHoverable = this.d3Graph.plotOptions.hoverable;
this.d3Graph.plotOptions.hoverable = false;
}
/**
* Function that controlls the panning (dragging) of the graph.
*/
private panMoveHandler() {
this.draggingMove = true;
const timeDiff = (new Date().valueOf() - this.dragTimeStart) >= 50;
if (this.dragMoveStart && this.draggingMove && timeDiff) {
if (!this.plotWhileDrag) {
this.plotWhileDrag = true;
this.dragTimeStart = new Date().valueOf();
const diff = -(d3.event.x - this.dragMoveStart); // d3.event.subject.x);
const amountTimestamp = this.dragMoveRange[1] - this.dragMoveRange[0];
const ratioTimestampDiagCoord = amountTimestamp / this.graphExtent.width;
const newTimeMin = this.dragMoveRange[0] + (ratioTimestampDiagCoord * diff);
const newTimeMax = this.dragMoveRange[1] + (ratioTimestampDiagCoord * diff);
this.xAxisRangePan = [newTimeMin, newTimeMax];
this.d3Graph.setTimespan({ from: this.xAxisRangePan[0], to: this.xAxisRangePan[1] });
this.d3Graph.drawBaseGraph();
this.plotWhileDrag = false;
}
}
}
/**
* Function that ends the dragging control.
*/
private panEndHandler() {
this.d3Graph.plotOptions.hoverable = this.isHoverable;
if (this.xAxisRangePan) {
this.d3Graph.changeTime(this.xAxisRangePan[0], this.xAxisRangePan[1]);
this.dragMoveStart = null;
this.draggingMove = false;
this.xAxisRangePan = null;
this.dragTimeStart = null;
}
}
/**
* Function that starts the zoom handling.
*/
private zoomStartHandler(timespan: Timespan, backgroundElem: d3.Selection<SVGSVGElement, any, any, any>) {
this.dragging = false;
// dependent on point or line hovering
this.dragStart = d3.mouse(backgroundElem.node());
this.xAxisRangeOrigin.push([timespan.from, timespan.to]);
}
/**
* Function that draws a rectangle when zoom is started and the mouse is moving.
*/
private zoomHandler(d3GraphElem: d3.Selection<SVGSVGElement, any, any, any>, backgroundElem: d3.Selection<SVGSVGElement, any, any, any>, graphExtent: D3GraphExtent) {
this.dragging = true;
this.drawDragRectangle(d3GraphElem, backgroundElem, graphExtent);
}
/**
* Function that ends the zoom handling and calculates the via zoom selected time interval.
*/
private zoomEndHandler(timespan: Timespan, graphExtent: D3GraphExtent, preparedData: any) {
if (!this.dragStart || !this.dragging) {
if (this.xAxisRangeOrigin[0]) {
// back to origin range (from - to)
this.d3Graph.changeTime(this.xAxisRangeOrigin[0][0], this.xAxisRangeOrigin[0][1]);
this.xAxisRangeOrigin = [];
this.d3Graph.redrawCompleteGraph();
}
} else {
let newTimespan: [number, number];
if (this.dragStart[0] <= this.dragCurrent[0]) {
newTimespan = this.getxDomain(this.dragStart[0], this.dragCurrent[0], graphExtent, preparedData);
} else {
newTimespan = this.getxDomain(this.dragCurrent[0], this.dragStart[0], graphExtent, preparedData);
}
this.d3Graph.changeTime(newTimespan[0], newTimespan[1]);
}
this.dragStart = null;
this.dragging = false;
this.resetDrag();
}
/**
* Function that returns the timestamp of provided x diagram coordinates.
* @param start {Number} Number with the minimum diagram coordinate.
* @param end {Number} Number with the maximum diagram coordinate.
*/
private getxDomain(start: number, end: number, graphExtent: D3GraphExtent, preparedData: any): [number, number] {
const domMinArr = [];
const domMaxArr = [];
let domMin: number;
let domMax: number;
let tmp;
let lowestMin = Number.POSITIVE_INFINITY;
let lowestMax = Number.POSITIVE_INFINITY;
start += graphExtent.leftOffset;
end += graphExtent.leftOffset;
preparedData.forEach((entry) => {
domMinArr.push(entry.data.find((elem, index, array) => {
if (elem.xDiagCoord) {
if (elem.xDiagCoord >= start) {
return array[index] !== undefined;
}
}
}));
domMaxArr.push(entry.data.find((elem, index, array) => {
if (elem.xDiagCoord >= end) {
return array[index] !== undefined;
}
}));
});
for (let i = 0; i <= domMinArr.length - 1; i++) {
if (domMinArr[i] != null) {
tmp = domMinArr[i].xDiagCoord;
if (tmp < lowestMin) {
lowestMin = tmp;
domMin = domMinArr[i].timestamp;
}
}
}
for (let j = 0; j <= domMaxArr.length - 1; j++) {
if (domMaxArr[j] != null) {
tmp = domMaxArr[j].xDiagCoord;
if (tmp < lowestMax) {
lowestMax = tmp;
domMax = domMaxArr[j].timestamp;
}
}
}
return [domMin, domMax];
}
/**
* Function that configurates and draws the rectangle.
*/
private drawDragRectangle(
d3GraphElem: d3.Selection<SVGSVGElement, any, any, any>,
background: d3.Selection<SVGSVGElement, any, any, any>,
graphExtent: D3GraphExtent
): void {
if (!this.dragStart) { return; }
this.dragCurrent = d3.mouse(background.node());
const x1 = Math.min(this.dragStart[0], this.dragCurrent[0]);
const x2 = Math.max(this.dragStart[0], this.dragCurrent[0]);
if (!this.dragRect && !this.dragRectG) {
this.dragRectG = d3GraphElem.append('g')
.style('fill-opacity', .2)
.style('fill', 'blue');
this.dragRect = this.dragRectG.append('rect')
.attr('width', x2 - x1)
.attr('height', graphExtent.height)
.attr('x', x1 + graphExtent.leftOffset)
.attr('class', 'mouse-drag')
.style('pointer-events', 'none');
} else {
this.dragRect.attr('width', x2 - x1)
.attr('x', x1 + graphExtent.leftOffset);
}
}
/**
* Function that disables the drawing rectangle control.
*/
private resetDrag(): void {
if (this.dragRectG) {
this.dragRectG.remove();
this.dragRectG = null;
this.dragRect = null;
}
}
}
./d3-graph-pan-zoom-interaction.component.scss