Skip to content

Commit 2cfd4cb

Browse files
committed
feat: allow single time plots and realtime streams
1 parent ab87cf5 commit 2cfd4cb

26 files changed

+529
-71
lines changed
+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Layout, Plot } from '@npl/interfaces';
2+
3+
function normal() {
4+
let x = 0,
5+
y = 0,
6+
rds;
7+
do {
8+
x = Math.random() * 2 - 1;
9+
y = Math.random() * 2 - 1;
10+
rds = x * x + y * y;
11+
} while (rds == 0 || rds > 1);
12+
const c = Math.sqrt((-2 * Math.log(rds)) / rds); // Box-Muller transform
13+
return x * c; // throw away extra sample y * c
14+
}
15+
16+
const N = 2000;
17+
const a = -1;
18+
const b = 1.2;
19+
20+
const step = (b - a) / (N - 1);
21+
const t = new Array(N),
22+
x = new Array(N),
23+
y = new Array(N);
24+
25+
for (let i = 0; i < N; i++) {
26+
t[i] = a + step * i;
27+
x[i] = Math.pow(t[i], 3) + 0.3 * normal();
28+
y[i] = Math.pow(t[i], 6) + 0.3 * normal();
29+
}
30+
31+
const trace1: Plot = {
32+
x: x,
33+
y: y,
34+
mode: 'markers',
35+
name: 'points',
36+
marker: {
37+
color: 'rgb(102,0,0)',
38+
size: 2,
39+
opacity: 0.4,
40+
},
41+
type: 'scatter',
42+
};
43+
const trace2: Plot = {
44+
x: x,
45+
y: y,
46+
name: 'density',
47+
ncontours: 20,
48+
colorscale: 'Hot',
49+
reversescale: true,
50+
showscale: false,
51+
type: 'histogram2dcontour',
52+
} as Plot;
53+
const trace3: Plot = {
54+
x: x,
55+
name: 'x density',
56+
marker: { color: 'rgb(102,0,0)' },
57+
yaxis: 'y2',
58+
type: 'histogram',
59+
};
60+
const trace4: Plot = {
61+
y: y,
62+
name: 'y density',
63+
marker: { color: 'rgb(102,0,0)' },
64+
xaxis: 'x2',
65+
type: 'histogram',
66+
};
67+
export const data = [trace1, trace2, trace3, trace4];
68+
export const layout: Layout = {
69+
showlegend: false,
70+
autosize: false,
71+
width: 600,
72+
height: 550,
73+
margin: { t: 50 },
74+
hovermode: 'closest',
75+
bargap: 0,
76+
xaxis: {
77+
domain: [0, 0.85],
78+
showgrid: false,
79+
zeroline: false,
80+
},
81+
yaxis: {
82+
domain: [0, 0.85],
83+
showgrid: false,
84+
zeroline: false,
85+
},
86+
xaxis2: {
87+
domain: [0.85, 1],
88+
showgrid: false,
89+
zeroline: false,
90+
},
91+
yaxis2: {
92+
domain: [0.85, 1],
93+
showgrid: false,
94+
zeroline: false,
95+
},
96+
};

apps/dev-server/src/data/bar.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Plot } from '@npl/interfaces';
2+
3+
export const data: Plot[] = [
4+
{
5+
type: 'bar',
6+
x: [20, 14, 23],
7+
y: ['giraffes', 'orangutans', 'monkeys'],
8+
orientation: 'h',
9+
},
10+
];
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { Plot } from '@npl/interfaces';
2+
3+
const trace1 = {
4+
x: [
5+
'2017-01-04',
6+
'2017-01-05',
7+
'2017-01-06',
8+
'2017-01-09',
9+
'2017-01-10',
10+
'2017-01-11',
11+
'2017-01-12',
12+
'2017-01-13',
13+
'2017-01-17',
14+
'2017-01-18',
15+
'2017-01-19',
16+
'2017-01-20',
17+
'2017-01-23',
18+
'2017-01-24',
19+
'2017-01-25',
20+
'2017-01-26',
21+
'2017-01-27',
22+
'2017-01-30',
23+
'2017-01-31',
24+
'2017-02-01',
25+
'2017-02-02',
26+
'2017-02-03',
27+
'2017-02-06',
28+
'2017-02-07',
29+
'2017-02-08',
30+
'2017-02-09',
31+
'2017-02-10',
32+
'2017-02-13',
33+
'2017-02-14',
34+
'2017-02-15',
35+
],
36+
close: [
37+
116.019997, 116.610001, 117.910004, 118.989998, 119.110001, 119.75, 119.25,
38+
119.040001, 120, 119.989998, 119.779999, 120, 120.080002, 119.970001,
39+
121.879997, 121.940002, 121.949997, 121.629997, 121.349998, 128.75,
40+
128.529999, 129.080002, 130.289993, 131.529999, 132.039993, 132.419998,
41+
132.119995, 133.289993, 135.020004, 135.509995,
42+
],
43+
decreasing: { line: { color: '#7F7F7F' } },
44+
high: [
45+
116.510002, 116.860001, 118.160004, 119.43, 119.379997, 119.93, 119.300003,
46+
119.620003, 120.239998, 120.5, 120.089996, 120.449997, 120.809998,
47+
120.099998, 122.099998, 122.440002, 122.349998, 121.629997, 121.389999,
48+
130.490005, 129.389999, 129.190002, 130.5, 132.089996, 132.220001,
49+
132.449997, 132.940002, 133.820007, 135.089996, 136.270004,
50+
],
51+
increasing: { line: { color: '#17BECF' } },
52+
line: { color: 'rgba(31,119,180,1)' },
53+
low: [
54+
115.75, 115.809998, 116.470001, 117.940002, 118.300003, 118.599998,
55+
118.209999, 118.809998, 118.220001, 119.709999, 119.370003, 119.730003,
56+
119.769997, 119.5, 120.279999, 121.599998, 121.599998, 120.660004,
57+
120.620003, 127.010002, 127.779999, 128.160004, 128.899994, 130.449997,
58+
131.220001, 131.119995, 132.050003, 132.75, 133.25, 134.619995,
59+
],
60+
open: [
61+
115.849998, 115.919998, 116.779999, 117.949997, 118.769997, 118.739998,
62+
118.900002, 119.110001, 118.339996, 120, 119.400002, 120.449997, 120,
63+
119.550003, 120.419998, 121.669998, 122.139999, 120.93, 121.150002,
64+
127.029999, 127.980003, 128.309998, 129.130005, 130.539993, 131.350006,
65+
131.649994, 132.460007, 133.080002, 133.470001, 135.520004,
66+
],
67+
type: 'candlestick',
68+
xaxis: 'x',
69+
yaxis: 'y',
70+
} as Plot;
71+
72+
export const data = [trace1];
73+
74+
export const layout = {
75+
dragmode: 'zoom',
76+
margin: {
77+
r: 10,
78+
t: 25,
79+
b: 40,
80+
l: 60,
81+
},
82+
showlegend: false,
83+
xaxis: {
84+
autorange: true,
85+
domain: [0, 1],
86+
range: ['2017-01-03 12:00', '2017-02-15 12:00'],
87+
rangeslider: { range: ['2017-01-03 12:00', '2017-02-15 12:00'] },
88+
title: 'Date',
89+
type: 'date',
90+
},
91+
yaxis: {
92+
autorange: true,
93+
domain: [0, 1],
94+
range: [114.609999778, 137.410004222],
95+
type: 'linear',
96+
},
97+
};
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Plot } from '@npl/interfaces';
2+
import { interval, Observable } from 'rxjs';
3+
import { map } from 'rxjs/operators';
4+
5+
export const stream$: Observable<Plot[]> = interval(100).pipe(
6+
map((index) => {
7+
const data: Plot = {
8+
x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
9+
y: Array(10)
10+
.fill(0)
11+
.map((_, i) => Math.sin(index + i)),
12+
type: 'scatter',
13+
};
14+
return [data];
15+
})
16+
);

apps/dev-server/src/data/sankey.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Plot } from '@npl/interfaces';
2+
3+
export const data = [
4+
{
5+
type: 'sankey',
6+
orientation: 'h',
7+
node: {
8+
pad: 15,
9+
thickness: 30,
10+
line: {
11+
color: 'black',
12+
width: 0.5,
13+
},
14+
label: ['A1', 'A2', 'B1', 'B2', 'C1', 'C2'],
15+
color: ['blue', 'blue', 'blue', 'blue', 'blue', 'blue'],
16+
},
17+
18+
link: {
19+
source: [0, 1, 0, 2, 3, 3],
20+
target: [2, 3, 3, 4, 4, 5],
21+
value: [8, 4, 2, 8, 4, 2],
22+
},
23+
} as Plot,
24+
];
25+
26+
export const layout = {
27+
title: 'Basic Sankey',
28+
font: {
29+
size: 10,
30+
},
31+
};

apps/dev-server/src/data/scatter.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Plot } from '@npl/interfaces';
2+
3+
const trace1: Plot = {
4+
x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
5+
y: [1, 2, 1, 2, 1, 2, 1, 2, 1, 2],
6+
type: 'scatter',
7+
};
8+
export const data = [trace1];
9+
export const layout = {};

apps/dev-server/src/data/table.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Plot } from '@npl/interfaces';
2+
3+
const values = [
4+
['Salaries', 'Office', 'Merchandise', 'Legal', '<b>TOTAL</b>'],
5+
[1200000, 20000, 80000, 2000, 12120000],
6+
[1300000, 20000, 70000, 2000, 130902000],
7+
[1300000, 20000, 120000, 2000, 131222000],
8+
[1400000, 20000, 90000, 2000, 14102000],
9+
];
10+
11+
const headerColor = 'grey';
12+
const rowEvenColor = 'lightgrey';
13+
const rowOddColor = 'white';
14+
15+
export const data = [
16+
{
17+
type: 'table',
18+
header: {
19+
values: [
20+
['<b>EXPENSES</b>'],
21+
['<b>Q1</b>'],
22+
['<b>Q2</b>'],
23+
['<b>Q3</b>'],
24+
['<b>Q4</b>'],
25+
],
26+
align: 'center',
27+
line: { width: 1, color: 'black' },
28+
fill: { color: headerColor },
29+
font: { family: 'Arial', size: 12, color: 'white' },
30+
},
31+
cells: {
32+
values: values,
33+
align: 'center',
34+
line: { color: 'black', width: 1 },
35+
fill: {
36+
color: [
37+
[rowOddColor, rowEvenColor, rowOddColor, rowEvenColor, rowOddColor],
38+
],
39+
},
40+
font: { family: 'Arial', size: 11, color: ['black'] },
41+
},
42+
} as Plot,
43+
];

apps/dev-server/src/main.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
* This is only a minimal backend to get started.
44
*/
55

6-
import { bootstrap, plot } from '@npl/core';
6+
import { plot } from '@npl/core';
7+
import {
8+
data as histogram,
9+
layout as histogramLayout,
10+
} from './data/2d-histogram';
11+
import { data as bar } from './data/bar';
12+
import { stream$ } from './data/line-stream';
13+
import { data as sankey, layout as sankeyLayout } from './data/sankey';
14+
import { data as scatter, layout as scatterPlotLayout } from './data/scatter';
15+
import { data as table } from './data/table';
716

8-
plot([]);
17+
plot(stream$);
18+
plot(scatter, scatterPlotLayout);
19+
plot(bar);
20+
plot(sankey, sankeyLayout);
21+
plot(histogram, histogramLayout);
22+
plot(table);

apps/web/src/app/app.module.ts

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { PlotComponent } from './components/plot/plot.component';
1515
import { PlotsComponent } from './components/plots/plots.component';
1616
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io';
1717
import { SocketService } from './services/socket.service';
18+
import { DragDropModule } from '@angular/cdk/drag-drop';
19+
import { MatCardModule } from '@angular/material/card';
1820

1921
const config: SocketIoConfig = {
2022
url: '',
@@ -39,6 +41,8 @@ const config: SocketIoConfig = {
3941
MatSidenavModule,
4042
MatListModule,
4143
SocketIoModule.forRoot(config),
44+
DragDropModule,
45+
MatCardModule,
4246
],
4347
providers: [PlotsService, SocketService],
4448
bootstrap: [AppComponent],

apps/web/src/app/components/plot/plot.component.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
Component,
55
ElementRef,
66
Input,
7+
OnChanges,
8+
SimpleChanges,
79
ViewChild,
810
} from '@angular/core';
911
import { PlotData } from '@npl/interfaces';
@@ -17,7 +19,7 @@ declare const Plotly: any;
1719
styleUrls: ['./plot.component.css'],
1820
changeDetection: ChangeDetectionStrategy.OnPush,
1921
})
20-
export class PlotComponent implements AfterViewInit {
22+
export class PlotComponent implements AfterViewInit, OnChanges {
2123
@Input() plotData!: PlotData;
2224
@ViewChild('plotContainer', { static: false }) plotContainer!: ElementRef;
2325

@@ -29,4 +31,15 @@ export class PlotComponent implements AfterViewInit {
2931
{ responsive: true }
3032
);
3133
}
34+
35+
ngOnChanges(simpleChanges: SimpleChanges) {
36+
if (simpleChanges.plotData) {
37+
Plotly.react(
38+
this.plotContainer.nativeElement,
39+
this.plotData.data,
40+
{ ...(this.plotData.layout ?? {}), autosize: true },
41+
{ responsive: true }
42+
);
43+
}
44+
}
3245
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
:host {
2+
display: grid;
3+
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
4+
grid-gap: 8px;
5+
margin: 8px;
6+
grid-template-rows: auto;
7+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
<p>plots works!</p>
1+
<mat-card *ngFor="let plot of plots$ | async; trackBy: trackById">
2+
<npl-plot [plotData]="plot"></npl-plot>
3+
</mat-card>

0 commit comments

Comments
 (0)