UNPKG

@senx/warpview

Version:
1,420 lines (1,410 loc) 947 kB
import * as i0 from '@angular/core'; import { EventEmitter, Injectable, Component, ViewEncapsulation, ViewChild, Input, Output, Directive, HostListener, NgModule } from '@angular/core'; import * as i4 from '@angular/common'; import { CommonModule } from '@angular/common'; import moment from 'moment-timezone'; import { Subject, BehaviorSubject, forkJoin } from 'rxjs'; import { debounceTime, throttleTime, tap, catchError } from 'rxjs/operators'; import * as Plotlyjs from 'plotly.js-dist'; import { Plots } from 'plotly.js-dist'; import { of } from 'rxjs/internal/observable/of'; import * as i1 from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http'; import Leaflet from 'leaflet'; import 'leaflet.heat'; import 'leaflet.markercluster'; import deepEqual from 'deep-equal'; import { antPath } from 'leaflet-ant-path'; import moment$1 from 'moment'; import * as noUiSlider from 'nouislider'; import * as i5 from 'angular-resize-event'; import { AngularResizeEventModule } from 'angular-resize-event'; import * as i7 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import fitty from 'fitty'; import * as d3 from 'd3'; import { max, scaleBand, scaleLinear, easeLinear, timeDays, sum, range } from 'd3'; import { select, event } from 'd3-selection'; import eventDrops from 'event-drops'; /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ class DataModel { } /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* tslint:disable:no-console */ class Logger { constructor(className, isDebug = false) { this.isDebug = false; this.className = className.name; this.isDebug = isDebug; } setDebug(debug) { this.isDebug = debug; } log(level, methods, args) { let logChain = []; logChain.push(`[${new Date().toISOString()} - [${this.className}] ${methods.join(' - ')}`); logChain = logChain.concat(args); switch (level) { case LEVEL.DEBUG: { if (this.isDebug) { console.debug(...logChain); } break; } case LEVEL.ERROR: { console.error(...logChain); break; } case LEVEL.INFO: { console.log(...logChain); break; } case LEVEL.WARN: { console.warn(...logChain); break; } default: { if (this.isDebug) { console.log(...logChain); } } } } debug(methods, ...args) { this.log(LEVEL.DEBUG, methods, args); } error(methods, ...args) { this.log(LEVEL.ERROR, methods, args); } warn(methods, ...args) { this.log(LEVEL.WARN, methods, args); } info(methods, ...args) { this.log(LEVEL.INFO, methods, args); } } var LEVEL; (function (LEVEL) { LEVEL[LEVEL["DEBUG"] = 0] = "DEBUG"; LEVEL[LEVEL["ERROR"] = 1] = "ERROR"; LEVEL[LEVEL["WARN"] = 2] = "WARN"; LEVEL[LEVEL["INFO"] = 3] = "INFO"; })(LEVEL || (LEVEL = {})); /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // adapted from Canop's JSON,parseMore https://github.com/Canop/JSON.parseMore/ class JsonLib { constructor() { this.escapee = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' }; } error(m) { throw { name: 'SyntaxError', message: m, at: this.at, text: this.text }; } next() { return this.ch = this.text.charAt(this.at++); } check(c) { if (c !== this.ch) { this.error('Expected \'' + c + '\' instead of \'' + this.ch + '\''); } this.ch = this.text.charAt(this.at++); } number() { let string = ''; if (this.ch === '-') { string = '-'; this.check('-'); } if (this.ch === 'I') { this.check('I'); this.check('n'); this.check('f'); this.check('i'); this.check('n'); this.check('i'); this.check('t'); this.check('y'); return -Infinity; } while (this.ch >= '0' && this.ch <= '9') { string += this.ch; this.next(); } if (this.ch === '.') { string += '.'; while (this.next() && this.ch >= '0' && this.ch <= '9') { string += this.ch; } } if (this.ch === 'e' || this.ch === 'E') { string += this.ch; this.next(); if (this.ch === '-' || this.ch === '+') { string += this.ch; this.next(); } while (this.ch >= '0' && this.ch <= '9') { string += this.ch; this.next(); } } return +string; } string() { let hex; let string = ''; let uffff; if (this.ch === '"') { while (this.next()) { if (this.ch === '"') { this.next(); return string; } if (this.ch === '\\') { this.next(); if (this.ch === 'u') { uffff = 0; for (let i = 0; i < 4; i++) { hex = parseInt(this.next(), 16); if (!isFinite(hex)) { break; } uffff = uffff * 16 + hex; } string += String.fromCharCode(uffff); } else if (this.escapee[this.ch]) { string += this.escapee[this.ch]; } else { break; } } else { string += this.ch; } } } this.error('Bad string'); } white() { while (this.ch && this.ch <= ' ') { this.next(); } } word() { switch (this.ch) { case 't': this.check('t'); this.check('r'); this.check('u'); this.check('e'); return true; case 'f': this.check('f'); this.check('a'); this.check('l'); this.check('s'); this.check('e'); return false; case 'n': this.check('n'); this.check('u'); this.check('l'); this.check('l'); return null; case 'N': this.check('N'); this.check('a'); this.check('N'); return NaN; case 'I': this.check('I'); this.check('n'); this.check('f'); this.check('i'); this.check('n'); this.check('i'); this.check('t'); this.check('y'); return Infinity; } this.error('Unexpected \'' + this.ch + '\''); } array() { const array = []; if (this.ch === '[') { this.check('['); this.white(); if (this.ch === ']') { this.check(']'); return array; // empty array } while (this.ch) { array.push(this.value()); this.white(); if (this.ch === ']') { this.check(']'); return array; } this.check(','); this.white(); } } this.error('Bad array'); } object() { let key; const object = {}; if (this.ch === '{') { this.check('{'); this.white(); if (this.ch === '}') { this.check('}'); return object; // empty object } while (this.ch) { key = this.string(); this.white(); this.check(':'); if (Object.hasOwnProperty.call(object, key)) { this.error('Duplicate key "' + key + '"'); } object[key] = this.value(); this.white(); if (this.ch === '}') { this.check('}'); return object; } this.check(','); this.white(); } } this.error('Bad object'); } value() { this.white(); switch (this.ch) { case '{': return this.object(); case '[': return this.array(); case '"': return this.string(); case '-': return this.number(); default: return this.ch >= '0' && this.ch <= '9' ? this.number() : this.word(); } } parse(source, reviver) { let result; this.text = source; this.at = 0; this.ch = ' '; result = this.value(); this.white(); if (this.ch) { this.error('Syntax error'); } return typeof reviver === 'function' ? (function walk(holder, key) { let k; let v; const value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); }({ '': result }, '')) : result; } } /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // @dynamic class GTSLib { static cleanArray(actual) { return actual.filter((i) => !!i); } static unique(arr) { const u = {}; const a = []; for (let i = 0, l = arr.length; i < l; ++i) { if (!u.hasOwnProperty(arr[i])) { a.push(arr[i]); u[arr[i]] = 1; } } return a; } static isArray(value) { return value && typeof value === 'object' && value instanceof Array && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length')); } static formatElapsedTime(elapsed) { if (elapsed < 1000) { return elapsed.toFixed(3) + ' ns'; } if (elapsed < 1000000) { return (elapsed / 1000).toFixed(3) + ' μs'; } if (elapsed < 1000000000) { return (elapsed / 1000000).toFixed(3) + ' ms'; } if (elapsed < 1000000000000) { return (elapsed / 1000000000).toFixed(3) + ' s '; } // Max exec time for nice output: 999.999 minutes (should be OK, timeout should happen before that). return (elapsed / 60000000000).toFixed(3) + ' m '; } static isValidResponse(data) { let response; try { response = new JsonLib().parse(data); } catch (e) { this.LOG.error(['isValidResponse'], 'Response non JSON compliant', data); return false; } if (!GTSLib.isArray(response)) { this.LOG.error(['isValidResponse'], 'Response isn\'t an Array', response); return false; } return true; } static isEmbeddedImage(item) { return !(typeof item !== 'string' || !/^data:image/.test(item)); } static isEmbeddedImageObject(item) { return !((item === null) || (item.image === null) || (item.caption === null) || !GTSLib.isEmbeddedImage(item.image)); } static isPositionArray(item) { if (!item || !item.positions) { return false; } if (GTSLib.isPositionsArrayWithValues(item) || GTSLib.isPositionsArrayWithTwoValues(item)) { return true; } (item.positions || []).forEach(p => { if (p.length < 2 || p.length > 3) { return false; } for (const j in p) { if (typeof p[j] !== 'number') { return false; } } }); return true; } static isPositionsArrayWithValues(item) { if ((item === null) || (item.positions === null)) { return false; } (item.positions || []).forEach(p => { if (p.length !== 3) { return false; } for (const j in p) { if (typeof p[j] !== 'number') { return false; } } }); return true; } static isPositionsArrayWithTwoValues(item) { if ((item === null) || (item.positions === null)) { return false; } (item.positions || []).forEach(p => { if (p.length !== 4) { return false; } for (const j in p) { if (typeof p[j] !== 'number') { return false; } } }); return true; } static gtsFromJSON(json, id) { return { gts: { c: json.c, l: json.l, a: json.a, v: json.v, id } }; } static uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { // tslint:disable-next-line:no-bitwise const r = Math.random() * 16 | 0; // tslint:disable-next-line:no-bitwise const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } static gtsFromJSONList(jsonList, prefixId) { const gtsList = []; let id; (jsonList || []).forEach((item, i) => { let gts = item; if (item.gts) { gts = item.gts; } if ((prefixId !== undefined) && (prefixId !== '')) { id = prefixId + '-' + i; } else { id = i; } if (GTSLib.isArray(gts)) { gtsList.push(GTSLib.gtsFromJSONList(gts, id)); } if (GTSLib.isGts(gts)) { gtsList.push(GTSLib.gtsFromJSON(gts, id)); } if (GTSLib.isEmbeddedImage(gts)) { gtsList.push({ image: gts, caption: 'Image', id }); } if (GTSLib.isEmbeddedImageObject(gts)) { gtsList.push({ image: gts.image, caption: gts.caption, id }); } }); return { content: gtsList || [], }; } static flatDeep(arrays) { const result = []; if (!GTSLib.isArray(arrays)) { arrays = [arrays]; } GTSLib.flatten(arrays, result); return result; } static flatten(arr, result = []) { const size = arr.length; for (let i = 0; i < size; i++) { const value = arr[i]; if (Array.isArray(value)) { GTSLib.flatten(value, result); } else { result.push(value); } } return result; } static flattenGtsIdArray(a, r) { const res = []; if (GTSLib.isGts(a)) { a = [a]; } (a || []).forEach(d => { if (GTSLib.isArray(d)) { const walk = GTSLib.flattenGtsIdArray(d, r); res.push(walk.res); r = walk.r; } else if (d && d.v) { d.id = r; res.push(d); r++; } }); return { res, r }; } static sanitizeNames(input) { return (input || '').replace(/{/g, '&#123;') .replace(/}/g, '&#125;') .replace(/,/g, '&#44;') .replace(/>/g, '&#62;') .replace(/</g, '&#60;') .replace(/"/g, '&#34;') .replace(/'/g, '&#39;'); } static serializeGtsMetadata(gts) { const serializedLabels = []; const serializedAttributes = []; if (gts.l) { Object.keys(gts.l).forEach((key) => { serializedLabels.push(this.sanitizeNames(key + '=' + gts.l[key])); }); } if (gts.a) { Object.keys(gts.a).forEach((key) => { serializedAttributes.push(this.sanitizeNames(key + '=' + gts.a[key])); }); } // tslint:disable-next-line:max-line-length return `${this.sanitizeNames(gts.c)}{${serializedLabels.join(',')}${serializedAttributes.length > 0 ? ',' : ''}${serializedAttributes.join(',')}}`; } static isGts(item) { return !!item && (item.c === '' || !!item.c) && !!item.v && GTSLib.isArray(item.v); } static isGtsToPlot(gts) { if (!GTSLib.isGts(gts) || gts.v.length === 0) { return false; } // We look at the first non-null value, if it's a String or Boolean it's an annotation GTS, // if it's a number it's a GTS to plot return (gts.v || []).some(v => { // noinspection JSPotentiallyInvalidConstructorUsage return typeof v[v.length - 1] === 'number' || !!v[v.length - 1].constructor.prototype.toFixed; }); } static isGtsToPlotOnMap(gts) { if (!GTSLib.isGts(gts) || gts.v.length === 0) { return false; } // We look at the first non-null value, if it's a String or Boolean it's an annotation GTS, // if it's a number it's a GTS to plot return (gts.v || []).some(v => { return v.length >= 3; }); } static isSingletonGTS(gts) { if (!GTSLib.isGts(gts) || gts.v.length === 0) { return false; } return (gts.v || []).length === 1; } static isGtsToAnnotate(gts) { if (!GTSLib.isGts(gts) || gts.v.length === 0) { return false; } // We look at the first non-null value, if it's a String or Boolean it's an annotation GTS, // if it's a number it's a GTS to plot return (gts.v || []).some(v => { if (v[v.length - 1] !== null) { // noinspection JSPotentiallyInvalidConstructorUsage return typeof (v[v.length - 1]) !== 'number' && (!!v[v.length - 1].constructor && v[v.length - 1].constructor.name !== 'Big') && v[v.length - 1].constructor.prototype.toFixed === undefined; } }); } static gtsSort(gts) { if (gts.isSorted) { return; } gts.v = gts.v.sort((a, b) => a[0] - b[0]); gts.isSorted = true; } static getData(data) { if (typeof data === 'string') { if (data.startsWith('[') || data.startsWith('{')) { return GTSLib.getData(new JsonLib().parse(data)); } else { return { data: new JsonLib().parse(`[${data}]`) }; } } else if (data && data.hasOwnProperty('data')) { if (!GTSLib.isArray(data.data)) { data.data = [data.data]; } return data; } else if (GTSLib.isArray(data) && data.length > 0 && data[0].data) { return data[0]; } else if (GTSLib.isArray(data)) { return { data: data }; } return new DataModel(); } static getDivider(timeUnit) { let timestampDivider = 1000; // default for µs timeunit if (timeUnit === 'ms') { timestampDivider = 1; } if (timeUnit === 'ns') { timestampDivider = 1000000; } return timestampDivider; } static formatLabel(data) { const serializedGTS = data.split('{'); let display = `<span class="gtsInfo"><span class='gts-classname'>${serializedGTS[0]}</span>`; if (serializedGTS.length > 1) { display += `<span class='gts-separator'>{</span>`; const labels = serializedGTS[1].substr(0, serializedGTS[1].length - 1).split(','); if (labels.length > 0) { labels.forEach((l, i) => { const label = l.split('='); if (l.length > 1) { display += `<span><span class='gts-labelname'>${label[0]}</span> <span class='gts-separator'>=</span><span class='gts-labelvalue'>${label[1]}</span>`; if (i !== labels.length - 1) { display += `<span>, </span>`; } } }); } display += `<span class='gts-separator'>}</span>`; } if (serializedGTS.length > 2) { display += `<span class='gts-separator'>{</span>`; const labels = serializedGTS[2].substr(0, serializedGTS[2].length - 1).split(','); if (labels.length > 0) { labels.forEach((l, i) => { const label = l.split('='); if (l.length > 1) { display += `<span><span class='gts-attrname'>${label[0]}</span> <span class='gts-separator'>=</span><span class='gts-attrvalue'>${label[1]}</span>`; if (i !== labels.length - 1) { display += `<span>, </span>`; } } }); } display += `<span class='gts-separator'>}</span>`; } display += '</span>'; return display; } static toISOString(timestamp, divider, timeZone) { if (timeZone !== 'UTC') { return moment.tz(timestamp / divider, timeZone).format(); } else { return moment.utc(timestamp / divider).toISOString(); } } static toTimestamp(date, divider, timeZone) { if (timeZone !== 'UTC') { return moment.tz(date, timeZone).utc().valueOf() * divider; } else { return moment.utc(date).valueOf() * divider; } } } GTSLib.LOG = new Logger(GTSLib); /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ class ChartBounds { constructor() { this.tsmin = 0; this.tsmax = 0; this.msmin = ''; this.msmax = ''; this.marginLeft = 0; } } /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var Colors; (function (Colors) { Colors["COHESIVE"] = "COHESIVE"; Colors["COHESIVE_2"] = "COHESIVE_2"; Colors["BELIZE"] = "BELIZE"; Colors["VIRIDIS"] = "VIRIDIS"; Colors["MAGMA"] = "MAGMA"; Colors["INFERNO"] = "INFERNO"; Colors["PLASMA"] = "PLASMA"; Colors["YL_OR_RD"] = "YL_OR_RD"; Colors["YL_GN_BU"] = "YL_GN_BU"; Colors["BU_GN"] = "BU_GN"; Colors["WARP10"] = "WARP10"; Colors["NINETEEN_EIGHTY_FOUR"] = "NINETEEN_EIGHTY_FOUR"; Colors["ATLANTIS"] = "ATLANTIS"; Colors["DO_ANDROIDS_DREAM"] = "DO_ANDROIDS_DREAM"; Colors["DELOREAN"] = "DELOREAN"; Colors["CTHULHU"] = "CTHULHU"; Colors["ECTOPLASM"] = "ECTOPLASM"; Colors["T_MAX_400_FILM"] = "T_MAX_400_FILM"; })(Colors || (Colors = {})); class ColorLib { static getColor(i, scheme) { if (!ColorLib.color[scheme]) { scheme = 'WARP10'; } return ColorLib.color[scheme][i % 2 === 0 ? i % ColorLib.color[scheme].length : ColorLib.color[scheme].length - i % ColorLib.color[scheme].length]; } static getColorGradient(id, scheme) { return [ [0, ColorLib.transparentize(ColorLib.getColor(id, scheme), 0)], [1, ColorLib.transparentize(ColorLib.getColor(id, scheme), 0.7)] ]; } static getBlendedColorGradient(id, scheme, bg = '#000000') { return [ [0, ColorLib.blend_colors(bg, ColorLib.getColor(id, scheme), 0)], [1, ColorLib.blend_colors(bg, ColorLib.getColor(id, scheme), 1)] ]; } static hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16) ] : null; } static transparentize(color, alpha = 0.5) { return 'rgba(' + ColorLib.hexToRgb(color).concat(alpha).join(',') + ')'; } static generateTransparentColors(num, scheme) { const color = []; for (let i = 0; i < num; i++) { color.push(ColorLib.transparentize(ColorLib.getColor(i, scheme))); } return color; } static hsvGradientFromRgbColors(c1, c2, steps) { const c1hsv = ColorLib.rgb2hsv(c1.r, c1.g, c1.b); const c2hsv = ColorLib.rgb2hsv(c2.r, c2.g, c2.b); c1.h = c1hsv[0]; c1.s = c1hsv[1]; c1.v = c1hsv[2]; c2.h = c2hsv[0]; c2.s = c2hsv[1]; c2.v = c2hsv[2]; const gradient = ColorLib.hsvGradient(c1, c2, steps); for (const i in gradient) { if (gradient[i]) { gradient[i].rgb = ColorLib.hsv2rgb(gradient[i].h, gradient[i].s, gradient[i].v); gradient[i].r = Math.floor(gradient[i].rgb[0]); gradient[i].g = Math.floor(gradient[i].rgb[1]); gradient[i].b = Math.floor(gradient[i].rgb[2]); } } return gradient; } static rgb2hsv(r, g, b) { // Normalize const normR = r / 255.0; const normG = g / 255.0; const normB = b / 255.0; const M = Math.max(normR, normG, normB); const m = Math.min(normR, normG, normB); const d = M - m; let h; let s; let v; v = M; if (d === 0) { h = 0; s = 0; } else { s = d / v; switch (M) { case normR: h = ((normG - normB) + d * (normG < normB ? 6 : 0)) / 6 * d; break; case normG: h = ((normB - normR) + d * 2) / 6 * d; break; case normB: h = ((normR - normG) + d * 4) / 6 * d; break; } } return [h, s, v]; } static hsvGradient(c1, c2, steps) { const gradient = new Array(steps); // determine clockwise and counter-clockwise distance between hues const distCCW = (c1.h >= c2.h) ? c1.h - c2.h : 1 + c1.h - c2.h; const distCW = (c1.h >= c2.h) ? 1 + c2.h - c1.h : c2.h - c1.h; // make gradient for this part for (let i = 0; i < steps; i++) { // interpolate h, s, b let h = (distCW <= distCCW) ? c1.h + (distCW * i / (steps - 1)) : c1.h - (distCCW * i / (steps - 1)); if (h < 0) { h = 1 + h; } if (h > 1) { h = h - 1; } const s = (1 - i / (steps - 1)) * c1.s + i / (steps - 1) * c2.s; const v = (1 - i / (steps - 1)) * c1.v + i / (steps - 1) * c2.v; // add to gradient array gradient[i] = { h, s, v }; } return gradient; } static hsv2rgb(h, s, v) { let r; let g; let b; const i = Math.floor(h * 6); const f = h * 6 - i; const p = v * (1 - s); const q = v * (1 - f * s); const t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } return [r * 255, g * 255, b * 255]; } static rgb2hex(r, g, b) { function componentToHex(c) { const hex = c.toString(16); return hex.length === 1 ? '0' + hex : hex; } return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b); } static blend_colors(color1, color2, percentage) { // check input color1 = color1 || '#000000'; color2 = color2 || '#ffffff'; percentage = percentage || 0.5; // 1: validate input, make sure we have provided a valid hex if (color1.length !== 4 && color1.length !== 7) { throw new Error('colors must be provided as hexes'); } if (color2.length !== 4 && color2.length !== 7) { throw new Error('colors must be provided as hexes'); } if (percentage > 1 || percentage < 0) { throw new Error('percentage must be between 0 and 1'); } // 2: check to see if we need to convert 3 char hex to 6 char hex, else slice off hash // the three character hex is just a representation of the 6 hex where each character is repeated // ie: #060 => #006600 (green) if (color1.length === 4) { color1 = color1[1] + color1[1] + color1[2] + color1[2] + color1[3] + color1[3]; } else { color1 = color1.substring(1); } if (color2.length === 4) { color2 = color2[1] + color2[1] + color2[2] + color2[2] + color2[3] + color2[3]; } else { color2 = color2.substring(1); } // 3: we have valid input, convert colors to rgb color1 = [parseInt(color1[0] + color1[1], 16), parseInt(color1[2] + color1[3], 16), parseInt(color1[4] + color1[5], 16)]; color2 = [parseInt(color2[0] + color2[1], 16), parseInt(color2[2] + color2[3], 16), parseInt(color2[4] + color2[5], 16)]; // 4: blend const color3 = [ (1 - percentage) * color1[0] + percentage * color2[0], (1 - percentage) * color1[1] + percentage * color2[1], (1 - percentage) * color1[2] + percentage * color2[2] ]; // return hex return '#' + ColorLib.int_to_hex(color3[0]) + ColorLib.int_to_hex(color3[1]) + ColorLib.int_to_hex(color3[2]); } /* convert a Number to a two character hex string must round, or we will end up with more digits than expected (2) note: can also result in single digit, which will need to be padded with a 0 to the left @param: num => the number to conver to hex @returns: string => the hex representation of the provided number */ static int_to_hex(num) { let hex = Math.round(num).toString(16); if (hex.length === 1) { hex = '0' + hex; } return hex; } } ColorLib.color = { COHESIVE: ['#F2D354', '#E4612F', '#D32C2E', '#6D2627', '#6C7F55', '#934FC6', '#F07A5D', '#ED8371', '#94E751', '#C457F7', '#973AF7', '#B6FF7A', '#C7FFD5', '#90E4D0', '#E09234', '#D2FF91', '#17B201'], COHESIVE_2: ['#6F694E', '#65D0B2', '#D8F546', '#FF724B', '#D6523E', '#F9F470', '#F4BC78', '#B1D637', '#FFCFC8', '#56CDAB', '#CFDD22', '#B3F5D2', '#97DB29', '#9DC5EE', '#CFC0F5', '#EDEA29', '#5EC027', '#386C94'], BELIZE: ['#5899DA', '#E8743B', '#19A979', '#ED4A7B', '#945ECF', '#13A4B4', '#525DF4', '#BF399E', '#6C8893', '#EE6868', '#2F6497'], VIRIDIS: ['#440154', '#481f70', '#443983', '#3b528b', '#31688e', '#287c8e', '#21918c', '#20a486', '#35b779', '#5ec962', '#90d743', '#c8e020'], MAGMA: ['#000004', '#100b2d', '#2c115f', '#51127c', '#721f81', '#932b80', '#b73779', '#d8456c', '#f1605d', '#fc8961', '#feb078', '#fed799'], INFERNO: ['#000004', '#110a30', '#320a5e', '#57106e', '#781c6d', '#9a2865', '#bc3754', '#d84c3e', '#ed6925', '#f98e09', '#fbb61a', '#f4df53'], PLASMA: ['#0d0887', '#3a049a', '#5c01a6', '#7e03a8', '#9c179e', '#b52f8c', '#cc4778', '#de5f65', '#ed7953', '#f89540', '#fdb42f', '#fbd524'], YL_OR_RD: ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'], YL_GN_BU: ['#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58'], BU_GN: ['#f7fcfd', '#ebf7fa', '#dcf2f2', '#c8eae4', '#aadfd2', '#88d1bc', '#68c2a3', '#4eb485', '#37a266', '#228c49', '#0d7635', '#025f27'], WARP10: [ '#ff9900', '#E53935', '#F4511E', '#D81B60', '#00ACC1', '#1E88E5', '#43A047', '#FFB300', '#6D4C41', '#FDD835', '#00897B', '#3949AB', '#5E35B1', '#8E24AA', '#C0CA33', '#039BE5', '#7CB342', '#004eff' ], NINETEEN_EIGHTY_FOUR: ['#fc9ca2', '#fb747d', '#fa4c58', '#f92432', '#e30613', '#c70512', '#9f040e', '#77030b', '#500207'], ATLANTIS: ['#edf2fb', '#e2eafc', '#d7e3fc', '#ccdbfd', '#c1d3fe', '#b6ccfe', '#abc4ff'], DO_ANDROIDS_DREAM: ['#d8f3dc', '#b7e4c7', '#95d5b2', '#74c69d', '#52b788', '#40916c', '#2d6a4f', '#1b4332', '#081c15'], DELOREAN: ['#b98b73', '#cb997e', '#ddbea9', '#ffe8d6', '#d4c7b0', '#b7b7a4', '#a5a58d', '#6b705c', '#3f4238'], CTHULHU: ['#004c6d', '#006083', '#007599', '#008bad', '#00a1c1', '#00b8d3', '#00cfe3', '#00e7f2', '#00ffff'], ECTOPLASM: ['#006466', '#065a60', '#0b525b', '#144552', '#1b3a4b', '#212f45', '#272640', '#312244', '#3e1f47', '#4d194d'], T_MAX_400_FILM: ['#f8f9fa', '#e9ecef', '#dee2e6', '#ced4da', '#adb5bd', '#6c757d', '#495057', '#343a40', '#212529'], MATRIX: ['#025f27', '#025f27', '#0d7635', '#228c49', '#37a266', '#4eb485', '#68c2a3', '#88d1bc'], CHARTANA: ['#77BE69', '#FADE2B', '#F24865', '#5694F2', '#FF9830', '#B876D9'], }; /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // @dynamic class ChartLib { static mergeDeep(base, ext) { const obj = { ...base }; const extended = { ...ext }; for (const prop in extended || {}) { // If property is an object, merge properties if (Object.prototype.toString.call(extended[prop]) === '[object Object]') { obj[prop] = ChartLib.mergeDeep(obj[prop], extended[prop]); } else { obj[prop] = extended[prop]; } } return obj; } static fraction2r(rl0, rl1, v) { return v * (rl1 - rl0); } } ChartLib.DEFAULT_WIDTH = 640; ChartLib.DEFAULT_HEIGHT = 480; /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ class Param { constructor() { this.scheme = 'WARP10'; this.showlegend = false; this.horizontal = false; this.stacked = false; this.showControls = true; this.showErrors = true; this.showStatus = false; this.expandAnnotation = false; this.showDots = false; this.timeUnit = 'us'; this.timeZone = 'UTC'; this.map = { tiles: [], showTimeSlider: false, showTimeRange: false, timeSliderMode: 'timestamp' }; this.bounds = {}; } } /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ class Size { constructor(width, height) { this.width = width; this.height = height; } } class SizeService { constructor() { this.debouncer = new Subject(); this.sizeChanged$ = new EventEmitter(); this.debouncer .pipe(debounceTime(100)) .subscribe((value) => this.sizeChanged$.emit(value)); } change(size) { this.debouncer.next(size); } } SizeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: SizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); SizeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: SizeService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.1", ngImport: i0, type: SizeService, decorators: [{ type: Injectable }], ctorParameters: function () { return []; } }); /* * Copyright 2021 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ class PlotlyComponent { constructor(iterableDiffers, el, keyValueDiffers) { this.iterableDiffers = iterableDiffers; this.el = el; this.keyValueDiffers = keyValueDiffers; this.defaultClassName = 'js-plotly-plot'; this._debug = false; this.revision = 0; this.useResizeHandler = false; this.updateOnLayoutChange = true; this.updateOnDataChange = true; this.updateOnlyWithRevision = true; this.initialized = new EventEmitter(); this.update = new EventEmitter(); this.purge = new EventEmitter(); // tslint:disable-next-line:no-output-native this.error = new EventEmitter(); this.afterExport = new EventEmitter(); this.afterPlot = new EventEmitter(); this.animated = new EventEmitter(); this.animatingFrame = new EventEmitter(); this.animationInterrupted = new EventEmitter(); this.autoSize = new EventEmitter(); this.beforeExport = new EventEmitter(); this.buttonClicked = new EventEmitter(); // tslint:disable-next-line:no-output-native this.click = new EventEmitter(); this.plotly_click = new EventEmitter(); this.clickAnnotation = new EventEmitter(); this.deselect = new EventEmitter(); this.doubleClick = new EventEmitter(); this.framework = new EventEmitter(); this.hover = new EventEmitter(); this.legendClick = new EventEmitter(); this.legendDoubleClick = new EventEmitter(); this.relayout = new EventEmitter(); this.restyle = new EventEmitter(); this.redraw = new EventEmitter(); this.selected = new EventEmitter(); this.selecting = new EventEmitter(); this.sliderChange = new EventEmitter(); this.sliderEnd = new EventEmitter(); this.sliderStart = new EventEmitter(); this.transitioning = new EventEmitter(); this.transitionInterrupted = new EventEmitter(); this.unhover = new EventEmitter(); this.relayouting = new EventEmitter(); this.eventNames = [ // 'afterExport', // 'afterPlot', // 'animated', 'animatingFrame', 'animationInterrupted', 'autoSize', // 'beforeExport', 'buttonClicked', 'clickAnnotation', 'deselect', 'doubleClick', 'framework', 'hover', 'unhover', // 'legendClick', 'legendDoubleClick', 'relayout', // 'restyle', 'redraw', 'selected', 'selecting', 'sliderChange', // 'sliderEnd', 'sliderStart', 'transitioning', 'transitionInterrupted', 'relayouting' ]; this.LOG = new Logger(PlotlyComponent, this._debug); } set data(data) { this._data = data; this.LOG.debug(['PlotlyComponent'], data); this.updatePlot(); this.LOG.debug(['PlotlyComponent'], 'after updatePlot'); this.updateWindowResizeHandler(); this.LOG.debug(['PlotlyComponent'], 'after updateWindowResizeHandler'); } get data() { return this._data; } set layout(layout) { this._layout = { ...layout }; if (!!this._data && !!this.plotEl.nativeElement) { try { Plotlyjs.relayout(this.plotEl.nativeElement, this._layout); this.updateWindowResizeHandler(); } catch (e) { // } } } get layout() { return this._layout; } set config(config) { this._config = config; this.updatePlot(); this.updateWindowResizeHandler(); } get config() { return this._config; } set debug(debug) { if (typeof debug === 'string') { debug = 'true' === debug; } this._debug = debug; this.LOG.setDebug(debug); } get debug() { return this._debug; } ngOnInit() { this.createPlot().then(() => { const figure = this.createFigure(); this.LOG.debug(['figure'], figure); this.initialized.emit(this.plotlyInstance); }); } ngOnDestroy() { if (typeof this.resizeHandler === 'function') { this.getWindow().removeEventListener('resize', this.resizeHandler); this.resizeHandler = undefined; } const figure = this.createFigure(); this.purge.emit(figure); this.remove(this.plotlyInstance); } ngDoCheck() { if (this.updateOnlyWithRevision) { return false; } let shouldUpdate = false; if (this.updateOnLayoutChange) { if (this.layoutDiffer) { const layoutHasDiff = this.layoutDiffer.diff(this._layout); if (layoutHasDiff) { shouldUpdate = true; } } else if (this._layout) { this.layoutDiffer = this.keyValueDiffers.find(this._layout).create(); } else { this.layoutDiffer = undefined; } } if (this.updateOnDataChange) { if (this.dataDiffer) { const dataHasDiff = this.dataDiffer.diff(this._data); if (dataHasDiff) { shouldUpdate = true; } } else if (Array.isArray(this._data)) { this.dataDiffer = this.iterableDiffers.find(this._data).create(this.dataDifferTrackBy); } else { this.dataDiffer = undefined; } } if (shouldUpdate && this.plotlyInstance) { this.updatePlot(); } } getWindow() { return window; } getBoundingClientRect() { return this.rect; } getClassName() { let classes = [this.defaultClassName]; if (Array.isArray(this.className)) { classes = classes.concat(this.className); } else if (!!this.className) { classes.push(this.className); } return classes.join(' '); } restyleChart(properties, curves) { Plotlyjs.restyle(this.plotlyInstance, properties, curves); } createPlot() { this.LOG.debug(['createPlot']); return Plotlyjs.react(this.plotEl.nativeElement, this._data, this._layout, this._config) .then(plotlyInstance => { this.LOG.debug(['plotlyInstance'], plotlyInstance); this.rect = this.el.nativeElement.getBoundingClientRect(); this.plotlyInstance = plotlyInstance; this.getWindow().gd = this._debug ? plotlyInstance : undefined; this.eventNames.forEach(name => { const eventName = `plotly_${name.toLowerCase()}`; // @ts-ignore plotlyInstance.on(eventName, (data) => { this.LOG.debug(['plotlyEvent', eventName], data); this[name].emit(data); }); }); plotlyInstance.on('plotly_click', (data) => { this.