- add chartjs-plugin-zoom via npm
- add label to timeseries graph only for 08:00 and 18:00
This commit is contained in:
parent
4241bd2cb9
commit
dbf8aa495e
10 changed files with 2198 additions and 2258 deletions
|
@ -222,8 +222,15 @@ CREATE src/app/app-router.service.ts (138 bytes)
|
||||||
|
|
||||||
=============================== chart.js ======================================
|
=============================== chart.js ======================================
|
||||||
npm install --save chart.js
|
npm install --save chart.js
|
||||||
|
import { Chart } from 'chart.js';
|
||||||
|
|
||||||
npm install --save moment
|
npm install --save moment
|
||||||
|
|
||||||
npm install moment chartjs-adapter-moment --save
|
npm install moment chartjs-adapter-moment --save
|
||||||
import 'chartjs-adapter-moment';
|
import 'chartjs-adapter-moment';
|
||||||
|
|
||||||
|
|
||||||
|
npm install chartjs-plugin-zoom
|
||||||
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||||
|
|
||||||
|
Chart.register(zoomPlugin);
|
4242
package-lock.json
generated
4242
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -64,6 +64,7 @@
|
||||||
"bulma": "^0.9.3",
|
"bulma": "^0.9.3",
|
||||||
"chart.js": "^3.5.1",
|
"chart.js": "^3.5.1",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.0",
|
||||||
|
"chartjs-plugin-zoom": "^1.1.1",
|
||||||
"core-js": "^3.16.0",
|
"core-js": "^3.16.0",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.7.1",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
|
|
|
@ -46,16 +46,13 @@
|
||||||
<section id="app" class="hero">
|
<section id="app" class="hero">
|
||||||
|
|
||||||
|
|
||||||
<div class="main columns">
|
<div>
|
||||||
|
|
||||||
<div class="canvas-area is-8 column">
|
<router-outlet></router-outlet>
|
||||||
<!-- <div id="div-map">
|
|
||||||
|
|
||||||
<app-map [mapId]="'map'" [mapOptions]="mapOptions" [serviceUrl]="providerUrl" [baseMaps]="baseMaps"
|
</div>
|
||||||
(onSelected)="onStationSelected($event)" (onMapInitializedEvent)="onMapInitialized($event)">
|
<!-- <div class="canvas-area is-8 column">
|
||||||
</app-map>
|
|
||||||
|
|
||||||
</div> -->
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -64,25 +61,12 @@
|
||||||
<app-messages></app-messages>
|
<app-messages></app-messages>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- <app-dashboard></app-dashboard> -->
|
<!-- <app-dashboard></app-dashboard> -->
|
||||||
<!-- <span>{{ name }} app is running!</span> -->
|
<!-- <span>{{ name }} app is running!</span> -->
|
||||||
|
|
||||||
<!-- <div>
|
|
||||||
<n52-station-map-selector [mapId]="'timeseries'" [serviceUrl]="providerUrl"
|
|
||||||
[mapOptions]="mapOptions"></n52-station-map-selector>
|
|
||||||
</div>
|
|
||||||
<div>Is loading: {{loadingStations}}</div> -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <h1>{{ name }}</h1>
|
|
||||||
<nav>
|
|
||||||
<a routerLink="/dashboard">Dashboard</a>
|
|
||||||
<a routerLink="/map">Map</a>
|
|
||||||
</nav>-->
|
|
||||||
<!-- <router-outlet></router-outlet> -->
|
|
||||||
|
|
||||||
<!-- <app-messages></app-messages> -->
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { deserialize } from 'class-transformer';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { InternalIdHandler } from '../../common/components/services/internal-id-handler.service';
|
import { InternalIdHandler } from '../../common/components/services/internal-id-handler.service';
|
||||||
|
|
||||||
import moment from "moment";
|
import * as moment from "moment";
|
||||||
import { Timespan } from '../../shared/models/timespan';
|
import { Timespan } from '../../shared/models/timespan';
|
||||||
import { GeomonDataset } from '../../shared/models/dataset';
|
import { GeomonDataset } from '../../shared/models/dataset';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
<!-- <h2>Bar Chart</h2>
|
|
||||||
<canvas id="line-chart" width="800" height="450"></canvas> -->
|
<div class="main columns">
|
||||||
<geomon-timeseries-chart [datasetOptions]="datasetOptions"
|
|
||||||
[datasetIds]="datasetIds" [timeInterval]="datasetService.timespan"></geomon-timeseries-chart>
|
<div class="canvas-area is-8 column">
|
||||||
|
<!-- <h2>Bar Chart</h2>
|
||||||
|
<canvas id="line-chart" width="800" height="450"></canvas> -->
|
||||||
|
<geomon-timeseries-chart [datasetOptions]="datasetOptions"
|
||||||
|
[datasetIds]="datasetIds" [timeInterval]="datasetService.timespan"></geomon-timeseries-chart>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-area is-4 column">
|
||||||
|
Test
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -5,8 +5,20 @@
|
||||||
</n52-station-map-selector>
|
</n52-station-map-selector>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div id="div-map">
|
<div class="main columns">
|
||||||
<app-map [mapId]="'map'" [mapOptions]="mapOptions" [serviceUrl]="providerUrl" [baseMaps]="baseMaps"
|
|
||||||
(onSelected)="onStationSelected($event)" (onMapInitializedEvent)="onMapInitialized($event)" class="vbox boxItem fullHeight">
|
<div class="canvas-area is-8 column">
|
||||||
</app-map>
|
<div id="div-map">
|
||||||
|
<app-map [mapId]="'map'" [mapOptions]="mapOptions" [serviceUrl]="providerUrl" [baseMaps]="baseMaps"
|
||||||
|
(onSelected)="onStationSelected($event)" (onMapInitializedEvent)="onMapInitialized($event)"
|
||||||
|
class="vbox boxItem fullHeight">
|
||||||
|
</app-map>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-area is-4 column">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -8,11 +8,13 @@ import { DatasetService } from '../../../app/services/dataset.service';
|
||||||
|
|
||||||
import { Timespan } from '../../../shared/models/timespan';
|
import { Timespan } from '../../../shared/models/timespan';
|
||||||
import { TimeService } from '../../core/time/time.service';
|
import { TimeService } from '../../core/time/time.service';
|
||||||
import moment from 'moment';
|
import * as moment from 'moment';
|
||||||
// import 'moment-duration-format';
|
// import 'moment-duration-format';
|
||||||
import { TimeValueTuple, Data } from '../../../shared/models/dataset';
|
import { TimeValueTuple, Data } from '../../../shared/models/dataset';
|
||||||
import 'chartjs-adapter-moment';
|
import 'chartjs-adapter-moment';
|
||||||
import { MAT_SELECTION_LIST_VALUE_ACCESSOR } from '@angular/material/list';
|
|
||||||
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||||
|
Chart.register(zoomPlugin);
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'geomon-timeseries-chart',
|
selector: 'geomon-timeseries-chart',
|
||||||
|
@ -159,8 +161,57 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
|
|
||||||
const data = this.generalizeData(rawdata, this.width, this.timespan);
|
const data = this.generalizeData(rawdata, this.width, this.timespan);
|
||||||
|
|
||||||
|
|
||||||
|
let grouped_items = groupBy(rawdata.values, function (b: TimeValueTuple) {
|
||||||
|
return moment(b[0]).format('YYYY-MM-DD');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Object.keys(grouped_items).forEach(function (value: any, key: string) {
|
||||||
|
for (let [key, value] of grouped_items) {
|
||||||
|
// grouped_items[key] = findMinMax(grouped_items.get(key));
|
||||||
|
// let test = grouped_items.get(key);
|
||||||
|
let reducedValues = findMinMax(grouped_items.get(key));
|
||||||
|
let ar = new Array<TimeValueTuple>(reducedValues.min);
|
||||||
|
// ar.push(reducedValues.min, reducedValues.max);
|
||||||
|
grouped_items.set(key, ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupBy(list: Array<[number, number]>, keyGetter: any): Map<string, TimeValueTuple[]> {
|
||||||
|
const map = new Map();
|
||||||
|
list.forEach((item: any) => {
|
||||||
|
const key = keyGetter(item);
|
||||||
|
const collection = map.get(key);
|
||||||
|
if (!collection) {
|
||||||
|
map.set(key, [item]);
|
||||||
|
} else {
|
||||||
|
collection.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function findMinMax(values: TimeValueTuple[]) {
|
||||||
|
var res = { min: values[0], max: values[0] };
|
||||||
|
|
||||||
|
values.forEach(function (val: [number, number]) {
|
||||||
|
res.min = val[0] < res.min[0] ? val : res.min;
|
||||||
|
res.max = val[0] > res.max[0] ? val : res.max;
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
let values = Array.from( grouped_items.values() );
|
||||||
|
values = [].concat(...values);
|
||||||
|
let xLabels = values.map(function (label) {
|
||||||
|
let date = moment(label[0]).format("DD/MM HH:mm");
|
||||||
|
return date;
|
||||||
|
});
|
||||||
|
// console.log(values);
|
||||||
|
|
||||||
// this.datasetMap.get(dataset.internalId).data = data;
|
// this.datasetMap.get(dataset.internalId).data = data;
|
||||||
this.addData(this.lineChart, dataset, rawdata);
|
this.addData(this.lineChart, dataset, rawdata, xLabels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +232,7 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private addData(chart: Chart, dataset: GeomonTimeseries, data: GeomonTimeseriesData): void {
|
private addData(chart: Chart, dataset: GeomonTimeseries, data: GeomonTimeseriesData, xLabels: string[]): void {
|
||||||
|
|
||||||
let labels = data.values.map(function (label) {
|
let labels = data.values.map(function (label) {
|
||||||
let date = moment(label[0]).format("YYYY-MM-DD HH:mm");
|
let date = moment(label[0]).format("YYYY-MM-DD HH:mm");
|
||||||
|
@ -205,12 +256,28 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
label: dataset.label,
|
label: dataset.label,
|
||||||
// backgroundColor: 'rgba(99, 255, 132, 0.2)',
|
// backgroundColor: 'rgba(99, 255, 132, 0.2)',
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
// borderColor: 'rgba(99, 255, 132, 1)',
|
borderColor: color, //'rgba(99, 255, 132, 1)',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
data: values,
|
data: values,
|
||||||
}
|
}
|
||||||
// You add the newly created dataset to the list of `data`
|
// You add the newly created dataset to the list of `data`
|
||||||
chart.data.datasets.push(newDataset);
|
chart.data.datasets.push(newDataset);
|
||||||
|
chart.options.scales.x.ticks.callback = (val, index) => {
|
||||||
|
// // Hide the label of every 2nd dataset
|
||||||
|
// return xLabels.includes(val.toString()) ? val : null;
|
||||||
|
// return index % 2 === 0 ? (val) : '';
|
||||||
|
let valTime = moment(val, "DD/MM HH:mm").format("HH:mm");
|
||||||
|
if (valTime == "08:00" || valTime == "18:00"){
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
chart.options.scales.y.ticks.callback = (value, index, values) => {
|
||||||
|
return value + '°';
|
||||||
|
}
|
||||||
|
|
||||||
chart.update();
|
chart.update();
|
||||||
this.width = this.calculateWidth();
|
this.width = this.calculateWidth();
|
||||||
}
|
}
|
||||||
|
@ -252,20 +319,36 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
|
plugins: {
|
||||||
|
zoom: {
|
||||||
|
pan: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
wheel: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
pinch: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
mode: 'xy',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
suggestedMin: 0, // minimum will be 0, unless there is a lower value.
|
suggestedMin: 0, // minimum will be 0, unless there is a lower value.
|
||||||
// OR //
|
// OR //
|
||||||
beginAtZero: true // minimum value will be 0.
|
beginAtZero: true // minimum value will be 0.
|
||||||
},
|
},
|
||||||
|
|
||||||
x: {
|
x: {
|
||||||
type: 'time',
|
type: 'time',
|
||||||
|
|
||||||
time: {
|
time: {
|
||||||
unit: 'minute',
|
unit: 'minute',
|
||||||
displayFormats: {
|
displayFormats: {
|
||||||
minute: "DD/MM HH:mm",
|
minute: "DD/MM HH:mm",
|
||||||
hour: "DD/MM HH:mm",
|
hour: "DD/MM HH:mm",
|
||||||
day: "dd/MM",
|
day: "dd/MM",
|
||||||
week: "dd/MM",
|
week: "dd/MM",
|
||||||
|
@ -274,12 +357,21 @@ export class GeomonTimeseriesChartComponent implements AfterViewInit {
|
||||||
year: "yyyy",
|
year: "yyyy",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// ticks: {
|
|
||||||
// labelOffset: 10
|
ticks: {
|
||||||
// }
|
|
||||||
|
|
||||||
|
// callback: function(val, index) {
|
||||||
|
// // Hide the label of every 2nd dataset
|
||||||
|
// return index % 2 === 0 ? (val) : '';
|
||||||
|
// },
|
||||||
|
// autoSkip: true,
|
||||||
|
// maxRotation: 0,
|
||||||
|
// minRotation: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// options: {
|
// options: {
|
||||||
// title: {
|
// title: {
|
||||||
// display: true,
|
// display: true,
|
||||||
|
|
|
@ -132,11 +132,11 @@ export interface GeomonData { }
|
||||||
export class GeomonTimeseriesData implements GeomonData {
|
export class GeomonTimeseriesData implements GeomonData {
|
||||||
|
|
||||||
referenceValues: ReferenceValues<TimeValueTuple> = {};
|
referenceValues: ReferenceValues<TimeValueTuple> = {};
|
||||||
valueBeforeTimespan: TimeValueTuple;
|
valueBeforeTimespan: [number, number]; // TimeValueTuple;
|
||||||
valueAfterTimespan: TimeValueTuple;
|
valueAfterTimespan: [number, number]; //TimeValueTuple;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public values: TimeValueTuple[],
|
public values: Array<[number, number]>,
|
||||||
) { }
|
) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
padding-top: 51px;
|
padding-top: 65px;
|
||||||
// display: flex;
|
// display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
Loading…
Add table
Reference in a new issue