@uwrl/qc-utils 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +189 -186
- package/dist/index.umd.cjs +3 -3
- package/dist/types.d.ts +51 -0
- package/dist/utils/__tests__/ellapsedTime.spec.d.ts +1 -0
- package/dist/utils/__tests__/format.spec.d.ts +1 -0
- package/dist/utils/__tests__/notifications.spec.d.ts +1 -0
- package/dist/utils/__tests__/observationsUtils.spec.d.ts +1 -0
- package/dist/utils/ellapsedTime.d.ts +0 -0
- package/dist/utils/format.d.ts +0 -0
- package/dist/utils/notifications.d.ts +46 -0
- package/dist/utils/observationsUtils.d.ts +0 -0
- package/dist/utils/plotting/__tests__/delete-data.worker.spec.d.ts +1 -0
- package/dist/utils/plotting/__tests__/mock.d.ts +4 -0
- package/dist/utils/plotting/__tests__/observationRecord.spec.d.ts +1 -0
- package/dist/utils/plotting/add-data.worker.d.ts +0 -0
- package/dist/utils/plotting/delete-data.worker.d.ts +0 -0
- package/dist/utils/plotting/drift-correction.worker.d.ts +0 -0
- package/dist/utils/plotting/fill-gaps.worker.d.ts +0 -0
- package/dist/utils/plotting/interpolate.worker.d.ts +0 -0
- package/dist/utils/plotting/observationRecord.d.ts +170 -0
- package/dist/utils/plotting/shift-datetimes.worker.d.ts +0 -0
- package/package.json +7 -4
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
|
@@ -1,119 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
var f = /* @__PURE__ */ ((r) => (r.ADD_POINTS = "ADD_POINTS", r.CHANGE_VALUES = "CHANGE_VALUES", r.DELETE_POINTS = "DELETE_POINTS", r.DRIFT_CORRECTION = "DRIFT_CORRECTION", r.INTERPOLATE = "INTERPOLATE", r.SHIFT_DATETIMES = "SHIFT_DATETIMES", r.FILL_GAPS = "FILL_GAPS", r))(f || {}), _ = /* @__PURE__ */ ((r) => (r.FIND_GAPS = "FIND_GAPS", r.PERSISTENCE = "PERSISTENCE", r.RATE_OF_CHANGE = "RATE_OF_CHANGE", r.VALUE_THRESHOLD = "VALUE_THRESHOLD", r))(_ || {});
|
|
2
|
+
const X = {
|
|
3
|
+
"Less than": (r, t) => r < t,
|
|
4
|
+
"Less than or equal to": (r, t) => r <= t,
|
|
5
|
+
"Greater than": (r, t) => r > t,
|
|
6
|
+
"Greater than or equal to": (r, t) => r >= t,
|
|
7
|
+
Equal: (r, t) => r == t,
|
|
8
|
+
"Start datetime": (r, t) => r == t,
|
|
9
|
+
"End datetime": (r, t) => r == t
|
|
10
|
+
};
|
|
11
|
+
var E = /* @__PURE__ */ ((r) => (r.ADD = "ADD", r.SUB = "SUB", r.MULT = "MULT", r.DIV = "DIV", r.ASSIGN = "ASSIGN", r))(E || {});
|
|
12
|
+
const x = {
|
|
13
|
+
"Less than": (r, t) => r < t,
|
|
14
|
+
"Less than or equal to": (r, t) => r <= t,
|
|
15
|
+
"Greater than": (r, t) => r > t,
|
|
16
|
+
"Greater than or equal to": (r, t) => r >= t,
|
|
17
|
+
Equal: (r, t) => r == t
|
|
18
|
+
}, I = `(function(){"use strict";self.onmessage=o=>{const{bufferX:s,bufferY:n,outputBufferX:u,outputBufferY:f,start:l,end:y,deleteSegment:a,startTarget:c}=o.data,A=new Float64Array(s),d=new Float32Array(n),g=new Float64Array(u),p=new Float32Array(f);let e=0,r=c;for(let t=l;t<=y;t++)e<a.length&&t===a[e]?e++:(g[r]=A[t],p[r]=d[t],r++);self.postMessage("Done")}})();
|
|
19
|
+
`, b = typeof self < "u" && self.Blob && new Blob([I], { type: "text/javascript;charset=utf-8" });
|
|
20
|
+
function U(r) {
|
|
4
21
|
let t;
|
|
5
22
|
try {
|
|
6
23
|
if (t = b && (self.URL || self.webkitURL).createObjectURL(b), !t) throw "";
|
|
7
24
|
const s = new Worker(t, {
|
|
8
|
-
name:
|
|
25
|
+
name: r?.name
|
|
9
26
|
});
|
|
10
27
|
return s.addEventListener("error", () => {
|
|
11
28
|
(self.URL || self.webkitURL).revokeObjectURL(t);
|
|
12
29
|
}), s;
|
|
13
30
|
} catch {
|
|
14
31
|
return new Worker(
|
|
15
|
-
"data:text/javascript;charset=utf-8," + encodeURIComponent(
|
|
32
|
+
"data:text/javascript;charset=utf-8," + encodeURIComponent(I),
|
|
16
33
|
{
|
|
17
|
-
name:
|
|
34
|
+
name: r?.name
|
|
18
35
|
}
|
|
19
36
|
);
|
|
20
37
|
} finally {
|
|
21
38
|
t && (self.URL || self.webkitURL).revokeObjectURL(t);
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
|
-
const
|
|
25
|
-
`,
|
|
26
|
-
function
|
|
41
|
+
const F = `(function(){"use strict";self.onmessage=function(e){const{bufferX:f,bufferY:t,outputBufferX:u,outputBufferY:s}=e.data;self.postMessage("Done")}})();
|
|
42
|
+
`, R = typeof self < "u" && self.Blob && new Blob([F], { type: "text/javascript;charset=utf-8" });
|
|
43
|
+
function M(r) {
|
|
27
44
|
let t;
|
|
28
45
|
try {
|
|
29
|
-
if (t =
|
|
46
|
+
if (t = R && (self.URL || self.webkitURL).createObjectURL(R), !t) throw "";
|
|
30
47
|
const s = new Worker(t, {
|
|
31
|
-
name:
|
|
48
|
+
name: r?.name
|
|
32
49
|
});
|
|
33
50
|
return s.addEventListener("error", () => {
|
|
34
51
|
(self.URL || self.webkitURL).revokeObjectURL(t);
|
|
35
52
|
}), s;
|
|
36
53
|
} catch {
|
|
37
54
|
return new Worker(
|
|
38
|
-
"data:text/javascript;charset=utf-8," + encodeURIComponent(
|
|
55
|
+
"data:text/javascript;charset=utf-8," + encodeURIComponent(F),
|
|
39
56
|
{
|
|
40
|
-
name:
|
|
57
|
+
name: r?.name
|
|
41
58
|
}
|
|
42
59
|
);
|
|
43
60
|
} finally {
|
|
44
61
|
t && (self.URL || self.webkitURL).revokeObjectURL(t);
|
|
45
62
|
}
|
|
46
63
|
}
|
|
47
|
-
const
|
|
48
|
-
let s = 0, e =
|
|
64
|
+
const C = (r, t) => {
|
|
65
|
+
let s = 0, e = r.length;
|
|
49
66
|
for (; s < e; ) {
|
|
50
|
-
const
|
|
51
|
-
|
|
67
|
+
const n = s + e >> 1;
|
|
68
|
+
r[n] < t ? s = n + 1 : e = n;
|
|
52
69
|
}
|
|
53
70
|
return s;
|
|
54
|
-
}, B = (
|
|
55
|
-
let s = 0, e =
|
|
71
|
+
}, B = (r, t) => {
|
|
72
|
+
let s = 0, e = r.length;
|
|
56
73
|
for (; s < e; ) {
|
|
57
|
-
const
|
|
58
|
-
|
|
74
|
+
const n = s + e >> 1;
|
|
75
|
+
r[n] > t ? e = n : s = n + 1;
|
|
59
76
|
}
|
|
60
77
|
return s - 1;
|
|
61
|
-
}, w = async (
|
|
62
|
-
const s = performance.now(), e = await
|
|
63
|
-
console.log(` Done in ${(
|
|
64
|
-
const a = +(
|
|
78
|
+
}, w = async (r, t) => {
|
|
79
|
+
const s = performance.now(), e = await r(), n = performance.now();
|
|
80
|
+
console.log(` Done in ${(n - s).toFixed(2)} ms`);
|
|
81
|
+
const a = +(n - s);
|
|
65
82
|
return { response: e, duration: a };
|
|
66
|
-
},
|
|
67
|
-
s:
|
|
83
|
+
}, N = 1, Y = N * 60, S = Y * 60, D = S * 24, G = D * 7, O = S * 30, k = D * 365, A = {
|
|
84
|
+
s: N,
|
|
68
85
|
m: Y,
|
|
69
|
-
h:
|
|
70
|
-
D
|
|
71
|
-
W:
|
|
72
|
-
M:
|
|
73
|
-
Y:
|
|
74
|
-
},
|
|
86
|
+
h: S,
|
|
87
|
+
D,
|
|
88
|
+
W: G,
|
|
89
|
+
M: O,
|
|
90
|
+
Y: k
|
|
91
|
+
}, H = (r, t, s) => {
|
|
75
92
|
if (s === "M") {
|
|
76
|
-
const e = new Date(
|
|
93
|
+
const e = new Date(r);
|
|
77
94
|
return e.setMonth(e.getMonth() + t), e.getTime();
|
|
78
95
|
} else if (s === "Y") {
|
|
79
|
-
const e = new Date(
|
|
96
|
+
const e = new Date(r);
|
|
80
97
|
return e.setFullYear(e.getFullYear() + t), e.getTime();
|
|
81
98
|
} else
|
|
82
|
-
return
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
"Less than or equal to": (o, t) => o <= t,
|
|
86
|
-
"Greater than": (o, t) => o > t,
|
|
87
|
-
"Greater than or equal to": (o, t) => o >= t,
|
|
88
|
-
Equal: (o, t) => o == t,
|
|
89
|
-
"Start datetime": (o, t) => o == t,
|
|
90
|
-
"End datetime": (o, t) => o == t
|
|
91
|
-
}, k = {
|
|
92
|
-
"Less than": (o, t) => o < t,
|
|
93
|
-
"Less than or equal to": (o, t) => o <= t,
|
|
94
|
-
"Greater than": (o, t) => o > t,
|
|
95
|
-
"Greater than or equal to": (o, t) => o >= t,
|
|
96
|
-
Equal: (o, t) => o == t
|
|
97
|
-
}, g = 20 * 1e3, C = ["date", "value", "qualifier"];
|
|
98
|
-
class G {
|
|
99
|
+
return r + t * A[s] * 1e3;
|
|
100
|
+
}, L = 20 * 1e3, v = ["date", "value", "qualifier"];
|
|
101
|
+
class V {
|
|
99
102
|
/** The generated dataset to be used for plotting */
|
|
100
103
|
dataset = {
|
|
101
|
-
dimensions:
|
|
104
|
+
dimensions: v,
|
|
102
105
|
source: {
|
|
103
106
|
x: new Float64Array(
|
|
104
107
|
new SharedArrayBuffer(
|
|
105
|
-
|
|
108
|
+
L * Float64Array.BYTES_PER_ELEMENT,
|
|
106
109
|
{
|
|
107
|
-
maxByteLength:
|
|
110
|
+
maxByteLength: L * Float64Array.BYTES_PER_ELEMENT
|
|
108
111
|
// Max size the array can reach
|
|
109
112
|
}
|
|
110
113
|
)
|
|
111
114
|
),
|
|
112
115
|
y: new Float32Array(
|
|
113
116
|
new SharedArrayBuffer(
|
|
114
|
-
|
|
117
|
+
L * Float32Array.BYTES_PER_ELEMENT,
|
|
115
118
|
{
|
|
116
|
-
maxByteLength:
|
|
119
|
+
maxByteLength: L * Float32Array.BYTES_PER_ELEMENT
|
|
117
120
|
// Max size the array can reach
|
|
118
121
|
}
|
|
119
122
|
)
|
|
@@ -162,9 +165,9 @@ class G {
|
|
|
162
165
|
const s = t * Float64Array.BYTES_PER_ELEMENT;
|
|
163
166
|
let e = this.dataX.buffer.byteLength;
|
|
164
167
|
for (; s > e; )
|
|
165
|
-
e +=
|
|
168
|
+
e += L * Float64Array.BYTES_PER_ELEMENT;
|
|
166
169
|
if (e * Float64Array.BYTES_PER_ELEMENT > this.dataX.buffer.maxByteLength) {
|
|
167
|
-
const
|
|
170
|
+
const n = new SharedArrayBuffer(
|
|
168
171
|
this.dataX.buffer.byteLength,
|
|
169
172
|
{
|
|
170
173
|
maxByteLength: e * Float64Array.BYTES_PER_ELEMENT
|
|
@@ -174,8 +177,8 @@ class G {
|
|
|
174
177
|
{
|
|
175
178
|
maxByteLength: e * Float32Array.BYTES_PER_ELEMENT
|
|
176
179
|
}
|
|
177
|
-
),
|
|
178
|
-
|
|
180
|
+
), o = new Float64Array(n), i = new Float32Array(a);
|
|
181
|
+
o.set(this.dataX), i.set(this.dataY), this.dataset.source.x = o, this.dataset.source.y = i;
|
|
179
182
|
}
|
|
180
183
|
this.dataX.buffer.byteLength < t * Float64Array.BYTES_PER_ELEMENT && (this.dataX.buffer.grow(t * Float64Array.BYTES_PER_ELEMENT), this.dataY.buffer.grow(t * Float32Array.BYTES_PER_ELEMENT));
|
|
181
184
|
}
|
|
@@ -210,36 +213,36 @@ class G {
|
|
|
210
213
|
/** Dispatch an operation and log its signature in hisotry */
|
|
211
214
|
async dispatch(t, ...s) {
|
|
212
215
|
const e = {
|
|
213
|
-
ADD_POINTS: this._addDataPoints,
|
|
214
|
-
CHANGE_VALUES: this._changeValues,
|
|
215
|
-
DELETE_POINTS: this._deleteDataPoints,
|
|
216
|
-
DRIFT_CORRECTION: this._driftCorrection,
|
|
217
|
-
INTERPOLATE: this._interpolate,
|
|
218
|
-
SHIFT_DATETIMES: this._shift,
|
|
219
|
-
FILL_GAPS: this._fillGaps
|
|
220
|
-
},
|
|
221
|
-
ADD_POINTS: "mdi-plus",
|
|
222
|
-
CHANGE_VALUES: "mdi-pencil",
|
|
223
|
-
DELETE_POINTS: "mdi-trash-can",
|
|
224
|
-
DRIFT_CORRECTION: "mdi-chart-sankey",
|
|
225
|
-
INTERPOLATE: "mdi-transit-connection-horizontal",
|
|
226
|
-
SHIFT_DATETIMES: "mdi-calendar",
|
|
227
|
-
FILL_GAPS: "mdi-keyboard-space"
|
|
216
|
+
[f.ADD_POINTS]: this._addDataPoints,
|
|
217
|
+
[f.CHANGE_VALUES]: this._changeValues,
|
|
218
|
+
[f.DELETE_POINTS]: this._deleteDataPoints,
|
|
219
|
+
[f.DRIFT_CORRECTION]: this._driftCorrection,
|
|
220
|
+
[f.INTERPOLATE]: this._interpolate,
|
|
221
|
+
[f.SHIFT_DATETIMES]: this._shift,
|
|
222
|
+
[f.FILL_GAPS]: this._fillGaps
|
|
223
|
+
}, n = {
|
|
224
|
+
[f.ADD_POINTS]: "mdi-plus",
|
|
225
|
+
[f.CHANGE_VALUES]: "mdi-pencil",
|
|
226
|
+
[f.DELETE_POINTS]: "mdi-trash-can",
|
|
227
|
+
[f.DRIFT_CORRECTION]: "mdi-chart-sankey",
|
|
228
|
+
[f.INTERPOLATE]: "mdi-transit-connection-horizontal",
|
|
229
|
+
[f.SHIFT_DATETIMES]: "mdi-calendar",
|
|
230
|
+
[f.FILL_GAPS]: "mdi-keyboard-space"
|
|
228
231
|
};
|
|
229
232
|
let a = [];
|
|
230
233
|
try {
|
|
231
234
|
if (Array.isArray(t)) {
|
|
232
|
-
for (let
|
|
233
|
-
const i = t[
|
|
235
|
+
for (let o = 0; o < t.length; o++) {
|
|
236
|
+
const i = t[o][0], h = t[o].slice(1, t[o].length), l = {
|
|
234
237
|
method: i,
|
|
235
238
|
args: h,
|
|
236
|
-
icon:
|
|
239
|
+
icon: n[i],
|
|
237
240
|
isLoading: !1
|
|
238
241
|
};
|
|
239
242
|
this.history.push(l);
|
|
240
243
|
}
|
|
241
|
-
for (let
|
|
242
|
-
const i = this.history[
|
|
244
|
+
for (let o = this.history.length - t.length; o < this.history.length; o++) {
|
|
245
|
+
const i = this.history[o];
|
|
243
246
|
i.isLoading = !0;
|
|
244
247
|
const h = await w(async () => await e[i.method].apply(
|
|
245
248
|
this,
|
|
@@ -248,48 +251,48 @@ class G {
|
|
|
248
251
|
i.duration = h.duration, i.isLoading = !1, a.push(h.response);
|
|
249
252
|
}
|
|
250
253
|
} else {
|
|
251
|
-
const
|
|
254
|
+
const o = {
|
|
252
255
|
method: t,
|
|
253
256
|
args: s,
|
|
254
|
-
icon:
|
|
257
|
+
icon: n[t],
|
|
255
258
|
isLoading: !0
|
|
256
259
|
};
|
|
257
|
-
this.history.push(
|
|
260
|
+
this.history.push(o);
|
|
258
261
|
const i = await w(async () => await e[t].apply(this, s));
|
|
259
|
-
a = i.response,
|
|
262
|
+
a = i.response, o.duration = i.duration, o.isLoading = !1;
|
|
260
263
|
}
|
|
261
|
-
} catch (
|
|
264
|
+
} catch (o) {
|
|
262
265
|
console.log(
|
|
263
266
|
`Failed to execute operation: ${t} with arguments: `,
|
|
264
267
|
s
|
|
265
|
-
), console.log(
|
|
268
|
+
), console.log(o);
|
|
266
269
|
}
|
|
267
270
|
return a;
|
|
268
271
|
}
|
|
269
272
|
/** Filter operations do not transform the data and are not logged in history */
|
|
270
273
|
async dispatchFilter(t, ...s) {
|
|
271
274
|
const e = {
|
|
272
|
-
FIND_GAPS: this._findGaps,
|
|
273
|
-
VALUE_THRESHOLD: this._valueThreshold,
|
|
274
|
-
PERSISTENCE: this._persistence,
|
|
275
|
-
RATE_OF_CHANGE: this._rateOfChange
|
|
275
|
+
[_.FIND_GAPS]: this._findGaps,
|
|
276
|
+
[_.VALUE_THRESHOLD]: this._valueThreshold,
|
|
277
|
+
[_.PERSISTENCE]: this._persistence,
|
|
278
|
+
[_.RATE_OF_CHANGE]: this._rateOfChange
|
|
276
279
|
};
|
|
277
|
-
let
|
|
280
|
+
let n = [];
|
|
278
281
|
try {
|
|
279
282
|
if (Array.isArray(t))
|
|
280
283
|
for (let a = 0; a < t.length; a++) {
|
|
281
|
-
const
|
|
282
|
-
|
|
284
|
+
const o = t[a][0], i = t[a].slice(1, t[a].length), h = await e[o].apply(this, i);
|
|
285
|
+
n.push(h);
|
|
283
286
|
}
|
|
284
287
|
else
|
|
285
|
-
|
|
288
|
+
n = await e[t].apply(this, s);
|
|
286
289
|
} catch (a) {
|
|
287
290
|
console.log(
|
|
288
291
|
`Failed to execute filter operation: ${t} with arguments: `,
|
|
289
292
|
s
|
|
290
293
|
), console.log(a);
|
|
291
294
|
}
|
|
292
|
-
return
|
|
295
|
+
return n;
|
|
293
296
|
}
|
|
294
297
|
/**
|
|
295
298
|
* @param index An array containing the list of index of values to perform the operations on.
|
|
@@ -298,44 +301,44 @@ class G {
|
|
|
298
301
|
* @returns The modified DataFrame
|
|
299
302
|
*/
|
|
300
303
|
_changeValues(t, s, e) {
|
|
301
|
-
const
|
|
304
|
+
const n = (a) => {
|
|
302
305
|
switch (s) {
|
|
303
|
-
case
|
|
306
|
+
case E.ADD:
|
|
304
307
|
return a + e;
|
|
305
|
-
case
|
|
308
|
+
case E.ASSIGN:
|
|
306
309
|
return e;
|
|
307
|
-
case
|
|
310
|
+
case E.DIV:
|
|
308
311
|
return a / e;
|
|
309
|
-
case
|
|
312
|
+
case E.MULT:
|
|
310
313
|
return a * e;
|
|
311
|
-
case
|
|
314
|
+
case E.SUB:
|
|
312
315
|
return a - e;
|
|
313
316
|
default:
|
|
314
317
|
return a;
|
|
315
318
|
}
|
|
316
319
|
};
|
|
317
320
|
t.forEach((a) => {
|
|
318
|
-
this.dataset.source.y[a] =
|
|
321
|
+
this.dataset.source.y[a] = n(this.dataset.source.y[a]);
|
|
319
322
|
});
|
|
320
323
|
}
|
|
321
324
|
_interpolate(t) {
|
|
322
325
|
this._getConsecutiveGroups(t).forEach((e) => {
|
|
323
|
-
const
|
|
324
|
-
let
|
|
326
|
+
const n = e[0], a = e[e.length - 1];
|
|
327
|
+
let o = Math.max(0, n - 1), i = Math.min(this.dataset.source.y.length - 1, a + 1);
|
|
325
328
|
const h = this.dataset.source.x, l = this.dataset.source.y;
|
|
326
|
-
for (let
|
|
327
|
-
this.dataset.source.y[e[
|
|
328
|
-
h[e[
|
|
329
|
-
h[
|
|
330
|
-
l[
|
|
329
|
+
for (let u = 0; u < e.length; u++)
|
|
330
|
+
this.dataset.source.y[e[u]] = this._interpolateLinear(
|
|
331
|
+
h[e[u]],
|
|
332
|
+
h[o],
|
|
333
|
+
l[o],
|
|
331
334
|
h[i],
|
|
332
335
|
l[i]
|
|
333
336
|
);
|
|
334
337
|
});
|
|
335
338
|
}
|
|
336
339
|
/** Interpolate existing values in the data source */
|
|
337
|
-
_interpolateLinear(t, s, e,
|
|
338
|
-
return e + (t - s) * (a - e) / (
|
|
340
|
+
_interpolateLinear(t, s, e, n, a) {
|
|
341
|
+
return e + (t - s) * (a - e) / (n - s);
|
|
339
342
|
}
|
|
340
343
|
/**
|
|
341
344
|
* Shifts the selected indexes by specified amount of units. Elements are reinserted according to their datetime.
|
|
@@ -345,33 +348,33 @@ class G {
|
|
|
345
348
|
* @returns
|
|
346
349
|
*/
|
|
347
350
|
async _shift(t, s, e) {
|
|
348
|
-
const
|
|
349
|
-
|
|
351
|
+
const n = t.map((a) => [
|
|
352
|
+
H(this.dataX[a], s, e),
|
|
350
353
|
this.dataY[a]
|
|
351
354
|
]);
|
|
352
|
-
await this._deleteDataPoints(t), await this._addDataPoints(
|
|
355
|
+
await this._deleteDataPoints(t), await this._addDataPoints(n);
|
|
353
356
|
}
|
|
354
|
-
async _fillGapsV2(t, s, e,
|
|
355
|
-
const a = navigator.hardwareConcurrency || 1,
|
|
357
|
+
async _fillGapsV2(t, s, e, n) {
|
|
358
|
+
const a = navigator.hardwareConcurrency || 1, o = [], i = [], h = this.dataX.length, l = new SharedArrayBuffer(this.dataX.buffer.byteLength, {
|
|
356
359
|
maxByteLength: this.dataX.buffer.maxByteLength
|
|
357
|
-
}),
|
|
360
|
+
}), u = new SharedArrayBuffer(this.dataY.buffer.byteLength, {
|
|
358
361
|
maxByteLength: this.dataY.buffer.maxByteLength
|
|
359
362
|
});
|
|
360
|
-
for (let
|
|
363
|
+
for (let c = 0; c < a; c++)
|
|
361
364
|
i.push(
|
|
362
|
-
new Promise((
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
+
new Promise((d) => {
|
|
366
|
+
const y = new M();
|
|
367
|
+
o.push(y), y.postMessage({
|
|
365
368
|
bufferX: this.dataX.buffer,
|
|
366
369
|
bufferY: this.dataY.buffer,
|
|
367
370
|
outputBufferX: l,
|
|
368
|
-
outputBufferY:
|
|
369
|
-
}),
|
|
370
|
-
|
|
371
|
+
outputBufferY: u
|
|
372
|
+
}), y.onmessage = (g) => {
|
|
373
|
+
d(g.data);
|
|
371
374
|
};
|
|
372
375
|
})
|
|
373
376
|
);
|
|
374
|
-
await Promise.all(i),
|
|
377
|
+
await Promise.all(i), o.forEach((c) => c.terminate()), this.dataset.source.x = new Float64Array(l), this.dataset.source.y = new Float32Array(u), this._resizeTo(h);
|
|
375
378
|
}
|
|
376
379
|
/**
|
|
377
380
|
* Find gaps and fill them with placeholder value
|
|
@@ -381,22 +384,22 @@ class G {
|
|
|
381
384
|
* @returns
|
|
382
385
|
*/
|
|
383
386
|
// TODO: this needs to be improved using web workers
|
|
384
|
-
_fillGaps(t, s, e,
|
|
385
|
-
const a = this._findGaps(t[0], t[1],
|
|
386
|
-
for (let
|
|
387
|
-
const i = a[
|
|
388
|
-
let
|
|
389
|
-
for (;
|
|
390
|
-
const
|
|
391
|
-
|
|
387
|
+
_fillGaps(t, s, e, n) {
|
|
388
|
+
const a = this._findGaps(t[0], t[1], n);
|
|
389
|
+
for (let o = a.length - 1; o >= 0; o--) {
|
|
390
|
+
const i = a[o], h = this.dataX[i[0]], l = this.dataX[i[1]], u = [], c = s[0] * A[s[1]] * 1e3;
|
|
391
|
+
let d = h + c;
|
|
392
|
+
for (; d < l; ) {
|
|
393
|
+
const y = e ? this._interpolateLinear(
|
|
394
|
+
d,
|
|
392
395
|
this.dataX[i[0]],
|
|
393
396
|
this.dataY[i[0]],
|
|
394
397
|
this.dataX[i[1]],
|
|
395
398
|
this.dataY[i[1]]
|
|
396
399
|
) : -9999;
|
|
397
|
-
|
|
400
|
+
u.push([d, y]), d += c;
|
|
398
401
|
}
|
|
399
|
-
this._addDataPoints(
|
|
402
|
+
this._addDataPoints(u);
|
|
400
403
|
}
|
|
401
404
|
}
|
|
402
405
|
/**
|
|
@@ -409,40 +412,40 @@ class G {
|
|
|
409
412
|
*/
|
|
410
413
|
// TODO: implement similar multithread solutions for other operations
|
|
411
414
|
async _deleteDataPoints(t) {
|
|
412
|
-
const s = navigator.hardwareConcurrency || 1, e = Math.ceil(this.dataX.length / s),
|
|
413
|
-
for (let
|
|
414
|
-
const
|
|
415
|
-
a.push({ start:
|
|
415
|
+
const s = navigator.hardwareConcurrency || 1, e = Math.ceil(this.dataX.length / s), n = [], a = [];
|
|
416
|
+
for (let c = 0; c < s; c++) {
|
|
417
|
+
const d = c * e, y = Math.min((c + 1) * e - 1, this.dataX.length - 1), g = C(t, d), p = B(t, y), m = t.slice(g, p + 1);
|
|
418
|
+
a.push({ start: d, end: y, deleteSegment: m });
|
|
416
419
|
}
|
|
417
|
-
const
|
|
418
|
-
for (let
|
|
419
|
-
|
|
420
|
+
const o = new Array(s).fill(0);
|
|
421
|
+
for (let c = 1; c < s; c++)
|
|
422
|
+
o[c] = o[c - 1] + a[c - 1].deleteSegment.length;
|
|
420
423
|
const i = [], h = this.dataX.length - t.length, l = new SharedArrayBuffer(this.dataX.buffer.byteLength, {
|
|
421
424
|
maxByteLength: this.dataX.buffer.maxByteLength
|
|
422
|
-
}),
|
|
425
|
+
}), u = new SharedArrayBuffer(this.dataY.buffer.byteLength, {
|
|
423
426
|
maxByteLength: this.dataY.buffer.maxByteLength
|
|
424
427
|
});
|
|
425
|
-
for (let
|
|
426
|
-
const { start:
|
|
428
|
+
for (let c = 0; c < s; c++) {
|
|
429
|
+
const { start: d, end: y, deleteSegment: g } = a[c], p = d - o[c];
|
|
427
430
|
i.push(
|
|
428
431
|
new Promise((m) => {
|
|
429
|
-
const
|
|
430
|
-
|
|
432
|
+
const T = new U();
|
|
433
|
+
n.push(T), T.postMessage({
|
|
431
434
|
bufferX: this.dataX.buffer,
|
|
432
435
|
bufferY: this.dataY.buffer,
|
|
433
436
|
outputBufferX: l,
|
|
434
|
-
outputBufferY:
|
|
435
|
-
start:
|
|
436
|
-
end:
|
|
437
|
-
deleteSegment:
|
|
437
|
+
outputBufferY: u,
|
|
438
|
+
start: d,
|
|
439
|
+
end: y,
|
|
440
|
+
deleteSegment: g,
|
|
438
441
|
startTarget: p
|
|
439
|
-
}),
|
|
440
|
-
m(
|
|
442
|
+
}), T.onmessage = (P) => {
|
|
443
|
+
m(P.data);
|
|
441
444
|
};
|
|
442
445
|
})
|
|
443
446
|
);
|
|
444
447
|
}
|
|
445
|
-
await Promise.all(i),
|
|
448
|
+
await Promise.all(i), n.forEach((c) => c.terminate()), this.dataset.source.x = new Float64Array(l), this.dataset.source.y = new Float32Array(u), this._resizeTo(h);
|
|
446
449
|
}
|
|
447
450
|
/**
|
|
448
451
|
*
|
|
@@ -451,9 +454,9 @@ class G {
|
|
|
451
454
|
* @param value The drift amount
|
|
452
455
|
*/
|
|
453
456
|
_driftCorrection(t, s, e) {
|
|
454
|
-
const
|
|
457
|
+
const n = this.dataset.source.x, a = this.dataset.source.y, o = n[t], h = n[s] - o;
|
|
455
458
|
for (let l = t; l < s; l++)
|
|
456
|
-
this.dataset.source.y[l] = a[l] + e * ((
|
|
459
|
+
this.dataset.source.y[l] = a[l] + e * ((n[l] - o) / h);
|
|
457
460
|
}
|
|
458
461
|
/** Traverses the index array and returns groups of consecutive values.
|
|
459
462
|
* i.e.: `[0, 1, 3, 4, 6] => [[0, 1], [3, 4], [6]]`
|
|
@@ -462,9 +465,9 @@ class G {
|
|
|
462
465
|
*/
|
|
463
466
|
_getConsecutiveGroups(t) {
|
|
464
467
|
const s = [[]];
|
|
465
|
-
return t.reduce((e,
|
|
468
|
+
return t.reduce((e, n) => {
|
|
466
469
|
const a = e[e.length - 1];
|
|
467
|
-
return !a.length ||
|
|
470
|
+
return !a.length || n == a[a.length - 1] + 1 ? a.push(n) : e.push([n]), e;
|
|
468
471
|
}, s), s;
|
|
469
472
|
}
|
|
470
473
|
/**
|
|
@@ -473,15 +476,15 @@ class G {
|
|
|
473
476
|
*/
|
|
474
477
|
async _addDataPoints(t) {
|
|
475
478
|
const s = this.dataX.length + t.length;
|
|
476
|
-
this._growBuffer(s), t.sort((a,
|
|
479
|
+
this._growBuffer(s), t.sort((a, o) => a[0] - o[0]);
|
|
477
480
|
const e = t.map((a) => B(this.dataX, a[0]) + 1);
|
|
478
481
|
this._resizeTo(s), e.push(this.dataX.length);
|
|
479
|
-
let
|
|
482
|
+
let n = t.length;
|
|
480
483
|
for (let a = e.length - 1; a > 0; a--) {
|
|
481
|
-
const
|
|
482
|
-
for (let h = i; h >=
|
|
483
|
-
this.dataX[h +
|
|
484
|
-
|
|
484
|
+
const o = e[a - 1], i = e[a] - 1;
|
|
485
|
+
for (let h = i; h >= o; h--)
|
|
486
|
+
this.dataX[h + n] = this.dataX[h], this.dataY[h + n] = this.dataY[h];
|
|
487
|
+
n--, this.dataX[o + n] = t[a - 1][0], this.dataY[o + n] = t[a - 1][1];
|
|
485
488
|
}
|
|
486
489
|
}
|
|
487
490
|
// =======================
|
|
@@ -494,11 +497,11 @@ class G {
|
|
|
494
497
|
*/
|
|
495
498
|
_valueThreshold(t) {
|
|
496
499
|
const s = [];
|
|
497
|
-
return this.dataset.source.y.forEach((e,
|
|
498
|
-
Object.keys(t).some((a) =>
|
|
500
|
+
return this.dataset.source.y.forEach((e, n) => {
|
|
501
|
+
Object.keys(t).some((a) => X[a]?.(
|
|
499
502
|
e,
|
|
500
503
|
t[a]
|
|
501
|
-
)) && s.push(
|
|
504
|
+
)) && s.push(n);
|
|
502
505
|
}), s;
|
|
503
506
|
}
|
|
504
507
|
/**
|
|
@@ -508,10 +511,10 @@ class G {
|
|
|
508
511
|
* @returns
|
|
509
512
|
*/
|
|
510
513
|
_rateOfChange(t, s) {
|
|
511
|
-
const e = [],
|
|
512
|
-
for (let a = 1; a <
|
|
513
|
-
const
|
|
514
|
-
|
|
514
|
+
const e = [], n = this.dataset.source.y;
|
|
515
|
+
for (let a = 1; a < n.length; a++) {
|
|
516
|
+
const o = n[a - 1], h = (n[a] - o) / Math.abs(o);
|
|
517
|
+
x[t]?.(
|
|
515
518
|
h,
|
|
516
519
|
s
|
|
517
520
|
) && e.push(a);
|
|
@@ -526,15 +529,15 @@ class G {
|
|
|
526
529
|
* @returns
|
|
527
530
|
*/
|
|
528
531
|
_findGaps(t, s, e) {
|
|
529
|
-
const
|
|
530
|
-
let
|
|
531
|
-
e?.[0] && e?.[1] && (
|
|
532
|
-
let h = a[
|
|
533
|
-
for (let l =
|
|
534
|
-
const
|
|
535
|
-
|
|
532
|
+
const n = [], a = this.dataset.source.x;
|
|
533
|
+
let o = 0, i = a.length;
|
|
534
|
+
e?.[0] && e?.[1] && (o = e[0], i = e[1]);
|
|
535
|
+
let h = a[o];
|
|
536
|
+
for (let l = o + 1; l <= i; l++) {
|
|
537
|
+
const u = a[l];
|
|
538
|
+
u - h > t * A[s] * 1e3 && n.push([l - 1, l]), h = u;
|
|
536
539
|
}
|
|
537
|
-
return
|
|
540
|
+
return n;
|
|
538
541
|
}
|
|
539
542
|
/**
|
|
540
543
|
* Find points where the values are the same at least x times in a row
|
|
@@ -543,14 +546,14 @@ class G {
|
|
|
543
546
|
* @returns
|
|
544
547
|
*/
|
|
545
548
|
_persistence(t, s) {
|
|
546
|
-
let e = [],
|
|
547
|
-
s?.[0] && s?.[1] && (a = s[0],
|
|
548
|
-
let i =
|
|
549
|
-
for (let l = a + 1; l <
|
|
550
|
-
|
|
549
|
+
let e = [], n = this.dataset.source.y, a = 0, o = n.length;
|
|
550
|
+
s?.[0] && s?.[1] && (a = s[0], o = s[1]);
|
|
551
|
+
let i = n[a], h = [];
|
|
552
|
+
for (let l = a + 1; l < o; l++)
|
|
553
|
+
n[l] != i || l === o ? (h.length >= t && (e = [...e, ...h]), h = []) : h.push(l);
|
|
551
554
|
return e;
|
|
552
555
|
}
|
|
553
556
|
}
|
|
554
557
|
export {
|
|
555
|
-
|
|
558
|
+
V as ObservationRecord
|
|
556
559
|
};
|
package/dist/index.umd.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`,B=typeof self<"u"&&self.Blob&&new Blob([
|
|
3
|
-
`,
|
|
1
|
+
(function(E,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(E=typeof globalThis<"u"?globalThis:E||self,c(E["@uwrl/qc-utils"]={}))})(this,function(E){"use strict";var c=(r=>(r.ADD_POINTS="ADD_POINTS",r.CHANGE_VALUES="CHANGE_VALUES",r.DELETE_POINTS="DELETE_POINTS",r.DRIFT_CORRECTION="DRIFT_CORRECTION",r.INTERPOLATE="INTERPOLATE",r.SHIFT_DATETIMES="SHIFT_DATETIMES",r.FILL_GAPS="FILL_GAPS",r))(c||{}),p=(r=>(r.FIND_GAPS="FIND_GAPS",r.PERSISTENCE="PERSISTENCE",r.RATE_OF_CHANGE="RATE_OF_CHANGE",r.VALUE_THRESHOLD="VALUE_THRESHOLD",r))(p||{});const x={"Less than":(r,t)=>r<t,"Less than or equal to":(r,t)=>r<=t,"Greater than":(r,t)=>r>t,"Greater than or equal to":(r,t)=>r>=t,Equal:(r,t)=>r==t,"Start datetime":(r,t)=>r==t,"End datetime":(r,t)=>r==t};var g=(r=>(r.ADD="ADD",r.SUB="SUB",r.MULT="MULT",r.DIV="DIV",r.ASSIGN="ASSIGN",r))(g||{});const X={"Less than":(r,t)=>r<t,"Less than or equal to":(r,t)=>r<=t,"Greater than":(r,t)=>r>t,"Greater than or equal to":(r,t)=>r>=t,Equal:(r,t)=>r==t},R=`(function(){"use strict";self.onmessage=o=>{const{bufferX:s,bufferY:n,outputBufferX:u,outputBufferY:f,start:l,end:y,deleteSegment:a,startTarget:c}=o.data,A=new Float64Array(s),d=new Float32Array(n),g=new Float64Array(u),p=new Float32Array(f);let e=0,r=c;for(let t=l;t<=y;t++)e<a.length&&t===a[e]?e++:(g[r]=A[t],p[r]=d[t],r++);self.postMessage("Done")}})();
|
|
2
|
+
`,B=typeof self<"u"&&self.Blob&&new Blob([R],{type:"text/javascript;charset=utf-8"});function U(r){let t;try{if(t=B&&(self.URL||self.webkitURL).createObjectURL(B),!t)throw"";const s=new Worker(t,{name:r?.name});return s.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(t)}),s}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(R),{name:r?.name})}finally{t&&(self.URL||self.webkitURL).revokeObjectURL(t)}}const I=`(function(){"use strict";self.onmessage=function(e){const{bufferX:f,bufferY:t,outputBufferX:u,outputBufferY:s}=e.data;self.postMessage("Done")}})();
|
|
3
|
+
`,F=typeof self<"u"&&self.Blob&&new Blob([I],{type:"text/javascript;charset=utf-8"});function M(r){let t;try{if(t=F&&(self.URL||self.webkitURL).createObjectURL(F),!t)throw"";const s=new Worker(t,{name:r?.name});return s.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(t)}),s}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(I),{name:r?.name})}finally{t&&(self.URL||self.webkitURL).revokeObjectURL(t)}}const C=(r,t)=>{let s=0,e=r.length;for(;s<e;){const n=s+e>>1;r[n]<t?s=n+1:e=n}return s},N=(r,t)=>{let s=0,e=r.length;for(;s<e;){const n=s+e>>1;r[n]>t?e=n:s=n+1}return s-1},_=async(r,t)=>{const s=performance.now(),e=await r(),n=performance.now();console.log(` Done in ${(n-s).toFixed(2)} ms`);const a=+(n-s);return{response:e,duration:a}},Y=1,P=Y*60,T=P*60,w=T*24,O=w*7,G=T*30,k=w*365,A={s:Y,m:P,h:T,D:w,W:O,M:G,Y:k},v=(r,t,s)=>{if(s==="M"){const e=new Date(r);return e.setMonth(e.getMonth()+t),e.getTime()}else if(s==="Y"){const e=new Date(r);return e.setFullYear(e.getFullYear()+t),e.getTime()}else return r+t*A[s]*1e3},L=20*1e3,H=["date","value","qualifier"];class j{dataset={dimensions:H,source:{x:new Float64Array(new SharedArrayBuffer(L*Float64Array.BYTES_PER_ELEMENT,{maxByteLength:L*Float64Array.BYTES_PER_ELEMENT})),y:new Float32Array(new SharedArrayBuffer(L*Float32Array.BYTES_PER_ELEMENT,{maxByteLength:L*Float32Array.BYTES_PER_ELEMENT}))}};history=[];loadingTime=null;isLoading=!0;rawData;constructor(t){this.history=[],this.rawData=t,this.loadData(this.rawData)}async loadData(t){if(!t)return;this.isLoading=!0;const s=await _(()=>{this._growBuffer(t.datetimes.length),this._resizeTo(t.datetimes.length),this.dataX.set(t.datetimes),this.dataY.set(t.dataValues)});this.loadingTime=s.duration,this.history.length=0,this.isLoading=!1}get dataX(){return this.dataset.source.x}get dataY(){return this.dataset.source.y}_resizeTo(t){this.dataset.source.x=new Float64Array(this.dataset.source.x.buffer).subarray(0,t),this.dataset.source.y=new Float32Array(this.dataset.source.y.buffer).subarray(0,t)}_growBuffer(t){const s=t*Float64Array.BYTES_PER_ELEMENT;let e=this.dataX.buffer.byteLength;for(;s>e;)e+=L*Float64Array.BYTES_PER_ELEMENT;if(e*Float64Array.BYTES_PER_ELEMENT>this.dataX.buffer.maxByteLength){const n=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:e*Float64Array.BYTES_PER_ELEMENT}),a=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:e*Float32Array.BYTES_PER_ELEMENT}),o=new Float64Array(n),i=new Float32Array(a);o.set(this.dataX),i.set(this.dataY),this.dataset.source.x=o,this.dataset.source.y=i}this.dataX.buffer.byteLength<t*Float64Array.BYTES_PER_ELEMENT&&(this.dataX.buffer.grow(t*Float64Array.BYTES_PER_ELEMENT),this.dataY.buffer.grow(t*Float32Array.BYTES_PER_ELEMENT))}async reload(){this.loadingTime=null,this.isLoading=!0,this.history.length=0,await this.loadData(this.rawData)}async reloadHistory(t){const s=this.history.slice(0,t+1);await this.reload(),await this.dispatch(s.map(e=>[e.method,...e.args||[]]))}async removeHistoryItem(t){const s=[...this.history];s.splice(t,1),await this.reload(),await this.dispatch(s.map(e=>[e.method,...e.args||[]]))}get beginTime(){return this.dataset.source.x.length?new Date(this.dataset.source.x[0]):null}get endTime(){return this.dataset.source.x.length?new Date(this.dataset.source.x[this.dataset.source.x.length-1]):null}async dispatch(t,...s){const e={[c.ADD_POINTS]:this._addDataPoints,[c.CHANGE_VALUES]:this._changeValues,[c.DELETE_POINTS]:this._deleteDataPoints,[c.DRIFT_CORRECTION]:this._driftCorrection,[c.INTERPOLATE]:this._interpolate,[c.SHIFT_DATETIMES]:this._shift,[c.FILL_GAPS]:this._fillGaps},n={[c.ADD_POINTS]:"mdi-plus",[c.CHANGE_VALUES]:"mdi-pencil",[c.DELETE_POINTS]:"mdi-trash-can",[c.DRIFT_CORRECTION]:"mdi-chart-sankey",[c.INTERPOLATE]:"mdi-transit-connection-horizontal",[c.SHIFT_DATETIMES]:"mdi-calendar",[c.FILL_GAPS]:"mdi-keyboard-space"};let a=[];try{if(Array.isArray(t)){for(let o=0;o<t.length;o++){const i=t[o][0],h=t[o].slice(1,t[o].length),l={method:i,args:h,icon:n[i],isLoading:!1};this.history.push(l)}for(let o=this.history.length-t.length;o<this.history.length;o++){const i=this.history[o];i.isLoading=!0;const h=await _(async()=>await e[i.method].apply(this,i.args));i.duration=h.duration,i.isLoading=!1,a.push(h.response)}}else{const o={method:t,args:s,icon:n[t],isLoading:!0};this.history.push(o);const i=await _(async()=>await e[t].apply(this,s));a=i.response,o.duration=i.duration,o.isLoading=!1}}catch(o){console.log(`Failed to execute operation: ${t} with arguments: `,s),console.log(o)}return a}async dispatchFilter(t,...s){const e={[p.FIND_GAPS]:this._findGaps,[p.VALUE_THRESHOLD]:this._valueThreshold,[p.PERSISTENCE]:this._persistence,[p.RATE_OF_CHANGE]:this._rateOfChange};let n=[];try{if(Array.isArray(t))for(let a=0;a<t.length;a++){const o=t[a][0],i=t[a].slice(1,t[a].length),h=await e[o].apply(this,i);n.push(h)}else n=await e[t].apply(this,s)}catch(a){console.log(`Failed to execute filter operation: ${t} with arguments: `,s),console.log(a)}return n}_changeValues(t,s,e){const n=a=>{switch(s){case g.ADD:return a+e;case g.ASSIGN:return e;case g.DIV:return a/e;case g.MULT:return a*e;case g.SUB:return a-e;default:return a}};t.forEach(a=>{this.dataset.source.y[a]=n(this.dataset.source.y[a])})}_interpolate(t){this._getConsecutiveGroups(t).forEach(e=>{const n=e[0],a=e[e.length-1];let o=Math.max(0,n-1),i=Math.min(this.dataset.source.y.length-1,a+1);const h=this.dataset.source.x,l=this.dataset.source.y;for(let f=0;f<e.length;f++)this.dataset.source.y[e[f]]=this._interpolateLinear(h[e[f]],h[o],l[o],h[i],l[i])})}_interpolateLinear(t,s,e,n,a){return e+(t-s)*(a-e)/(n-s)}async _shift(t,s,e){const n=t.map(a=>[v(this.dataX[a],s,e),this.dataY[a]]);await this._deleteDataPoints(t),await this._addDataPoints(n)}async _fillGapsV2(t,s,e,n){const a=navigator.hardwareConcurrency||1,o=[],i=[],h=this.dataX.length,l=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:this.dataX.buffer.maxByteLength}),f=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:this.dataY.buffer.maxByteLength});for(let u=0;u<a;u++)i.push(new Promise(d=>{const y=new M;o.push(y),y.postMessage({bufferX:this.dataX.buffer,bufferY:this.dataY.buffer,outputBufferX:l,outputBufferY:f}),y.onmessage=m=>{d(m.data)}}));await Promise.all(i),o.forEach(u=>u.terminate()),this.dataset.source.x=new Float64Array(l),this.dataset.source.y=new Float32Array(f),this._resizeTo(h)}_fillGaps(t,s,e,n){const a=this._findGaps(t[0],t[1],n);for(let o=a.length-1;o>=0;o--){const i=a[o],h=this.dataX[i[0]],l=this.dataX[i[1]],f=[],u=s[0]*A[s[1]]*1e3;let d=h+u;for(;d<l;){const y=e?this._interpolateLinear(d,this.dataX[i[0]],this.dataY[i[0]],this.dataX[i[1]],this.dataY[i[1]]):-9999;f.push([d,y]),d+=u}this._addDataPoints(f)}}async _deleteDataPoints(t){const s=navigator.hardwareConcurrency||1,e=Math.ceil(this.dataX.length/s),n=[],a=[];for(let u=0;u<s;u++){const d=u*e,y=Math.min((u+1)*e-1,this.dataX.length-1),m=C(t,d),S=N(t,y),D=t.slice(m,S+1);a.push({start:d,end:y,deleteSegment:D})}const o=new Array(s).fill(0);for(let u=1;u<s;u++)o[u]=o[u-1]+a[u-1].deleteSegment.length;const i=[],h=this.dataX.length-t.length,l=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:this.dataX.buffer.maxByteLength}),f=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:this.dataY.buffer.maxByteLength});for(let u=0;u<s;u++){const{start:d,end:y,deleteSegment:m}=a[u],S=d-o[u];i.push(new Promise(D=>{const b=new U;n.push(b),b.postMessage({bufferX:this.dataX.buffer,bufferY:this.dataY.buffer,outputBufferX:l,outputBufferY:f,start:d,end:y,deleteSegment:m,startTarget:S}),b.onmessage=V=>{D(V.data)}}))}await Promise.all(i),n.forEach(u=>u.terminate()),this.dataset.source.x=new Float64Array(l),this.dataset.source.y=new Float32Array(f),this._resizeTo(h)}_driftCorrection(t,s,e){const n=this.dataset.source.x,a=this.dataset.source.y,o=n[t],h=n[s]-o;for(let l=t;l<s;l++)this.dataset.source.y[l]=a[l]+e*((n[l]-o)/h)}_getConsecutiveGroups(t){const s=[[]];return t.reduce((e,n)=>{const a=e[e.length-1];return!a.length||n==a[a.length-1]+1?a.push(n):e.push([n]),e},s),s}async _addDataPoints(t){const s=this.dataX.length+t.length;this._growBuffer(s),t.sort((a,o)=>a[0]-o[0]);const e=t.map(a=>N(this.dataX,a[0])+1);this._resizeTo(s),e.push(this.dataX.length);let n=t.length;for(let a=e.length-1;a>0;a--){const o=e[a-1],i=e[a]-1;for(let h=i;h>=o;h--)this.dataX[h+n]=this.dataX[h],this.dataY[h+n]=this.dataY[h];n--,this.dataX[o+n]=t[a-1][0],this.dataY[o+n]=t[a-1][1]}}_valueThreshold(t){const s=[];return this.dataset.source.y.forEach((e,n)=>{Object.keys(t).some(a=>x[a]?.(e,t[a]))&&s.push(n)}),s}_rateOfChange(t,s){const e=[],n=this.dataset.source.y;for(let a=1;a<n.length;a++){const o=n[a-1],h=(n[a]-o)/Math.abs(o);X[t]?.(h,s)&&e.push(a)}return e}_findGaps(t,s,e){const n=[],a=this.dataset.source.x;let o=0,i=a.length;e?.[0]&&e?.[1]&&(o=e[0],i=e[1]);let h=a[o];for(let l=o+1;l<=i;l++){const f=a[l];f-h>t*A[s]*1e3&&n.push([l-1,l]),h=f}return n}_persistence(t,s){let e=[],n=this.dataset.source.y,a=0,o=n.length;s?.[0]&&s?.[1]&&(a=s[0],o=s[1]);let i=n[a],h=[];for(let l=a+1;l<o;l++)n[l]!=i||l===o?(h.length>=t&&(e=[...e,...h]),h=[]):h.push(l);return e}}E.ObservationRecord=j,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export declare enum EnumEditOperations {
|
|
2
|
+
ADD_POINTS = "ADD_POINTS",
|
|
3
|
+
CHANGE_VALUES = "CHANGE_VALUES",
|
|
4
|
+
DELETE_POINTS = "DELETE_POINTS",
|
|
5
|
+
DRIFT_CORRECTION = "DRIFT_CORRECTION",
|
|
6
|
+
INTERPOLATE = "INTERPOLATE",
|
|
7
|
+
SHIFT_DATETIMES = "SHIFT_DATETIMES",
|
|
8
|
+
FILL_GAPS = "FILL_GAPS"
|
|
9
|
+
}
|
|
10
|
+
export declare enum EnumFilterOperations {
|
|
11
|
+
FIND_GAPS = "FIND_GAPS",
|
|
12
|
+
PERSISTENCE = "PERSISTENCE",
|
|
13
|
+
RATE_OF_CHANGE = "RATE_OF_CHANGE",
|
|
14
|
+
VALUE_THRESHOLD = "VALUE_THRESHOLD"
|
|
15
|
+
}
|
|
16
|
+
export type HistoryItem = {
|
|
17
|
+
method: EnumEditOperations;
|
|
18
|
+
icon: string;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
args?: any[];
|
|
21
|
+
duration?: number;
|
|
22
|
+
status?: 'success' | 'failed';
|
|
23
|
+
};
|
|
24
|
+
export type EnumDictionary<T extends string | symbol | number, U> = {
|
|
25
|
+
[K in T]: U;
|
|
26
|
+
};
|
|
27
|
+
export declare enum FilterOperation {
|
|
28
|
+
LT = "Less than",
|
|
29
|
+
LTE = "Less than or equal to",
|
|
30
|
+
GT = "Greater than",
|
|
31
|
+
GTE = "Greater than or equal to",
|
|
32
|
+
E = "Equal",
|
|
33
|
+
START = "Start datetime",
|
|
34
|
+
END = "End datetime"
|
|
35
|
+
}
|
|
36
|
+
export declare const FilterOperationFn: EnumDictionary<FilterOperation, (value: number, toCompare: number) => boolean>;
|
|
37
|
+
export declare enum Operator {
|
|
38
|
+
ADD = "ADD",
|
|
39
|
+
SUB = "SUB",
|
|
40
|
+
MULT = "MULT",
|
|
41
|
+
DIV = "DIV",
|
|
42
|
+
ASSIGN = "ASSIGN"
|
|
43
|
+
}
|
|
44
|
+
export declare enum RateOfChangeOperation {
|
|
45
|
+
LT = "Less than",
|
|
46
|
+
LTE = "Less than or equal to",
|
|
47
|
+
GT = "Greater than",
|
|
48
|
+
GTE = "Greater than or equal to",
|
|
49
|
+
E = "Equal"
|
|
50
|
+
}
|
|
51
|
+
export declare const RateOfChangeComparator: EnumDictionary<RateOfChangeOperation, (value: number, toCompare: number) => boolean>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const DEFAULT_SNACK_DURATION = 3000;
|
|
2
|
+
export declare enum SnackColor {
|
|
3
|
+
Warning = "warning",
|
|
4
|
+
Success = "success",
|
|
5
|
+
Error = "error",
|
|
6
|
+
Info = "info"
|
|
7
|
+
}
|
|
8
|
+
export declare enum SnackIcon {
|
|
9
|
+
Success = "mdi-checkbox-marked-circle",
|
|
10
|
+
Warning = "mdi-alert",
|
|
11
|
+
Error = "mdi-alert-circle",
|
|
12
|
+
Info = "mdi-information",
|
|
13
|
+
None = "none"
|
|
14
|
+
}
|
|
15
|
+
export declare enum SnackTitle {
|
|
16
|
+
Warning = "Warning",
|
|
17
|
+
Success = "Success",
|
|
18
|
+
Error = "Error",
|
|
19
|
+
Info = "Info"
|
|
20
|
+
}
|
|
21
|
+
export declare enum Position {
|
|
22
|
+
Center = "center",
|
|
23
|
+
Left = "left",
|
|
24
|
+
Right = "right",
|
|
25
|
+
Bottom = "bottom",
|
|
26
|
+
Top = "top"
|
|
27
|
+
}
|
|
28
|
+
export declare class Snack {
|
|
29
|
+
message: string;
|
|
30
|
+
color: SnackColor;
|
|
31
|
+
icon: SnackIcon;
|
|
32
|
+
title: SnackTitle;
|
|
33
|
+
timeout: number;
|
|
34
|
+
position: Position;
|
|
35
|
+
visible: boolean;
|
|
36
|
+
constructor(message?: string, color?: SnackColor, icon?: SnackIcon, title?: SnackTitle, timeout?: number, position?: Position, visible?: boolean);
|
|
37
|
+
}
|
|
38
|
+
export declare class Snackbar {
|
|
39
|
+
private static subject;
|
|
40
|
+
static get snack$(): import("rxjs").Observable<Snack>;
|
|
41
|
+
private static createSnackbar;
|
|
42
|
+
static success(message: string): void;
|
|
43
|
+
static warn(message: string): void;
|
|
44
|
+
static error(message: string): void;
|
|
45
|
+
static info(message: string): void;
|
|
46
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { EnumDictionary, EnumEditOperations, EnumFilterOperations, HistoryItem } from '../../types';
|
|
2
|
+
export declare function subtractHours(timestamp: string, hours: number): string;
|
|
3
|
+
/** Returns the index of the first value that is greater or equal to the target value */
|
|
4
|
+
export declare const findFirstGreaterOrEqual: (array: number[] | Float64Array<SharedArrayBuffer>, target: number) => number;
|
|
5
|
+
/** Returns the index of the last value that is lesser or equal to the target value */
|
|
6
|
+
export declare const findLastLessOrEqual: (array: number[] | Float64Array<SharedArrayBuffer>, target: number) => number;
|
|
7
|
+
export declare const measureEllapsedTime: (fn: () => any, message?: string) => Promise<{
|
|
8
|
+
response: any;
|
|
9
|
+
duration: number;
|
|
10
|
+
}>;
|
|
11
|
+
export declare enum TimeUnit {
|
|
12
|
+
SECOND = "s",
|
|
13
|
+
MINUTE = "m",
|
|
14
|
+
HOUR = "h",
|
|
15
|
+
DAY = "D",
|
|
16
|
+
WEEK = "W",
|
|
17
|
+
MONTH = "M",
|
|
18
|
+
YEAR = "Y"
|
|
19
|
+
}
|
|
20
|
+
export declare const timeUnitMultipliers: EnumDictionary<TimeUnit, number>;
|
|
21
|
+
export declare const formatDate: (date: Date) => string;
|
|
22
|
+
export declare const formatDuration: (duration: number) => string;
|
|
23
|
+
export declare const shiftDatetime: (datetime: number, amount: number, unit: TimeUnit) => number;
|
|
24
|
+
/**
|
|
25
|
+
* This number should approximate the number of observations that a dataset could increase by during a session.
|
|
26
|
+
* The lower this number, the less memory the entire app uses.
|
|
27
|
+
* Note that when a dataset number of data points increases by more than `INCREASE_AMOUNT`,
|
|
28
|
+
* the `_growBuffer()` method will allocate a new buffer, and the data will be copied into it.
|
|
29
|
+
*/
|
|
30
|
+
export declare const INCREASE_AMOUNT: number;
|
|
31
|
+
export declare class ObservationRecord {
|
|
32
|
+
/** The generated dataset to be used for plotting */
|
|
33
|
+
dataset: {
|
|
34
|
+
dimensions: string[];
|
|
35
|
+
source: {
|
|
36
|
+
x: Float64Array<SharedArrayBuffer>;
|
|
37
|
+
y: Float32Array<SharedArrayBuffer>;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
history: HistoryItem[];
|
|
41
|
+
loadingTime: number | null;
|
|
42
|
+
isLoading: boolean;
|
|
43
|
+
rawData: {
|
|
44
|
+
datetimes: Float64Array<ArrayBuffer> | number[];
|
|
45
|
+
dataValues: Float32Array<ArrayBuffer> | number[];
|
|
46
|
+
};
|
|
47
|
+
constructor(dataArrays: {
|
|
48
|
+
datetimes: Float64Array<ArrayBuffer> | number[];
|
|
49
|
+
dataValues: Float32Array<ArrayBuffer> | number[];
|
|
50
|
+
});
|
|
51
|
+
loadData(dataArrays: {
|
|
52
|
+
datetimes: Float64Array<ArrayBuffer> | number[];
|
|
53
|
+
dataValues: Float32Array<ArrayBuffer> | number[];
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
get dataX(): Float64Array<SharedArrayBuffer>;
|
|
56
|
+
get dataY(): Float32Array<SharedArrayBuffer>;
|
|
57
|
+
/**
|
|
58
|
+
* Resizes the typed array
|
|
59
|
+
* @param length The total number of elements that the view will contain
|
|
60
|
+
*/
|
|
61
|
+
private _resizeTo;
|
|
62
|
+
/**
|
|
63
|
+
* Buffer size is always in increments of `INCREASE_AMOUNT`.
|
|
64
|
+
* Grows the buffer by `INCREASE_AMOUNT` in bytes if the current data doesn't fit
|
|
65
|
+
* @param newLength The total number of elements that the view will contain
|
|
66
|
+
*/
|
|
67
|
+
private _growBuffer;
|
|
68
|
+
/**
|
|
69
|
+
* Reloads the dataset with the raw data
|
|
70
|
+
*/
|
|
71
|
+
reload(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* @param index
|
|
74
|
+
* @returns
|
|
75
|
+
*/
|
|
76
|
+
reloadHistory(index: number): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Remove a history item
|
|
79
|
+
* @param index
|
|
80
|
+
*/
|
|
81
|
+
removeHistoryItem(index: number): Promise<void>;
|
|
82
|
+
get beginTime(): Date | null;
|
|
83
|
+
get endTime(): Date | null;
|
|
84
|
+
/** Dispatch an operation and log its signature in hisotry */
|
|
85
|
+
dispatch(action: EnumEditOperations | [EnumEditOperations, ...any][], ...args: any): Promise<any[]>;
|
|
86
|
+
/** Filter operations do not transform the data and are not logged in history */
|
|
87
|
+
dispatchFilter(action: EnumFilterOperations | [EnumFilterOperations, ...any][], ...args: any): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* @param index An array containing the list of index of values to perform the operations on.
|
|
90
|
+
* @param operator The operator that will be applied
|
|
91
|
+
* @param value The value to use in the operation
|
|
92
|
+
* @returns The modified DataFrame
|
|
93
|
+
*/
|
|
94
|
+
private _changeValues;
|
|
95
|
+
private _interpolate;
|
|
96
|
+
/** Interpolate existing values in the data source */
|
|
97
|
+
private _interpolateLinear;
|
|
98
|
+
/**
|
|
99
|
+
* Shifts the selected indexes by specified amount of units. Elements are reinserted according to their datetime.
|
|
100
|
+
* @param index The index of the elements to shift
|
|
101
|
+
* @param amount Number of {@link TimeUnit}
|
|
102
|
+
* @param unit {@link TimeUnit}
|
|
103
|
+
* @returns
|
|
104
|
+
*/
|
|
105
|
+
private _shift;
|
|
106
|
+
private _fillGapsV2;
|
|
107
|
+
/**
|
|
108
|
+
* Find gaps and fill them with placeholder value
|
|
109
|
+
* @param gap Intervals to detect as gaps
|
|
110
|
+
* @param fill Interval used to fill the detected gaps
|
|
111
|
+
* @param interpolateValues If true, the new values will be linearly interpolated
|
|
112
|
+
* @returns
|
|
113
|
+
*/
|
|
114
|
+
private _fillGaps;
|
|
115
|
+
/**
|
|
116
|
+
Deletes data points from a large array using worker threads.
|
|
117
|
+
1. The main thread divides the original array into equal parts to distribute work among workers.
|
|
118
|
+
2. For each segment, binary search locates the indexes to delete (deleteSegment), ensuring efficient lookups.
|
|
119
|
+
3. The cumulative deletions before each segment help compute the starting index (startTarget) for each worker's output, ensuring no overlap.
|
|
120
|
+
4. Each worker processes its segment linearly, skipping deletions and copying kept elements to their computed positions.
|
|
121
|
+
* @param deleteIndices
|
|
122
|
+
*/
|
|
123
|
+
private _deleteDataPoints;
|
|
124
|
+
/**
|
|
125
|
+
*
|
|
126
|
+
* @param start The start index
|
|
127
|
+
* @param end The end index
|
|
128
|
+
* @param value The drift amount
|
|
129
|
+
*/
|
|
130
|
+
private _driftCorrection;
|
|
131
|
+
/** Traverses the index array and returns groups of consecutive values.
|
|
132
|
+
* i.e.: `[0, 1, 3, 4, 6] => [[0, 1], [3, 4], [6]]`
|
|
133
|
+
* Assumes the input array is sorted.
|
|
134
|
+
* @param index: the index array (sorted)
|
|
135
|
+
*/
|
|
136
|
+
private _getConsecutiveGroups;
|
|
137
|
+
/**
|
|
138
|
+
* Adds data points. Their insert index is determined using `findFirstGreaterOrEqual` in the x-axis.
|
|
139
|
+
* @param dataPoints
|
|
140
|
+
*/
|
|
141
|
+
private _addDataPoints;
|
|
142
|
+
/**
|
|
143
|
+
* Filter by applying a set of logical operations
|
|
144
|
+
* @param appliedFilters
|
|
145
|
+
* @returns
|
|
146
|
+
*/
|
|
147
|
+
private _valueThreshold;
|
|
148
|
+
/**
|
|
149
|
+
*
|
|
150
|
+
* @param comparator
|
|
151
|
+
* @param value
|
|
152
|
+
* @returns
|
|
153
|
+
*/
|
|
154
|
+
private _rateOfChange;
|
|
155
|
+
/**
|
|
156
|
+
* Find gaps in the data
|
|
157
|
+
* @param value The time value
|
|
158
|
+
* @param unit The time unit (TimeUnit)
|
|
159
|
+
* @param range If specified, the gaps will be found only within the range
|
|
160
|
+
* @returns
|
|
161
|
+
*/
|
|
162
|
+
private _findGaps;
|
|
163
|
+
/**
|
|
164
|
+
* Find points where the values are the same at least x times in a row
|
|
165
|
+
* @param times The number of times in a row that points can be equal
|
|
166
|
+
* @param range If specified, the points will be found only within the range
|
|
167
|
+
* @returns
|
|
168
|
+
*/
|
|
169
|
+
private _persistence;
|
|
170
|
+
}
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uwrl/qc-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Quality Control Utilities",
|
|
5
5
|
"homepage": "https://github.com/hydroserver2/qc-utils#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
},
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"author": "Maurier Ramirez",
|
|
15
|
-
"type": "module",
|
|
16
15
|
"main": "./dist/index.cjs",
|
|
17
16
|
"module": "./dist/index.js",
|
|
18
17
|
"exports": {
|
|
@@ -21,11 +20,13 @@
|
|
|
21
20
|
"import": "./dist/index.js"
|
|
22
21
|
}
|
|
23
22
|
},
|
|
23
|
+
"types": "./dist/types.d.ts",
|
|
24
24
|
"files": [
|
|
25
25
|
"dist"
|
|
26
26
|
],
|
|
27
|
+
"type": "module",
|
|
27
28
|
"scripts": {
|
|
28
|
-
"build": "npm run clean:dist && vite build --mode prod",
|
|
29
|
+
"build": "npm run clean:dist && vite build --mode prod && vue-tsc --declaration --emitDeclarationOnly",
|
|
29
30
|
"pub": "npm publish --access public",
|
|
30
31
|
"clean:dist": "rimraf dist",
|
|
31
32
|
"clean:coverage": "rimraf coverage",
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"link": "npm link"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
41
|
+
"rxjs": "^7.8.2",
|
|
40
42
|
"vite": "^7.0.4"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
@@ -49,6 +51,7 @@
|
|
|
49
51
|
"stylelint": "^16.20.0",
|
|
50
52
|
"taze": "^19.1.0",
|
|
51
53
|
"typescript": "latest",
|
|
52
|
-
"vitest": "^3.2.4"
|
|
54
|
+
"vitest": "^3.2.4",
|
|
55
|
+
"vue-tsc": "^3.0.5"
|
|
53
56
|
}
|
|
54
57
|
}
|