import { Component, OnInit, ViewChild } from '@angular/core'; import { PlotService, PlotType, PlotRequest, PlotResponse, TagField, FilterDefaults, DataType, YAxisDefinition, AxesTypes } from '../plot.service'; import { Observable } from 'rxjs/Observable'; import { FormControl, Validators } from '@angular/forms'; import { MatSnackBar } from '@angular/material/snack-bar'; import { LimitByComponent } from '../limit-by/limit-by.component'; import { YAxisDefinitionComponent } from '../y-axis-definition/y-axis-definition.component'; import { QueryAutocompleteComponent } from '../query-autocomplete/query-autocomplete.component'; import { PlotViewComponent, SelectionRange, DateAnchor } from '../plot-view/plot-view.component'; import { GalleryViewComponent } from '../gallery-view/gallery-view.component'; import * as moment from 'moment'; @Component({ selector: 'pdb-visualization-page', templateUrl: './visualization-page.component.html', styleUrls: ['./visualization-page.component.scss'] }) export class VisualizationPageComponent implements OnInit { readonly DATE_PATTERN = "YYYY-MM-DD HH:mm:ss"; // for moment-JS dateRange = new FormControl('2019-10-05 00:00:00 - 2019-10-11 23:59:59'); selectedPlotType = []; plotTypes: Array; tagFields: Array = new Array(); groupBy = new Array(); @ViewChild('limitbycomponent') private limitbycomponent : LimitByComponent; @ViewChild('y1AxisDefinitionComponent', { read: YAxisDefinitionComponent }) private y1AxisDefinitionComponent : YAxisDefinitionComponent; @ViewChild('y2AxisDefinitionComponent', { read: YAxisDefinitionComponent }) private y2AxisDefinitionComponent : YAxisDefinitionComponent; @ViewChild('query') query: QueryAutocompleteComponent; @ViewChild('plotView') plotView: PlotViewComponent; @ViewChild('galleryView') galleryView: GalleryViewComponent; enableGallery = false; splitBy = null; y2AxisAvailable = false; intervalUnit = 'NO_INTERVAL'; intervalValue = 1; renderBarChartTickLabels = false; constructor(private plotService: PlotService, private snackBar: MatSnackBar) { } showError(message) { this.snackBar.open(message, "", { duration: 5000, verticalPosition: 'top' }); } ngOnInit() { const that = this; this.plotTypes = this.plotService.getPlotTypes(); this.selectedPlotType.push(this.plotTypes[0]); that.plotService.getFilterDefaults().subscribe(function(filterDefaults) { filterDefaults.fields.forEach(function(name) { that.tagFields.push(new TagField(name)); }, error => { that.showError(error.error.message); }); that.groupBy = that.tagFields.filter(val => filterDefaults.groupBy.includes(val.name)); that.splitBy = that.tagFields.find(val => filterDefaults.splitBy == val.name); }); } changePlotType(selectedPlotTypes: Array) { const compatiblePlotTypes = this.plotTypes.filter(pt => pt.compatible(selectedPlotTypes)); this.plotTypes.forEach(pt => pt.active=false); compatiblePlotTypes.forEach(pt => pt.active=true); const axesTypes = this.getAxes(); this.y2AxisAvailable = axesTypes.y.length == 2; } selectedPlotTypesContains(plotTypeId: string){ return this.selectedPlotType.filter(pt => pt.id == plotTypeId).length > 0; } dateRangeAsString() : string { return (document.getElementById("search-date-range")).value; } gallery(){ const that = this; this.plotView.imageUrl = ''; that.plotView.stats = null; that.galleryView.show=true; const request = this.createPlotRequest(); this.galleryView.renderGallery(request, this.splitBy.name); } getAxes() : AxesTypes { var x = new Array(); var y = new Array(); for(var i = 0; i < this.selectedPlotType.length; i++){ var plotType = this.selectedPlotType[i]; if (!x.includes(plotType.xAxis)) { x.push(plotType.xAxis); } if (!y.includes(plotType.yAxis)) { y.push(plotType.yAxis); } } return new AxesTypes(x,y); } plot(){ const that = this; that.plotView.imageUrl = ''; that.plotView.stats = null; this.plotView.axes = this.getAxes(); console.log(JSON.stringify(this.getAxes())); that.galleryView.show=false; document.dispatchEvent(new Event("invadersStart", {})); const request = this.createPlotRequest(); this.plotService.sendPlotRequest(request).subscribe(function(plotResponse){ console.log("response: " + JSON.stringify(plotResponse)); that.plotView.imageUrl = "http://"+window.location.hostname+':'+window.location.port+'/'+plotResponse.imageUrl; that.plotView.stats = plotResponse.stats; document.dispatchEvent(new Event("invadersPause", {})); }, error => { that.plotView.imageUrl = ''; that.plotView.stats = null; that.showError(error.error.message); document.dispatchEvent(new Event("invadersPause", {})); }); } createPlotRequest(): PlotRequest { const aggregates = []; this.selectedPlotType.forEach(a => aggregates.push(a.id)); const y1 = this.y1AxisDefinitionComponent.getAxisDefinition(); const y2 = this.y2AxisDefinitionComponent ? this.y2AxisDefinitionComponent.getAxisDefinition() : undefined; const request = new PlotRequest(); request.query = this.query.query; request.height = document.getElementById("results").offsetHeight-1; request.width = document.getElementById("results").offsetWidth-1; request.groupBy = this.groupBy.map(o => o.name); request.limitBy = this.limitbycomponent.limitBy; request.limit = this.limitbycomponent.limit; request.y1 = y1; request.y2 = y2; request.dateRange = this.dateRangeAsString(); request.aggregates = aggregates; request.keyOutside = false; request.generateThumbnail = this.enableGallery; request.intervalUnit = this.intervalUnit; request.intervalValue = this.intervalValue; request.renderBarChartTickLabels = this.renderBarChartTickLabels; return request; } /** * Zoom in/out by zoomFaktor, so that the anchorInPercentOfDateRange keeps the same position. * * shiftDateByAnchor(dateRangeAsString, 0.20, 0.5) zooms in by 50%, so that the date that was at 20% before the zoom is still at 20% after the zoom * shiftDateByAnchor(dateRangeAsString, 0.33, 2) zooms out by 50%, so that the date that was at 33% before the zoom is still at 33% after the zoom */ shiftDateByAnchor(dateRange:string, anchorInPercentOfDateRange:number, zoomFactor:number) { const dateRangeParsed = this.parseDateRange(dateRange); const dateRangeInSeconds = dateRangeParsed.duration.asSeconds(); const anchorTimestampInSeconds = dateRangeParsed.startDate.clone().add(Math.floor(dateRangeInSeconds*anchorInPercentOfDateRange), "seconds"); const newDateRangeInSeconds = dateRangeInSeconds * zoomFactor; const newStartDate = anchorTimestampInSeconds.clone().subtract(newDateRangeInSeconds*anchorInPercentOfDateRange, "seconds"); const newEndDate = newStartDate.clone().add({seconds: newDateRangeInSeconds});; this.setDateRange(newStartDate, newEndDate); } /** * Zoom in/out or shift date by adding factorStartDate*dateRangeInSeconds seconds to the start date * and factorEndDate*dateRangeInSeconds seconds to the end date. * * shiftDate(dateRangeAsString, 0.25, -0.25) will zoom in, making the range half its size * shiftDate(dateRangeAsString, -0.5, 0.5) will zoom out, making the range double its size * shiftDate(dateRangeAsString, -0.5, -0.5) will move the range by half its size to older values * shiftDate(dateRangeAsString, 1, 1) will move the range by its size to newer values */ shiftDate(dateRange: string, factorStartDate: number, factorEndDate: number) { const dateRangeParsed = this.parseDateRange(dateRange); const dateRangeInSeconds = dateRangeParsed.duration.asSeconds(); const newStartDate = dateRangeParsed.startDate.add({seconds: dateRangeInSeconds*factorStartDate}); const newEndDate = dateRangeParsed.endDate.add({seconds: dateRangeInSeconds*factorEndDate}); this.setDateRange(newStartDate, newEndDate); } parseDateRange(dateRangeAsString : string) : DateRange { if (dateRangeAsString) { const startDate = moment(dateRangeAsString.slice(0, 19)); const endDate = moment(dateRangeAsString.slice(22, 41)); return { startDate: startDate, endDate: endDate, duration: moment.duration(endDate.diff(startDate)) }; } } setDateRange(startDate: any, endDate: any) { const formattedStartDate = startDate.format(this.DATE_PATTERN); const formattedEndDate = endDate.format(this.DATE_PATTERN); const newDateRange = formattedStartDate+" - "+formattedEndDate; (document.getElementById("search-date-range")).value = newDateRange; this.plot(); } zoomRange(range: SelectionRange) { this.shiftDate(this.dateRangeAsString(), range.startPercentOfDateRange, range.endPercentOfDateRange-1); } zoomWithDateAnchor(dateAnchor: DateAnchor){ this.shiftDateByAnchor(this.dateRangeAsString(), dateAnchor.cursorPercentOfDateRange, dateAnchor.zoomFactor); } } export class DateRange { startDate: any; endDate: any; duration: any; } export class AxesUsed { x1: DataType; y1: DataType; x2: DataType; y2: DataType; }