import { Component, OnInit, ViewChild } from '@angular/core'; import { PlotService, PlotType, PlotRequest, PlotResponse, TagField, FilterDefaults, DataType, 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 { YAxisRangeComponent } from '../y-axis-range/y-axis-range.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, {static: false}) private limitbycomponent : LimitByComponent; yAxisScale: string; @ViewChild(YAxisRangeComponent, {static: false}) private yAxisRangeComponent : YAxisRangeComponent; @ViewChild(QueryAutocompleteComponent, {static: false}) query: QueryAutocompleteComponent; @ViewChild(PlotViewComponent, {static: false}) plotView: PlotViewComponent; @ViewChild(GalleryViewComponent, {static: false}) galleryView: GalleryViewComponent; enableGallery = false; splitBy = null; 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); }); this.yAxisScale = "LOG10"; } 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); } dateRangeAsString() : string { return (document.getElementById("search-date-range")).value; } gallery(){ const that = this; this.plotView.imageUrl = ''; that.galleryView.show=true; const request = this.createPlotRequest(); this.galleryView.renderGallery(request, this.splitBy.name); } getAxes() : AxesTypes { var x = new Set(); var y = new Set(); for(var i = 0; i < this.selectedPlotType.length; i++){ var plotType = this.selectedPlotType[i]; x.add(plotType.xAxis); y.add(plotType.yAxis); } return new AxesTypes(x,y); } plot(){ const that = this; that.plotView.imageUrl = ''; 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+':8080/'+plotResponse.imageUrl; document.dispatchEvent(new Event("invadersPause", {})); }, error => { that.plotView.imageUrl = ''; that.showError(error.error.message); document.dispatchEvent(new Event("invadersPause", {})); }); } createPlotRequest(): PlotRequest { const aggregates = []; this.selectedPlotType.forEach(a => aggregates.push(a.id)); 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.dateRange = this.dateRangeAsString(); request.axisScale = this.yAxisScale; request.aggregates = aggregates; request.keyOutside = false; request.generateThumbnail = this.enableGallery; request.yRangeMin = this.yAxisRangeComponent.minYValue; request.yRangeMax = this.yAxisRangeComponent.maxYValue; request.yRangeUnit = this.yAxisRangeComponent.yAxisUnit; 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; }