libs/plotly/src/lib/plotly-profile-graph/plotly-profile-graph.component.ts
selector | n52-plotly-profile-graph |
styleUrls | ./plotly-profile-graph.component.scss |
templateUrl | ./plotly-profile-graph.component.html |
constructor(iterableDiffers: IterableDiffers, servicesConnector: HelgolandServicesConnector, datasetIdResolver: InternalIdHandler, timeSrvc: Time, translateSrvc: TranslateService, timezoneSrvc: TimezoneService)
|
|||||||||||||||||||||
Parameters :
|
datasetIds | |
Type : string[]
|
|
Default value : []
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:38
|
|
List of presented dataset ids. |
datasetOptions | |
Type : Map<string | T>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:56
|
|
The corresponding dataset options. |
presenterOptions | |
Type : U
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:63
|
|
Options for general presentation of the data. |
reloadForDatasets | |
Type : string[]
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:70
|
|
List of datasets for which a reload should be triggered, when the Array is set to new value. |
selectedDatasetIds | |
Type : string[]
|
|
Default value : []
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:44
|
|
List of presented selected dataset ids. |
timeInterval | |
Type : TimeInterval
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:50
|
|
The time interval in which the data should presented. |
onHighlight | |
Type : EventEmitter<PresenterHighlight>
|
|
dataLoaded | |
Type : EventEmitter<Set<string>>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:100
|
|
Event, which triggers list of datasets where data is currently loaded. |
onContentLoading | |
Type : EventEmitter<boolean>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:94
|
|
Event flag, while there is data loaded in the component. |
onDatasetSelected | |
Type : EventEmitter<string[]>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:76
|
|
Event with a list of selected datasets. |
onMessageThrown | |
Type : EventEmitter<PresenterMessage>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:88
|
|
Event, when there occured a message in the component. |
onTimespanChanged | |
Type : EventEmitter<Timespan>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:82
|
|
Event when the timespan in the presentation is adjusted. |
window:resize |
Arguments : '$event'
|
window:resize(event: Event)
|
Inherited from
ResizableComponent
|
Defined in
ResizableComponent:6
|
Protected addDataset |
addDataset(id: string, url: string)
|
Returns :
void
|
Private clearData |
clearData()
|
Returns :
void
|
Private clearLayout |
clearLayout()
|
Returns :
void
|
Private createXAxis | |||||||||
createXAxis(dataset: HelgolandProfile, data: ProfileDataEntry)
|
|||||||||
Parameters :
Returns :
string
|
Private createYAxis | |||||||||
createYAxis(dataset: HelgolandProfile, data: ProfileDataEntry)
|
|||||||||
Parameters :
Returns :
string
|
Protected datasetOptionsChanged | ||||||||||||
datasetOptionsChanged(internalId: string, options: TimedDatasetOptions[], firstChange: boolean)
|
||||||||||||
Parameters :
Returns :
void
|
Private drawChart |
drawChart()
|
Returns :
void
|
Public ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
Protected onLanguageChanged | ||||||
onLanguageChanged(langChangeEvent: LangChangeEvent)
|
||||||
Parameters :
Returns :
void
|
Protected onResize |
onResize()
|
Returns :
void
|
Protected onTimezoneChanged | ||||||
onTimezoneChanged(timezone: string)
|
||||||
Parameters :
Returns :
void
|
Protected presenterOptionsChanged | ||||||
presenterOptionsChanged(options: any)
|
||||||
Parameters :
Returns :
void
|
Private processData |
processData()
|
Returns :
void
|
Private redrawChart |
redrawChart()
|
Returns :
void
|
Public reloadDataForDatasets | ||||||
reloadDataForDatasets(datasetIds: string[])
|
||||||
Parameters :
Returns :
void
|
Protected removeDataset | ||||||
removeDataset(internalId: string)
|
||||||
Parameters :
Returns :
void
|
Protected removeSelectedId | ||||||
removeSelectedId(internalId: string)
|
||||||
Parameters :
Returns :
void
|
Protected setSelectedId | ||||||
setSelectedId(internalId: string)
|
||||||
Parameters :
Returns :
void
|
Protected timeIntervalChanges |
timeIntervalChanges()
|
Returns :
void
|
Private updateAxis |
updateAxis()
|
Returns :
void
|
Protected Abstract addDataset |
addDataset(id: string, url: string)
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:191
|
Returns :
void
|
Protected addDatasetByInternalId | ||||||
addDatasetByInternalId(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:180
|
||||||
Parameters :
Returns :
void
|
Protected Abstract datasetOptionsChanged |
datasetOptionsChanged(internalId: string, options: T, firstChange: boolean)
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:201
|
Returns :
void
|
Public ngDoCheck |
ngDoCheck()
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:139
|
Returns :
void
|
Public ngOnChanges | ||||||
ngOnChanges(changes: SimpleChanges)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:124
|
||||||
Parameters :
Returns :
void
|
Public ngOnDestroy |
ngOnDestroy()
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:134
|
Returns :
void
|
Protected Abstract onLanguageChanged | ||||||
onLanguageChanged(langChangeEvent: LangChangeEvent)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:185
|
||||||
Parameters :
Returns :
void
|
Protected Abstract onTimezoneChanged | ||||||
onTimezoneChanged(timezone: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:187
|
||||||
Parameters :
Returns :
void
|
Protected Abstract presenterOptionsChanged | ||||||
presenterOptionsChanged(options: U)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:199
|
||||||
Parameters :
Returns :
void
|
Public Abstract reloadDataForDatasets | ||||||
reloadDataForDatasets(datasets: string[])
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:178
|
||||||
Parameters :
Returns :
void
|
Protected Abstract removeDataset | ||||||
removeDataset(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:193
|
||||||
Parameters :
Returns :
void
|
Protected Abstract removeSelectedId | ||||||
removeSelectedId(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:197
|
||||||
Parameters :
Returns :
void
|
Protected Abstract setSelectedId | ||||||
setSelectedId(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:195
|
||||||
Parameters :
Returns :
void
|
Protected Abstract timeIntervalChanges |
timeIntervalChanges()
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:189
|
Returns :
void
|
Protected Abstract onResize |
onResize()
|
Inherited from
ResizableComponent
|
Defined in
ResizableComponent:10
|
Returns :
void
|
Private counterXAxis |
Type : number
|
Default value : 0
|
Private counterYAxis |
Type : number
|
Default value : 0
|
Private layout |
Type : Layout
|
Default value : {
autosize: true,
showlegend: false,
dragmode: 'pan',
margin: {
l: 40,
r: 10,
b: 40,
t: 10
// pad: 100
},
hovermode: 'closest'
}
|
Private plotlyArea |
Type : any
|
Public plotlyElem |
Type : ElementRef
|
Decorators :
@ViewChild('plotly', {static: true})
|
Private preparedData |
Type : ExtendedScatterData[]
|
Default value : []
|
Private rawData |
Type : Map<string | RawData>
|
Default value : new Map()
|
Private datasetIdsDiffer |
Type : IterableDiffer<string>
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:104
|
Private langChangeSubscription |
Type : Subscription
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:106
|
Protected oldDatasetOptions |
Type : Map<string | T>
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:57
|
Protected oldPresenterOptions |
Type : U
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:64
|
Private selectedDatasetIdsDiffer |
Type : IterableDiffer<string>
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:105
|
Protected timespan |
Type : Timespan
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:102
|
Private timezoneSubscription |
Type : Subscription
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:107
|
import { AfterViewInit, Component, ElementRef, EventEmitter, IterableDiffers, Output, ViewChild } from '@angular/core';
import {
DatasetPresenterComponent,
DatasetType,
HelgolandProfile,
HelgolandServicesConnector,
InternalIdHandler,
PresenterHighlight,
ProfileDataEntry,
Time,
TimedDatasetOptions,
Timespan,
TimezoneService,
} from '@helgoland/core';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import * as d3 from 'd3';
import * as Plotly from 'plotly.js';
interface RawData {
dataset: HelgolandProfile;
datas: ProfileDataEntry[];
options: TimedDatasetOptions[];
}
interface ExtendedScatterData extends Partial<Plotly.ScatterData> {
timestamp: number;
id: string;
}
const LINE_WIDTH_SELECTED = 5;
const LINE_WIDTH = 2;
const MARKER_SIZE_SELECTED = 10;
const MARKER_SIZE = 6;
@Component({
selector: 'n52-plotly-profile-graph',
templateUrl: './plotly-profile-graph.component.html',
styleUrls: ['./plotly-profile-graph.component.scss']
})
export class PlotlyProfileGraphComponent
extends DatasetPresenterComponent<TimedDatasetOptions[], any>
implements AfterViewInit {
@Output()
public onHighlight: EventEmitter<PresenterHighlight> = new EventEmitter();
@ViewChild('plotly', { static: true })
public plotlyElem: ElementRef;
private plotlyArea: any;
private preparedData: ExtendedScatterData[] = [];
private rawData: Map<string, RawData> = new Map();
private counterXAxis = 0;
private counterYAxis = 0;
private layout: Layout = {
autosize: true,
showlegend: false,
dragmode: 'pan',
margin: {
l: 40,
r: 10,
b: 40,
t: 10
// pad: 100
},
hovermode: 'closest'
};
private settings: Partial<any> = {
displayModeBar: false,
modeBarButtonsToRemove: [
'sendDataToCloud',
'hoverCompareCartesian'
],
displaylogo: false,
showTips: false,
scrollZoom: true
};
constructor(
protected iterableDiffers: IterableDiffers,
protected servicesConnector: HelgolandServicesConnector,
protected datasetIdResolver: InternalIdHandler,
protected timeSrvc: Time,
protected translateSrvc: TranslateService,
protected timezoneSrvc: TimezoneService
) {
super(iterableDiffers, servicesConnector, datasetIdResolver, timeSrvc, translateSrvc, timezoneSrvc);
}
public ngAfterViewInit(): void {
this.plotlyArea = this.plotlyElem.nativeElement;
this.drawChart();
}
protected onLanguageChanged(langChangeEvent: LangChangeEvent): void { }
protected onTimezoneChanged(timezone: string): void { }
public reloadDataForDatasets(datasetIds: string[]): void {
console.log('reload data at ' + new Date());
}
// tslint:disable-next-line:no-empty
protected timeIntervalChanges(): void { }
protected addDataset(id: string, url: string): void {
this.servicesConnector.getDataset({ id, url }, { type: DatasetType.Profile }).subscribe(dataset => {
const options = this.datasetOptions.get(dataset.internalId);
options.forEach((option) => {
if (option.timestamp) {
const timespan = new Timespan(option.timestamp);
this.servicesConnector.getDatasetData(dataset, timespan).subscribe(data => {
if (data.values.length === 1) {
if (this.rawData.has(dataset.internalId)) {
this.rawData.get(dataset.internalId).datas.push(data.values[0]);
this.rawData.get(dataset.internalId).options.push(option);
} else {
this.rawData.set(dataset.internalId, {
dataset,
datas: [data.values[0]],
options: [option]
});
}
}
this.drawChart();
});
}
});
});
}
protected removeDataset(internalId: string): void {
this.rawData.delete(internalId);
this.drawChart();
}
protected setSelectedId(internalId: string): void {
this.drawChart();
}
protected removeSelectedId(internalId: string): void {
this.drawChart();
}
// tslint:disable-next-line:no-empty
protected presenterOptionsChanged(options: any): void { }
protected datasetOptionsChanged(internalId: string, options: TimedDatasetOptions[], firstChange: boolean): void {
if (!firstChange) {
// remove unused options
const removedIdx = this.rawData.get(internalId).options.findIndex((option) => {
const idx = options.findIndex((e) => e.timestamp === option.timestamp);
if (idx === -1) {
return true;
}
});
if (removedIdx > -1) {
this.rawData.get(internalId).options.splice(removedIdx, 1);
this.rawData.get(internalId).datas.splice(removedIdx, 1);
}
this.drawChart();
}
}
protected onResize(): void {
this.redrawChart();
}
private processData() {
this.clearLayout();
this.clearData();
this.rawData.forEach((dataEntry) => {
dataEntry.options.forEach((option, key) => {
if (option.visible) {
const x = new Array<number>();
const y = new Array<number>();
const selected = this.selectedDatasetIds.indexOf(dataEntry.dataset.internalId) >= 0;
dataEntry.datas[key].value.forEach((entry) => {
x.push(entry.value);
y.push(entry.vertical);
});
const prepared: ExtendedScatterData = {
x,
y,
type: 'scatter',
name: '',
timestamp: option.timestamp,
id: dataEntry.dataset.internalId,
yaxis: this.createYAxis(dataEntry.dataset, dataEntry.datas[key]),
xaxis: this.createXAxis(dataEntry.dataset, dataEntry.datas[key]),
// hovertext: dataEntry.label,
line: {
color: option.color,
width: selected ? LINE_WIDTH_SELECTED : LINE_WIDTH
},
marker: {
size: selected ? MARKER_SIZE_SELECTED : MARKER_SIZE
}
};
this.preparedData.push(prepared);
}
});
});
this.updateAxis();
}
private createXAxis(dataset: HelgolandProfile, data: ProfileDataEntry): string {
let axis;
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && key.startsWith('xaxis') && this.layout[key].title === dataset.uom) {
axis = this.layout[key];
}
}
const range = d3.extent(data.value, (d) => d.value);
if (!axis) {
this.counterXAxis = this.counterXAxis + 1;
axis = this.layout['xaxis' + this.counterXAxis] = {
id: 'x' + (this.counterXAxis > 1 ? this.counterXAxis : ''),
anchor: 'free',
title: dataset.uom,
zeroline: true,
hoverformat: '.2f',
showline: false,
range: [range[0], range[1]],
overlaying: '',
// rangemode: 'tozero',
fixedrange: false
};
if (this.counterXAxis !== 1) {
axis.overlaying = 'x';
}
} else {
axis.range = d3.extent([range[0], range[1], axis.range[0], axis.range[1]]);
}
return axis.id;
}
private createYAxis(dataset: HelgolandProfile, data: ProfileDataEntry): string {
let axis;
// find axis
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) &&
key.startsWith('yaxis') &&
this.layout[key].title === data.verticalUnit) {
axis = this.layout[key];
}
}
if (!axis) {
// add axis
this.counterYAxis = this.counterYAxis + 1;
axis = this.layout[('yaxis' + this.counterYAxis)] = {
id: 'y' + (this.counterYAxis > 1 ? this.counterYAxis : ''),
// zeroline: true,
anchor: 'free',
hoverformat: '.2r',
side: 'left',
autorange: 'reversed',
showline: false,
overlaying: '',
title: data.verticalUnit,
fixedrange: false
};
if (this.counterYAxis !== 1) {
axis.overlaying = 'y';
}
}
return axis.id;
}
private updateAxis() {
if (this.counterYAxis > 1) {
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && key.startsWith('xaxis')) {
this.layout[key].domain = [(0.1 * this.counterYAxis) - 0.1, 1];
}
}
let yaxisCount = 0;
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && key.startsWith('yaxis')) {
this.layout[key].position = 0.1 * yaxisCount;
yaxisCount += 1;
}
}
}
if (this.counterXAxis > 1) {
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && key.startsWith('yaxis')) {
this.layout[key].domain = [(0.06 * this.counterXAxis) - 0.06, 1];
}
}
let xaxisCount = 0;
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && key.startsWith('xaxis')) {
this.layout[key].position = 0.06 * xaxisCount;
xaxisCount += 1;
}
}
}
// add offset to xaxis ranges
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && key.startsWith('xaxis')) {
const range = this.layout[key].range;
const rangeOffset = (range[1] - range[0]) * 0.05;
this.layout[key].range = [range[0] - rangeOffset, range[1] + rangeOffset];
}
}
}
private drawChart() {
if (this.plotlyArea && this.rawData.size > 0) {
this.processData();
Plotly.newPlot(this.plotlyArea, this.preparedData, this.layout, this.settings);
this.plotlyArea.on('plotly_hover', (entry: any) => {
if (entry.points.length === 1) {
this.onHighlight.emit({
internalId: entry.points[0].data.id,
dataIndex: entry.points[0].pointNumber
});
}
});
}
}
private clearLayout() {
// todo remove yaxis
for (const key in this.layout) {
if (this.layout.hasOwnProperty(key) && (key.startsWith('yaxis') || key.startsWith('xaxis'))) {
delete this.layout[key];
}
}
// reset counter
this.counterYAxis = 0;
this.counterXAxis = 0;
}
private clearData() {
this.preparedData = [];
}
private redrawChart() {
if (this.plotlyArea) {
Plotly.relayout(this.plotlyArea, {});
}
}
}
interface ScatterData extends Partial<any> {
id: string;
timestamp: number;
}
interface Layout extends Partial<any> {
[key: string]: any;
}
<div #plotly></div>
./plotly-profile-graph.component.scss
:host {
div {
width: 100%;
height: 100%;
}
}