@seismo/core
Version:
This is the package for the core library of Seismo, a JavaScript library for seismic data processing and visualization. It provides utilities for handling seismic data, including FDSN web services, waveform processing, and event handling. The library is d
480 lines (479 loc) • 14.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AlignmentLinkedTimeScale = exports.LinkedTimeScale = exports.FixedHalfWidthAmplitudeScale = exports.IndividualAmplitudeScale = exports.LinkedAmplitudeScale = exports.TimeScalable = exports.AmplitudeScalable = exports.MinMaxable = exports.AMPLITUDE_MODE = void 0;
const utils_1 = require("../utils");
const luxon_1 = require("luxon");
/** enum for amplitude modes, RAW, ZERO, MINMAX, MEAN */
var AMPLITUDE_MODE;
(function (AMPLITUDE_MODE) {
AMPLITUDE_MODE["Raw"] = "raw";
AMPLITUDE_MODE["Zero"] = "zero";
AMPLITUDE_MODE["MinMax"] = "minmax";
AMPLITUDE_MODE["Mean"] = "mean";
})(AMPLITUDE_MODE || (exports.AMPLITUDE_MODE = AMPLITUDE_MODE = {}));
let _lastId = 0;
class MinMaxable {
min;
max;
constructor(min, max) {
this.min = min;
this.max = max;
}
get middle() {
return (this.min + this.max) / 2;
}
get halfWidth() {
return this.fullWidth / 2;
}
get fullWidth() {
return this.max - this.min;
}
union(omm) {
if (omm) {
return new MinMaxable(Math.min(this.min, omm.min), Math.max(this.max, omm.max));
}
else {
return this;
}
}
expandPercentage(percent) {
return MinMaxable.fromMiddleHalfWidth(this.middle, this.halfWidth * percent);
}
/**
* This as a d3 style 2 element array.
*
* @returns length 2 array of min then max
*/
asArray() {
return [this.min, this.max];
}
toString() {
return `${this.min} to ${this.max}, mid: ${this.middle} hw: ${this.halfWidth}`;
}
/**
* Create MinMaxable from a d3 style two element array.
*
* @param minmax array of min then max
* @returns new MinMaxable
*/
static fromArray(minmax) {
if (minmax.length < 2) {
throw new Error(`array must have lenght 2, ${minmax.length}`);
}
return new MinMaxable(minmax[0], minmax[1]);
}
static fromMiddleHalfWidth(mid, halfWidth) {
return new MinMaxable(mid - halfWidth, mid + halfWidth);
}
}
exports.MinMaxable = MinMaxable;
class AmplitudeScalable {
minMax;
constructor(minMax) {
if (minMax) {
this.minMax = minMax;
}
else {
this.minMax = new MinMaxable(0, 0);
}
}
// eslint-disable-next-line no-unused-vars
notifyAmplitudeChange(_middle, _halfWidth) {
// no-op
}
get middle() {
return this.minMax.middle;
}
get halfWidth() {
return this.minMax.halfWidth;
}
get fullWidth() {
return this.minMax.fullWidth;
}
get min() {
return this.minMax.min;
}
get max() {
return this.minMax.max;
}
toString() {
return this.minMax.toString();
}
}
exports.AmplitudeScalable = AmplitudeScalable;
class TimeScalable {
alignmentTimeOffset;
duration;
constructor(alignmentTimeOffset, duration) {
this.alignmentTimeOffset = alignmentTimeOffset;
this.duration = duration;
}
// eslint-disable-next-line no-unused-vars
notifyTimeRangeChange(_alignmentTimeOffset, _duration) {
// no-op
}
}
exports.TimeScalable = TimeScalable;
/**
* Links amplitude scales across multiple seismographs, respecting doRmean.
*
* @param graphList optional list of AmplitudeScalable to link
*/
class LinkedAmplitudeScale {
/**
* @private
*/
_graphSet;
_halfWidth;
_recalcTimeoutID;
_scaleId;
constructor(graphList) {
this._scaleId = ++_lastId;
const glist = graphList ? graphList : []; // in case null
this._halfWidth = 0;
this._graphSet = new Set(glist);
this._recalcTimeoutID = null;
}
get halfWidth() {
return this._halfWidth;
}
set halfWidth(val) {
if (this._halfWidth !== val) {
this._halfWidth = val;
this.notifyAll().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc halfWidth: ${m}`);
});
}
}
/**
* Links new Seismograph with this amplitude scale.
*
* @param graphList Array of AmplitudeScalable to link
*/
linkAll(graphList) {
graphList.forEach((graph) => {
if ("notifyAmplitudeChange" in graph) {
this._graphSet.add(graph);
}
else if ("amp_scalable" in graph) {
this._graphSet.add(graph.amp_scalable);
}
else {
// graph does not have notifyAmplitudeChange method or amp_scalable field, skipping
}
});
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc linkAll: ${m}`);
});
}
/**
* Link new Seismograph with this amplitude scale.
*
* @param graph AmplitudeScalable to link
*/
link(graph) {
this.linkAll([graph]);
}
/**
* Unlink Seismograph with this amplitude scale.
*
* @param graph AmplitudeScalable to unlink
*/
unlink(graph) {
this._graphSet.delete(graph);
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc unlink: ${m}`);
});
}
/**
* Recalculate the best amplitude scale for all Seismographs. Causes a redraw.
*
* @returns array of promise of best amp scales
*/
recalculate() {
const maxHalfRange = this.graphList.reduce((acc, cur) => {
return acc > cur.halfWidth ? acc : cur.halfWidth;
}, 0);
let promiseOut;
if (this.halfWidth !== maxHalfRange) {
this.halfWidth = maxHalfRange;
promiseOut = this._internalNotifyAll();
}
else {
// no change
promiseOut = Promise.all(this.graphList.map((g) => Promise.resolve(g)));
}
return promiseOut;
}
_internalNotifyAll() {
const hw = this.halfWidth;
return Promise.all(this.graphList.map((g) => {
return new Promise((resolve) => {
setTimeout(() => {
g.notifyAmplitudeChange(g.middle, hw);
resolve(g);
}, 10);
});
}));
}
notifyAll() {
return this._internalNotifyAll();
}
get graphList() {
return Array.from(this._graphSet.values());
}
}
exports.LinkedAmplitudeScale = LinkedAmplitudeScale;
class IndividualAmplitudeScale extends LinkedAmplitudeScale {
constructor(graphList) {
super(graphList);
}
recalculate() {
// no-op, just notify
return this.notifyAll();
}
notifyAll() {
return Promise.all(this.graphList.map((g) => {
return new Promise((resolve) => {
setTimeout(() => {
g.notifyAmplitudeChange(g.middle, g.halfWidth);
resolve(g);
}, 10);
});
}));
}
}
exports.IndividualAmplitudeScale = IndividualAmplitudeScale;
class FixedHalfWidthAmplitudeScale extends LinkedAmplitudeScale {
constructor(halfWidth, graphList) {
super(graphList);
this.halfWidth = halfWidth;
}
recalculate() {
// no-op, just notify
return this.notifyAll();
}
notifyAll() {
const hw = this.halfWidth;
return Promise.all(this.graphList.map((g) => {
return new Promise((resolve) => {
setTimeout(() => {
g.notifyAmplitudeChange(g.middle, hw);
resolve(g);
}, 10);
});
}));
}
}
exports.FixedHalfWidthAmplitudeScale = FixedHalfWidthAmplitudeScale;
/**
* Links time scales across multiple seismographs.
*
* @param graphList optional list of TimeScalables to link
*/
class LinkedTimeScale {
/**
* @private
*/
_graphSet;
_originalDuration;
_originalOffset;
_zoomedDuration;
_zoomedOffset;
_scaleId;
constructor(graphList, originalDuration, originalOffset, scaleId) {
if (scaleId) {
this._scaleId = scaleId;
}
else {
this._scaleId = -1;
}
const glist = graphList ? graphList : []; // in case null
this._graphSet = new Set(glist);
this._originalDuration = luxon_1.Duration.fromMillis(0);
this._originalOffset = luxon_1.Duration.fromMillis(0);
this._zoomedDuration = null;
this._zoomedOffset = null;
if ((0, utils_1.isDef)(originalDuration)) {
this._originalDuration = originalDuration;
// so know that duration passed in instead of calculated
// this prevents future links from causeing recalc
this._zoomedDuration = originalDuration;
}
else if (glist.length > 0) {
this._originalDuration = glist.reduce((acc, cur) => {
return acc > cur.duration ? acc : cur.duration;
}, luxon_1.Duration.fromMillis(0));
}
if (originalOffset) {
this._originalOffset = originalOffset;
}
else {
this._originalOffset = luxon_1.Duration.fromMillis(0);
}
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc constructor: ${m}`);
});
}
/**
* Link new TimeScalable with this time scale.
*
* @param graph TimeScalable to link
*/
link(graph) {
this.linkAll([graph]);
}
/**
* Links TimeScalable with this time scale. Each
* object in the array should either be a TimeScalable
* or have a time_scalable field that is a TimeScalable.
*
* @param graphList Array of TimeScalable to link
*/
linkAll(graphList) {
graphList.forEach((graph) => {
if ("notifyTimeRangeChange" in graph) {
this._graphSet.add(graph);
}
else if ("time_scalable" in graph) {
this._graphSet.add(graph.time_scalable);
}
else {
//graph does not have notifyTimeRangeChange method or time_scalable field, skipping
}
});
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc linkAll: ${m}`);
});
}
/**
* Unlink TimeScalable with this amplitude scale.
*
* @param graph TimeScalable to unlink
*/
unlink(graph) {
this._graphSet.delete(graph);
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc unlink: ${m}`);
});
}
zoom(startOffset, duration) {
this._zoomedDuration = duration;
this._zoomedOffset = startOffset;
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc zoom: ${m}`);
});
}
unzoom() {
this._zoomedDuration = null;
this._zoomedOffset = null;
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc unzoom: ${m}`);
});
}
get offset() {
return this._zoomedOffset ? this._zoomedOffset : this._originalOffset;
}
set offset(offset) {
this._originalOffset = offset;
this._zoomedOffset = offset;
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc set offset: ${m}`);
});
}
get duration() {
return (0, utils_1.isDef)(this._zoomedDuration)
? this._zoomedDuration
: this._originalDuration;
}
set duration(duration) {
if (!(0, utils_1.isDef)(duration)) {
throw new Error(`Duration must be defined`);
}
this._originalDuration = duration;
this._zoomedDuration = duration;
this.recalculate().catch((m) => {
// eslint-disable-next-line no-console
console.warn(`problem recalc set duration: ${m}`);
});
}
get origOffset() {
return this._originalOffset;
}
get origDuration() {
return this._originalDuration;
}
/**
* Recalculate the best time scale for all Seismographs. Causes a redraw.
* @returns promise to array of all linked items
*/
recalculate() {
if (!(0, utils_1.isDef)(this._zoomedDuration) ||
this._originalDuration.toMillis() === 0) {
this.graphList.forEach((graph) => {
if (graph && graph.duration > this._originalDuration) {
this._originalDuration = graph.duration;
}
});
}
return this.notifyAll();
}
notifyAll() {
return Promise.all(this.graphList.map((g) => {
return new Promise((resolve) => {
setTimeout(() => {
if (g != null) {
g.notifyTimeRangeChange(this.offset, this.duration);
}
resolve(g);
}, 10);
});
}));
}
get graphList() {
return Array.from(this._graphSet.values());
}
}
exports.LinkedTimeScale = LinkedTimeScale;
/**
* Linked Time Scale that only modifies the alignment via the offset. The
* duration of the linked TimeScalable is reused.
* @param graphList [description]
* @param originalDuration [description]
* @param originalOffset [description]
* @param scaleId [description]
*/
class AlignmentLinkedTimeScale extends LinkedTimeScale {
constructor(graphList, originalDuration, originalOffset, scaleId) {
super(graphList, originalDuration, originalOffset, scaleId);
}
/**
* Does no calculation, just causes a redraw.
* @returns promise to all linked items
*/
recalculate() {
return this.notifyAll();
}
notifyAll() {
return Promise.all(this.graphList.map((g) => {
return new Promise((resolve) => {
setTimeout(() => {
if (g != null) {
g.notifyTimeRangeChange(this.offset, this.duration);
}
resolve(g);
}, 10);
});
}));
}
}
exports.AlignmentLinkedTimeScale = AlignmentLinkedTimeScale;