- npm updates
- add legend-entry-component files - add DatasetOptions class
This commit is contained in:
parent
dbf8aa495e
commit
91cd763da0
15 changed files with 1043 additions and 534 deletions
980
package-lock.json
generated
980
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -29,6 +29,7 @@ import { DiagramViewComponent } from './views/diagram-view/diagram-view.componen
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { DatasetByStationSelectorComponent } from './components/dataset-by-station-selector/dataset-by-station-selector.component';
|
import { DatasetByStationSelectorComponent } from './components/dataset-by-station-selector/dataset-by-station-selector.component';
|
||||||
|
import { LegendEntryComponent } from './components/legend-entry/legend-entry.component';
|
||||||
// import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material';
|
// import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material';
|
||||||
import { MatListModule } from '@angular/material/list';
|
import { MatListModule } from '@angular/material/list';
|
||||||
import {MatBadgeModule} from '@angular/material/badge';
|
import {MatBadgeModule} from '@angular/material/badge';
|
||||||
|
@ -38,7 +39,7 @@ import { InternalIdHandler } from '../common/components/services/internal-id-han
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
// declarations: The components, directives, and pipes that belong to this NgModule.
|
// declarations: The components, directives, and pipes that belong to this NgModule.
|
||||||
declarations: [AppComponent, MapComponent, DashboardComponent, MessagesComponent, MapViewComponent, DiagramViewComponent, DatasetByStationSelectorComponent],
|
declarations: [AppComponent, MapComponent, DashboardComponent, MessagesComponent, MapViewComponent, DiagramViewComponent, DatasetByStationSelectorComponent, LegendEntryComponent],
|
||||||
// entryComponents: [
|
// entryComponents: [
|
||||||
// DatasetByStationSelectorComponent
|
// DatasetByStationSelectorComponent
|
||||||
// ],
|
// ],
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { MatSelectionListChange } from '@angular/material/list';
|
||||||
|
|
||||||
import { DatasetService } from '../../services/dataset.service';
|
import { DatasetService } from '../../services/dataset.service';
|
||||||
import { AppRouterService } from './../../services/app-router.service';
|
import { AppRouterService } from './../../services/app-router.service';
|
||||||
|
import { DatasetOptions } from './../../../shared/models/options';
|
||||||
|
|
||||||
// https://material.angular.io/components/dialog/overview
|
// https://material.angular.io/components/dialog/overview
|
||||||
// https://blog.angular-university.io/angular-material-dialog/
|
// https://blog.angular-university.io/angular-material-dialog/
|
||||||
|
@ -51,7 +52,7 @@ export class DatasetByStationSelectorComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
protected datasetApiService: DatasetApiService,
|
protected datasetApiService: DatasetApiService,
|
||||||
private dialogRef: MatDialogRef<DatasetByStationSelectorComponent>,
|
private dialogRef: MatDialogRef<DatasetByStationSelectorComponent>,
|
||||||
public datasetService : DatasetService<GeomonTimeseries>,
|
public datasetService : DatasetService<DatasetOptions>,
|
||||||
public appRouter: AppRouterService,
|
public appRouter: AppRouterService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ export class DatasetByStationSelectorComponent implements OnInit {
|
||||||
|
|
||||||
const id = (change.option.value as SelectableDataset).internalId;
|
const id = (change.option.value as SelectableDataset).internalId;
|
||||||
if (change.option.selected) {
|
if (change.option.selected) {
|
||||||
this.datasetService.addDataset(id, change.option.value as GeomonTimeseries);
|
this.datasetService.addDataset(id, change.option.value as DatasetOptions);
|
||||||
} else {
|
} else {
|
||||||
this.datasetService.removeDataset(id);
|
this.datasetService.removeDataset(id);
|
||||||
}
|
}
|
||||||
|
|
42
src/app/components/legend-entry/legend-entry.component.html
Normal file
42
src/app/components/legend-entry/legend-entry.component.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
<div id="geomon-legend-entry" class="legendItem" style="position: relative;" [ngStyle]="{'border-color': datasetOption?.color}"
|
||||||
|
[ngClass]="{'selected': selected}" (click)="toggleVisibility(); $event.stopPropagation();">
|
||||||
|
<!-- <div class="loading-overlay" *ngIf="loading" [ngStyle]="{'background-color': datasetOption?.color}">
|
||||||
|
<div class="fa fa-refresh fa-spin fa-3x fa-fw"></div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="legendItemheader" [ngClass]="{'highlight': highlight}">
|
||||||
|
|
||||||
|
<div class="legendItemLabel" [ngStyle]="{'color': datasetOption?.color}">
|
||||||
|
<label>Plattform: {{platformLabel}} </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="noDataWarning firstLastEntry" *ngIf="!hasData">
|
||||||
|
<div>
|
||||||
|
<span class="fa fa-exclamation-triangle red"></span>
|
||||||
|
<span class="small-label">Keine Daten verfügbar</span>
|
||||||
|
</div>
|
||||||
|
<div class="additionalLegendEntry">
|
||||||
|
<span class="fa fa-chevron-right"></span>
|
||||||
|
<span class="small-label">Springe zur letzten Messung</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="small-label">
|
||||||
|
<label> {{phenomenonLabel}} </label>
|
||||||
|
<span *ngIf="uom">
|
||||||
|
<span>[</span>
|
||||||
|
<label>{{uom}}</label>
|
||||||
|
<span>]</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="small-label">
|
||||||
|
<label>Sensor: {{procedureLabel}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="small-label" *ngIf="categoryLabel != phenomenonLabel">
|
||||||
|
<label>{{categoryLabel}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
74
src/app/components/legend-entry/legend-entry.component.scss
Normal file
74
src/app/components/legend-entry/legend-entry.component.scss
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#geomon-legend-entry.legendItem {
|
||||||
|
background-color: white;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
.small-label {
|
||||||
|
font-size: 90%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
padding: 0px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
.legendItemheader {
|
||||||
|
cursor: pointer;
|
||||||
|
&.highlight {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.legendicons {
|
||||||
|
span {
|
||||||
|
margin: 0 4%;
|
||||||
|
font-size: 150%;
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.delete {
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.noDataWarning {
|
||||||
|
border: red solid 2px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 3px;
|
||||||
|
.red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.additionalLegendEntry {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.refEntry.selected {
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
.loading-overlay {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.5;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
.fa-spin {
|
||||||
|
$icon-size: 25px;
|
||||||
|
color: white;
|
||||||
|
font-size: $icon-size;
|
||||||
|
width: $icon-size;
|
||||||
|
height: $icon-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
src/app/components/legend-entry/legend-entry.component.ts
Normal file
141
src/app/components/legend-entry/legend-entry.component.ts
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
import { GeomonTimeseries, DatasetFilter, DatasetType } from '../../../shared/models/dataset';
|
||||||
|
import { TimeInterval } from '../../../shared/models/timespan';
|
||||||
|
import { DatasetApiService } from '../../services/dataset-api.service';
|
||||||
|
import { InternalIdHandler, InternalDatasetId } from '../../../common/components/services/internal-id-handler.service';
|
||||||
|
import { DatasetOptions } from '../../../shared/models/options';
|
||||||
|
@Component({
|
||||||
|
selector: 'geomon-legend-entry',
|
||||||
|
templateUrl: './legend-entry.component.html',
|
||||||
|
styleUrls: ['./legend-entry.component.scss']
|
||||||
|
})
|
||||||
|
export class LegendEntryComponent {
|
||||||
|
@Input()
|
||||||
|
public timeInterval: TimeInterval;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public onSelectDate: EventEmitter<Date> = new EventEmitter();
|
||||||
|
|
||||||
|
// public firstValue: FirstLastValue;
|
||||||
|
// public lastValue: FirstLastValue;
|
||||||
|
public hasData = true;
|
||||||
|
|
||||||
|
public informationVisible = false;
|
||||||
|
// public referenceValues: ReferenceValue[];
|
||||||
|
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public datasetOption: DatasetOptions;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public highlight: boolean;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public onUpdateOptions: EventEmitter<DatasetOptions> = new EventEmitter();
|
||||||
|
|
||||||
|
// @Output()
|
||||||
|
// public onEditOptions: EventEmitter<DatasetOptions> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public onShowGeometry: EventEmitter<GeoJSON.GeoJsonObject> = new EventEmitter();
|
||||||
|
|
||||||
|
public dataset: GeomonTimeseries;
|
||||||
|
public platformLabel: string;
|
||||||
|
public phenomenonLabel: string;
|
||||||
|
public procedureLabel: string;
|
||||||
|
public categoryLabel: string;
|
||||||
|
public uom: string;
|
||||||
|
public error: any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public datasetId: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public selected: boolean;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public onDeleteDataset: EventEmitter<boolean> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public onSelectDataset: EventEmitter<boolean> = new EventEmitter();
|
||||||
|
|
||||||
|
public loading: boolean;
|
||||||
|
protected internalId: InternalDatasetId;
|
||||||
|
// private langChangeSubscription: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private internalIdHandler: InternalIdHandler,
|
||||||
|
private datasetApiService: DatasetApiService) { }
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
if (this.datasetId) {
|
||||||
|
this.internalId = this.internalIdHandler.resolveInternalId(this.datasetId);
|
||||||
|
this.loadDataset();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected loadDataset(locale?: string): void {
|
||||||
|
const params: DatasetFilter = {};
|
||||||
|
if (locale) { params.locale = locale; }
|
||||||
|
this.loading = true;
|
||||||
|
// this.servicesConnector.getDataset(this.internalId, { ...params, type: DatasetType.Timeseries })
|
||||||
|
// .subscribe(
|
||||||
|
// dataset => this.setDataset(dataset),
|
||||||
|
// error => this.handleError(error)
|
||||||
|
// );
|
||||||
|
this.datasetApiService.getDataset(this.internalId.id, this.internalId.url, { type: 'timeseries' }).subscribe({
|
||||||
|
next: (res: GeomonTimeseries) => this.setDataset(res),
|
||||||
|
error: (err: any) => this.handleError(err),
|
||||||
|
complete: () => console.log('HTTP request completed.')
|
||||||
|
// error => this.errorHandler.handleDatasetLoadError(error)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleError(error: any) {
|
||||||
|
this.loading = false;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeDataset() {
|
||||||
|
this.onDeleteDataset.emit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setDataset(timeseries: GeomonTimeseries) {
|
||||||
|
this.dataset = timeseries;
|
||||||
|
this.setParameters();
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setParameters() {
|
||||||
|
this.platformLabel = this.dataset.parameters.platform.label;
|
||||||
|
this.phenomenonLabel = this.dataset.parameters.phenomenon.label;
|
||||||
|
this.procedureLabel = this.dataset.parameters.procedure.label;
|
||||||
|
this.categoryLabel = this.dataset.parameters.category.label;
|
||||||
|
this.uom = this.dataset.uom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleSelection() {
|
||||||
|
this.selected = !this.selected;
|
||||||
|
this.onSelectDataset.emit(this.selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleVisibility() {
|
||||||
|
this.datasetOption.visible = !this.datasetOption.visible;
|
||||||
|
this.onUpdateOptions.emit(this.datasetOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
public editDatasetOptions() {
|
||||||
|
// this.onEditOptions.emit(this.datasetOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showGeometry() {
|
||||||
|
this.onShowGeometry.emit(this.dataset.platform.geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,12 +9,13 @@ const TIME_CACHE_PARAM = 'timeseriesTime';
|
||||||
// https://github.com/52North/helgoland-toolbox/blob/fe6af1b9df0e5d78eeec236e4690aeb7dc92119b/apps/helgoland/src/app/services/timeseries-service.service.ts#L22
|
// https://github.com/52North/helgoland-toolbox/blob/fe6af1b9df0e5d78eeec236e4690aeb7dc92119b/apps/helgoland/src/app/services/timeseries-service.service.ts#L22
|
||||||
|
|
||||||
import { TimeService } from '../../common/core/time/time.service';
|
import { TimeService } from '../../common/core/time/time.service';
|
||||||
|
import { DatasetOptions } from '../../shared/models/options';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DatasetService<T> {
|
export class DatasetService<T extends DatasetOptions> {
|
||||||
|
|
||||||
public datasetIds: string[] = [];
|
public datasetIds: string[] = [];
|
||||||
public datasetService: Map<string, T> = new Map();
|
public datasetOptions: Map<string, T> = new Map();
|
||||||
|
|
||||||
private _timespan: Timespan;
|
private _timespan: Timespan;
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ export class DatasetService<T> {
|
||||||
// options.forEach((e) => temp.push(e));
|
// options.forEach((e) => temp.push(e));
|
||||||
// // this.saveState();
|
// // this.saveState();
|
||||||
// }
|
// }
|
||||||
this.datasetService.set(internalId, options);
|
this.datasetOptions.set(internalId, options);
|
||||||
|
|
||||||
this.datasetIdsChanged.emit(this.datasetIds);
|
this.datasetIdsChanged.emit(this.datasetIds);
|
||||||
return true;
|
return true;
|
||||||
|
@ -65,7 +66,7 @@ export class DatasetService<T> {
|
||||||
|
|
||||||
public removeAllDatasets() {
|
public removeAllDatasets() {
|
||||||
this.datasetIds.length = 0;
|
this.datasetIds.length = 0;
|
||||||
this.datasetService.clear();
|
this.datasetOptions.clear();
|
||||||
this.datasetIdsChanged.emit(this.datasetIds);
|
this.datasetIdsChanged.emit(this.datasetIds);
|
||||||
// this.saveState();
|
// this.saveState();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,7 @@ export class DatasetService<T> {
|
||||||
const datasetIdx = this.datasetIds.indexOf(internalId);
|
const datasetIdx = this.datasetIds.indexOf(internalId);
|
||||||
if (datasetIdx > -1) {
|
if (datasetIdx > -1) {
|
||||||
this.datasetIds.splice(datasetIdx, 1);
|
this.datasetIds.splice(datasetIdx, 1);
|
||||||
this.datasetService.delete(internalId);
|
this.datasetOptions.delete(internalId);
|
||||||
}
|
}
|
||||||
this.datasetIdsChanged.emit(this.datasetIds);
|
this.datasetIdsChanged.emit(this.datasetIds);
|
||||||
// this.saveState();
|
// this.saveState();
|
||||||
|
@ -86,11 +87,11 @@ export class DatasetService<T> {
|
||||||
|
|
||||||
public hasDataset(id: string): boolean {
|
public hasDataset(id: string): boolean {
|
||||||
// return this.datasetIds.indexOf(id) >= 0;
|
// return this.datasetIds.indexOf(id) >= 0;
|
||||||
return this.datasetService.has(id);
|
return this.datasetOptions.has(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateDatasetOptions(options: T, internalId: string) {
|
public updateDatasetOptions(options: T, internalId: string) {
|
||||||
this.datasetService.set(internalId, options);
|
this.datasetOptions.set(internalId, options);
|
||||||
// this.saveState();
|
// this.saveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
|
<div class="main columns">
|
||||||
<div class="main columns">
|
|
||||||
|
|
||||||
<div class="canvas-area is-8 column">
|
<div class="canvas-area is-8 column">
|
||||||
<!-- <h2>Bar Chart</h2>
|
<!-- <h2>Bar Chart</h2>
|
||||||
<canvas id="line-chart" width="800" height="450"></canvas> -->
|
<canvas id="line-chart" width="800" height="450"></canvas> -->
|
||||||
<geomon-timeseries-chart [datasetOptions]="datasetOptions"
|
<geomon-timeseries-chart [datasetOptions]="datasetOptions" [datasetIds]="datasetIds"
|
||||||
[datasetIds]="datasetIds" [timeInterval]="datasetService.timespan"></geomon-timeseries-chart>
|
[timeInterval]="datasetService.timespan" [selectedDatasetIds]="selectedIds"></geomon-timeseries-chart>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-area is-4 column">
|
<div class="input-area is-4 column">
|
||||||
Test
|
<div *ngFor="let id of datasetIds">
|
||||||
|
<geomon-legend-entry [datasetId]="id" [datasetOption]="datasetOptions.get(id)" (onSelectDataset)="onSelectDataset($event, id)" (onUpdateOptions)="onUpdateOptions($event, id)">
|
||||||
|
</geomon-legend-entry>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -3,7 +3,8 @@ import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { DatasetService } from '../../services/dataset.service';
|
import { DatasetService } from '../../services/dataset.service';
|
||||||
import { AppRouterService } from '../../services/app-router.service';
|
import { AppRouterService } from '../../services/app-router.service';
|
||||||
import { GeomonTimeseries, GeomonDataset } from '../../../shared/models/dataset';
|
// import { GeomonTimeseries, GeomonDataset } from '../../../shared/models/dataset';
|
||||||
|
import { DatasetOptions } from '../../../shared/models/options';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bar',
|
selector: 'app-bar',
|
||||||
|
@ -16,7 +17,7 @@ export class DiagramViewComponent implements OnInit {
|
||||||
|
|
||||||
public selectedIds: string[] = [];
|
public selectedIds: string[] = [];
|
||||||
|
|
||||||
public datasetOptions: Map<string, GeomonTimeseries> = new Map();
|
public datasetOptions: Map<string, DatasetOptions> = new Map();
|
||||||
|
|
||||||
public diagramLoading: boolean;
|
public diagramLoading: boolean;
|
||||||
public overviewLoading: boolean;
|
public overviewLoading: boolean;
|
||||||
|
@ -29,7 +30,7 @@ export class DiagramViewComponent implements OnInit {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public datasetService: DatasetService<GeomonTimeseries>,
|
public datasetService: DatasetService<DatasetOptions>,
|
||||||
public appRouter: AppRouterService,) { }
|
public appRouter: AppRouterService,) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -41,11 +42,7 @@ export class DiagramViewComponent implements OnInit {
|
||||||
|
|
||||||
private setDatasets() {
|
private setDatasets() {
|
||||||
this.datasetIds = this.datasetService.datasetIds;
|
this.datasetIds = this.datasetService.datasetIds;
|
||||||
this.datasetOptions = this.datasetService.datasetService;
|
this.datasetOptions = this.datasetService.datasetOptions;
|
||||||
}
|
|
||||||
|
|
||||||
public clearSelection() {
|
|
||||||
this.selectedIds = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeAllTimeseries() {
|
public removeAllTimeseries() {
|
||||||
|
@ -56,6 +53,21 @@ export class DiagramViewComponent implements OnInit {
|
||||||
this.datasetService.removeDataset(internalId);
|
this.datasetService.removeDataset(internalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSelectDataset(selected: boolean, internalId: string) {
|
||||||
|
if (selected) {
|
||||||
|
this.selectedIds.push(internalId);
|
||||||
|
} else {
|
||||||
|
this.selectedIds.splice(this.selectedIds.findIndex(entry => entry === internalId), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearSelection() {
|
||||||
|
this.selectedIds = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public onUpdateOptions(options: DatasetOptions, internalId: string) {
|
||||||
|
this.datasetService.updateDatasetOptions(options, internalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ import { IDataset } from '../../../shared/models/dataset';
|
||||||
|
|
||||||
const INTERNAL_ID_SEPERATOR = '_';
|
const INTERNAL_ID_SEPERATOR = '_';
|
||||||
|
|
||||||
// export interface InternalDatasetId {
|
export interface InternalDatasetId {
|
||||||
// id: string;
|
id: string;
|
||||||
// url: string;
|
url: string;
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to generate or resolve internal dataset IDs
|
* Service to generate or resolve internal dataset IDs
|
||||||
|
@ -31,4 +31,33 @@ export class InternalIdHandler {
|
||||||
return url + INTERNAL_ID_SEPERATOR + id;
|
return url + INTERNAL_ID_SEPERATOR + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the internal ID to the url and the API specific dataset id.
|
||||||
|
* @param internalId The internal id as string
|
||||||
|
* @returns Construct of url and API id
|
||||||
|
*/
|
||||||
|
public resolveInternalId(internalId: string | InternalDatasetId): InternalDatasetId {
|
||||||
|
if (typeof (internalId) === 'string') {
|
||||||
|
if (internalId.indexOf(INTERNAL_ID_SEPERATOR) > 0) {
|
||||||
|
const url = internalId.substring(0, internalId.indexOf(INTERNAL_ID_SEPERATOR));
|
||||||
|
const id = internalId.substring(internalId.indexOf(INTERNAL_ID_SEPERATOR) + INTERNAL_ID_SEPERATOR.length);
|
||||||
|
return { url, id };
|
||||||
|
} else {
|
||||||
|
console.error('InternalID ' + internalId + ' is not resolvable');
|
||||||
|
return {url: "", id: ""};
|
||||||
|
}
|
||||||
|
} else if (this.instanceOfInternalDatasetId(internalId)) {
|
||||||
|
return internalId;
|
||||||
|
} else {
|
||||||
|
return {url: "", id: ""};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private instanceOfInternalDatasetId(object: any): object is InternalDatasetId {
|
||||||
|
return 'id' in object && 'url' in object;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
<div class="d3" #geomon_timeseries>
|
<div class="chart-container">
|
||||||
<canvas id="line-chart" width="100%" height="80%"></canvas>
|
<canvas #chart id="line-chart"></canvas>
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,20 @@
|
||||||
|
// :host {
|
||||||
|
// position: relative;
|
||||||
|
// margin: auto;
|
||||||
|
// width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
margin: 5px auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#line-chart {
|
||||||
|
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
import { Input, Component, AfterViewInit, ViewChild, ElementRef, SimpleChanges } from '@angular/core';
|
import { Input, Component, AfterViewInit, ViewChild, ElementRef, SimpleChanges, DoCheck, IterableDiffer} from '@angular/core';
|
||||||
// import * as d3 from 'd3';
|
// import * as d3 from 'd3';
|
||||||
import { Chart, LogarithmicScale, registerables } from 'chart.js';
|
import { Chart, registerables } from 'chart.js';
|
||||||
|
|
||||||
import { GeomonTimeseries, DataConst, GeomonTimeseriesData } from '../../../shared/models/dataset';
|
import { GeomonTimeseries, DataConst, GeomonTimeseriesData } from '../../../shared/models/dataset';
|
||||||
import { DatasetApiService } from '../../../app/services/dataset-api.service';
|
import { DatasetApiService } from '../../../app/services/dataset-api.service';
|
||||||
import { DatasetService } from '../../../app/services/dataset.service';
|
import { DatasetService } from '../../../app/services/dataset.service';
|
||||||
|
|
||||||
import { Timespan } from '../../../shared/models/timespan';
|
import { Timespan } from '../../../shared/models/timespan';
|
||||||
|
import { DatasetOptions } from '../../../shared/models/options';
|
||||||
import { TimeService } from '../../core/time/time.service';
|
import { TimeService } from '../../core/time/time.service';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
// import 'moment-duration-format';
|
// import 'moment-duration-format';
|
||||||
|
@ -14,27 +15,36 @@ import { TimeValueTuple, Data } from '../../../shared/models/dataset';
|
||||||
import 'chartjs-adapter-moment';
|
import 'chartjs-adapter-moment';
|
||||||
|
|
||||||
import zoomPlugin from 'chartjs-plugin-zoom';
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||||
|
|
||||||
Chart.register(zoomPlugin);
|
Chart.register(zoomPlugin);
|
||||||
|
import { InternalIdHandler, InternalDatasetId } from '../../../common/components/services/internal-id-handler.service';
|
||||||
|
|
||||||
|
// interface Color {
|
||||||
|
// borderColor: string,
|
||||||
|
// pointBackgroundColor: string
|
||||||
|
// }
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'geomon-timeseries-chart',
|
selector: 'geomon-timeseries-chart',
|
||||||
templateUrl: './geomon-timeseries-chart.component.html',
|
templateUrl: './geomon-timeseries-chart.component.html',
|
||||||
styleUrls: ['./geomon-timeseries-chart.component.scss']
|
styleUrls: ['./geomon-timeseries-chart.component.scss']
|
||||||
})
|
})
|
||||||
export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
export class GeomonTimeseriesChartComponent implements AfterViewInit, DoCheck {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @ViewChild('geomon_timeseries', { static: true })
|
// @ViewChild('geomon_timeseries', { static: true })
|
||||||
// public chartElem: ElementRef;
|
// public chartElem: ElementRef;
|
||||||
|
|
||||||
|
|
||||||
@ViewChild('geomon_timeseries') public chartElem: ElementRef;
|
@ViewChild('chart') private chartElem: ElementRef;
|
||||||
|
|
||||||
lineChart: Chart;
|
private lineChart: Chart;
|
||||||
/**
|
/**
|
||||||
* The corresponding dataset options.
|
* The corresponding dataset options.
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public datasetOptions: Map<string, GeomonTimeseries>;
|
public datasetOptions: Map<string, DatasetOptions>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of presented dataset ids.
|
* List of presented dataset ids.
|
||||||
|
@ -42,6 +52,13 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
@Input()
|
@Input()
|
||||||
public datasetIds: string[] = [];
|
public datasetIds: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of presented selected dataset ids.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public selectedDatasetIds: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time interval in which the data should presented.
|
* The time interval in which the data should presented.
|
||||||
|
@ -66,12 +83,39 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
left: 10
|
left: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// private datasetIdsDiffer: IterableDiffer<string>;
|
||||||
|
private selectedDatasetIdsDiffer: IterableDiffer<string>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
protected datasetIdResolver: InternalIdHandler,
|
||||||
protected datasetApiService: DatasetApiService,
|
protected datasetApiService: DatasetApiService,
|
||||||
protected timeService: TimeService,
|
protected timeService: TimeService,
|
||||||
public datasetService: DatasetService<GeomonTimeseries>,
|
public datasetService: DatasetService<DatasetOptions>,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
public ngDoCheck(): void {
|
||||||
|
// const selectedDatasetIdsChanges = this.selectedDatasetIdsDiffer.diff(this.selectedDatasetIds);
|
||||||
|
// if (selectedDatasetIdsChanges) {
|
||||||
|
// selectedDatasetIdsChanges.forEachAddedItem((addedItem) => {
|
||||||
|
// this.setSelectedId(addedItem.item);
|
||||||
|
// });
|
||||||
|
// selectedDatasetIdsChanges.forEachRemovedItem((removedItem) => {
|
||||||
|
// this.removeSelectedId(removedItem.item);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setSelectedId(internalId: string): void {
|
||||||
|
// const internalEntry = this.preparedData.find((e) => e.internalId === internalId);
|
||||||
|
// if (internalEntry) { internalEntry.selected = true; }
|
||||||
|
// this.redrawCompleteGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected removeSelectedId(internalId: string): void {
|
||||||
|
// const internalEntry = this.preparedData.find((e) => e.internalId === internalId);
|
||||||
|
// if (internalEntry) { internalEntry.selected = false; }
|
||||||
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void { // this.createSvg();
|
ngAfterViewInit(): void { // this.createSvg();
|
||||||
|
|
||||||
this.canvas = document.getElementById("line-chart") as HTMLCanvasElement;
|
this.canvas = document.getElementById("line-chart") as HTMLCanvasElement;
|
||||||
|
@ -83,8 +127,9 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
|
|
||||||
for (let i = 0; i < this.datasetIds.length; i++) {
|
for (let i = 0; i < this.datasetIds.length; i++) {
|
||||||
let datasetId = this.datasetIds[i];
|
let datasetId = this.datasetIds[i];
|
||||||
let dataset = this.datasetOptions.get(datasetId);
|
let datasetOptions = this.datasetOptions.get(datasetId);
|
||||||
this.addDataset(dataset.id, 'https://geomon.geologie.ac.at/52n-sos-webapp/api/');
|
// this.internalId = this.internalIdHandler.resolveInternalId(datasetId);
|
||||||
|
this.addDatasetByInternalId(datasetOptions.internalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// let firstDatasetId = this.datasetIds[0];
|
// let firstDatasetId = this.datasetIds[0];
|
||||||
|
@ -108,11 +153,17 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
return this.datasetMap.get(internalId);
|
return this.datasetMap.get(internalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected addDatasetByInternalId(internalId: string) {
|
||||||
|
const internalIdObj = this.datasetIdResolver.resolveInternalId(internalId);
|
||||||
|
this.addDataset(internalIdObj.id, internalIdObj.url);
|
||||||
|
}
|
||||||
|
|
||||||
protected addDataset(id: string, url: string): void {
|
protected addDataset(id: string, url: string): void {
|
||||||
// this.servicesConnector.getDataset({ id, url }, { locale: this.translateService.currentLang, type: DatasetType.Timeseries }).subscribe(
|
// this.servicesConnector.getDataset({ id, url }, { locale: this.translateService.currentLang, type: DatasetType.Timeseries }).subscribe(
|
||||||
// res => this.loadAddedDataset(res),
|
// res => this.loadAddedDataset(res),
|
||||||
// error => this.errorHandler.handleDatasetLoadError(error)
|
// error => this.errorHandler.handleDatasetLoadError(error)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
this.datasetApiService.getDataset(id, url).subscribe({
|
this.datasetApiService.getDataset(id, url).subscribe({
|
||||||
next: (res: GeomonTimeseries) => this.loadAddedDataset(res),
|
next: (res: GeomonTimeseries) => this.loadAddedDataset(res),
|
||||||
error: (err: any) => console.error('Observer got an error: ' + err),
|
error: (err: any) => console.error('Observer got an error: ' + err),
|
||||||
|
@ -252,6 +303,8 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
for (let i = 0; i < 6; i++) {
|
for (let i = 0; i < 6; i++) {
|
||||||
color += letters[Math.floor(Math.random() * 16)];
|
color += letters[Math.floor(Math.random() * 16)];
|
||||||
}
|
}
|
||||||
|
let datasetOptions = this.datasetOptions.get(dataset.internalId);
|
||||||
|
datasetOptions.color = color;
|
||||||
var newDataset = {
|
var newDataset = {
|
||||||
label: dataset.label,
|
label: dataset.label,
|
||||||
// backgroundColor: 'rgba(99, 255, 132, 0.2)',
|
// backgroundColor: 'rgba(99, 255, 132, 0.2)',
|
||||||
|
@ -284,7 +337,7 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
|
|
||||||
private initChart(): void {
|
private initChart(): void {
|
||||||
|
|
||||||
this.lineChart = new Chart(this.canvas, {
|
this.lineChart = new Chart(this.chartElem.nativeElement, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
// labels: [1500, 1600, 1700, 1750, 1800, 1850, 1900, 1950, 1999, 2050],
|
// labels: [1500, 1600, 1700, 1750, 1800, 1850, 1900, 1950, 1999, 2050],
|
||||||
|
@ -319,6 +372,8 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
zoom: {
|
zoom: {
|
||||||
pan: {
|
pan: {
|
||||||
|
@ -379,7 +434,8 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
this.width = this.calculateWidth() - 20; // add buffer to the left to garantee visualization of last date (tick x-axis)
|
|
||||||
|
// this.width = this.calculateWidth() - 20; // add buffer to the left to garantee visualization of last date (tick x-axis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { GeomonPlatform } from "./platform";
|
import { GeomonPlatform } from "./platform";
|
||||||
|
import { InternalIdHandler } from '../../common/components/services/internal-id-handler.service';
|
||||||
|
|
||||||
const INTERNAL_ID_SEPERATOR = '_';
|
const INTERNAL_ID_SEPERATOR = '_';
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ export class GeomonDataset {
|
||||||
public url: string,
|
public url: string,
|
||||||
public label: string
|
public label: string
|
||||||
) {
|
) {
|
||||||
// this.internalId = new InternalIdHandler().createInternalId(url, id);
|
this.internalId = new InternalIdHandler().createInternalId(url, id);
|
||||||
}
|
}
|
||||||
// public get internalId(): string {
|
// public get internalId(): string {
|
||||||
// return this.url + INTERNAL_ID_SEPERATOR + this.id;
|
// return this.url + INTERNAL_ID_SEPERATOR + this.id;
|
||||||
|
@ -69,6 +70,7 @@ export class ParameterConstellation {
|
||||||
public procedure: Parameter;
|
public procedure: Parameter;
|
||||||
public phenomenon: Parameter;
|
public phenomenon: Parameter;
|
||||||
public category: Parameter;
|
public category: Parameter;
|
||||||
|
public platform: Parameter;
|
||||||
}
|
}
|
||||||
interface Parameter {
|
interface Parameter {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -100,6 +102,23 @@ export interface DataConst extends GeomonTimeseries {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface DatasetFilter {
|
||||||
|
phenomenon?: string;
|
||||||
|
category?: string;
|
||||||
|
procedure?: string;
|
||||||
|
feature?: string;
|
||||||
|
offering?: string;
|
||||||
|
service?: string;
|
||||||
|
expanded?: boolean;
|
||||||
|
locale?: string;
|
||||||
|
type?: DatasetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DatasetType {
|
||||||
|
Timeseries = 'timeseries',
|
||||||
|
Trajectory = 'trajectory',
|
||||||
|
Profile = 'profile'
|
||||||
|
}
|
||||||
|
|
||||||
// export class TimeseriesData {
|
// export class TimeseriesData {
|
||||||
// public id: string;
|
// public id: string;
|
||||||
|
|
111
src/shared/models/options.ts
Normal file
111
src/shared/models/options.ts
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
export class DatasetOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal dataset id
|
||||||
|
*/
|
||||||
|
public internalId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* type to display the data
|
||||||
|
* default is 'line'
|
||||||
|
*/
|
||||||
|
public type: 'line' | 'bar' = 'line';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* color of the dataset
|
||||||
|
*/
|
||||||
|
public color: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show or hide in the graph
|
||||||
|
*/
|
||||||
|
public visible: boolean = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* separate y axis of datasets with same unit
|
||||||
|
*/
|
||||||
|
public separateYAxis?: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* align graph that zero y axis is visible
|
||||||
|
*/
|
||||||
|
public zeroBasedYAxis?: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* auto zoom when range selection
|
||||||
|
*/
|
||||||
|
public autoRangeSelection?: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* marker to request dataset data generalized
|
||||||
|
*/
|
||||||
|
public generalize?: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list of visible reference values
|
||||||
|
*/
|
||||||
|
public showReferenceValues: ReferenceValueOption[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* radius of graphpoint
|
||||||
|
* default is 0
|
||||||
|
*/
|
||||||
|
public pointRadius: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the start of, where to start with the bar chart
|
||||||
|
* See also: https://momentjs.com/docs/#/manipulating/start-of/
|
||||||
|
* default is 'hour'
|
||||||
|
*/
|
||||||
|
public barStartOf: string = 'hour';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* width of graphline
|
||||||
|
*/
|
||||||
|
public lineWidth: number = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dasharray to structure the line or bar chart border
|
||||||
|
* See also here: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
|
||||||
|
*/
|
||||||
|
public lineDashArray: number | number[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* color of the point border
|
||||||
|
*/
|
||||||
|
public pointBorderColor: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* width of the point border
|
||||||
|
*/
|
||||||
|
public pointBorderWidth: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* min and max range of y axis
|
||||||
|
*/
|
||||||
|
public yAxisRange?: MinMaxRange;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
internalId: string,
|
||||||
|
color: string
|
||||||
|
) {
|
||||||
|
this.internalId = internalId;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReferenceValueOption {
|
||||||
|
id: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* numbered range with a min and a max value
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export interface MinMaxRange {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue