@genome-spy/core 0.41.0 → 0.42.0
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/bundle/index--cKb-dKG.js +615 -0
- package/dist/bundle/{index-gn8bhQ8w.js → index-d7k3kkin.js} +365 -366
- package/dist/bundle/index.es.js +4077 -3900
- package/dist/bundle/index.js +111 -69
- package/dist/schema.json +58 -6
- package/dist/src/data/sources/dynamic/axisGenomeSource.js +1 -1
- package/dist/src/data/sources/dynamic/axisTickSource.js +3 -3
- package/dist/src/data/sources/dynamic/bamSource.d.ts +3 -21
- package/dist/src/data/sources/dynamic/bamSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/bamSource.js +38 -55
- package/dist/src/data/sources/dynamic/bigBedSource.d.ts +2 -38
- package/dist/src/data/sources/dynamic/bigBedSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/bigBedSource.js +14 -71
- package/dist/src/data/sources/dynamic/bigWigSource.d.ts +4 -42
- package/dist/src/data/sources/dynamic/bigWigSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/bigWigSource.js +23 -60
- package/dist/src/data/sources/dynamic/gff3Source.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/gff3Source.js +1 -0
- package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts +2 -20
- package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/indexedFastaSource.js +23 -41
- package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts +23 -4
- package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/singleAxisLazySource.js +29 -4
- package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts +60 -0
- package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts.map +1 -0
- package/dist/src/data/sources/dynamic/singleAxisWindowedSource.js +152 -0
- package/dist/src/data/sources/dynamic/tabixSource.d.ts +6 -40
- package/dist/src/data/sources/dynamic/tabixSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/tabixSource.js +29 -78
- package/dist/src/data/transforms/regexFold.d.ts.map +1 -1
- package/dist/src/data/transforms/regexFold.js +8 -0
- package/dist/src/data/transforms/regexFold.test.js +28 -0
- package/dist/src/genomeSpy.d.ts +14 -0
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +114 -8
- package/dist/src/gl/webGLHelper.d.ts +6 -21
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +7 -38
- package/dist/src/img/90-ring-with-bg.svg +1 -0
- package/dist/src/img/README.md +5 -0
- package/dist/src/marks/link.d.ts +7 -0
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +72 -37
- package/dist/src/marks/text.d.ts.map +1 -1
- package/dist/src/marks/text.js +16 -17
- package/dist/src/spec/data.d.ts +28 -13
- package/dist/src/spec/mark.d.ts +0 -8
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +33 -4
- package/dist/src/styles/genome-spy.scss +40 -4
- package/dist/src/types/viewContext.d.ts +9 -0
- package/dist/src/utils/binnedIndex.d.ts +2 -0
- package/dist/src/utils/binnedIndex.d.ts.map +1 -1
- package/dist/src/utils/binnedIndex.js +59 -10
- package/dist/src/utils/binnedIndex.test.js +46 -0
- package/dist/src/view/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView.js +2 -0
- package/dist/src/view/layerView.d.ts.map +1 -1
- package/dist/src/view/layerView.js +2 -0
- package/dist/src/view/unitView.d.ts +0 -6
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +2 -9
- package/dist/src/view/view.d.ts +6 -0
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +11 -0
- package/package.json +3 -3
- package/dist/bundle/index-Cbz74kpR.js +0 -638
- package/dist/src/data/sources/dynamic/windowedMixin.d.ts +0 -32
- package/dist/src/data/sources/dynamic/windowedMixin.d.ts.map +0 -1
- package/dist/src/data/sources/dynamic/windowedMixin.js +0 -53
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import { debounce } from "../../../utils/debounce.js";
|
|
2
|
-
import SingleAxisLazySource from "./singleAxisLazySource.js";
|
|
3
|
-
import windowedMixin from "./windowedMixin.js";
|
|
4
1
|
import addBaseUrl from "../../../utils/addBaseUrl.js";
|
|
2
|
+
import SingleAxisWindowedSource from "./singleAxisWindowedSource.js";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
*
|
|
8
6
|
*/
|
|
9
|
-
export default class BigWigSource extends
|
|
7
|
+
export default class BigWigSource extends SingleAxisWindowedSource {
|
|
10
8
|
/** @type {number[]} */
|
|
11
|
-
reductionLevels = [];
|
|
12
|
-
|
|
13
|
-
/** Keep track of the order of the requests */
|
|
14
|
-
lastRequestId = 0;
|
|
9
|
+
#reductionLevels = [];
|
|
15
10
|
|
|
16
11
|
/** @type {import("@gmod/bbi").BigWig} */
|
|
17
|
-
bbi;
|
|
12
|
+
#bbi;
|
|
18
13
|
|
|
19
14
|
/**
|
|
20
15
|
* @param {import("../../../spec/data.js").BigWigData} params
|
|
@@ -25,6 +20,8 @@ export default class BigWigSource extends windowedMixin(SingleAxisLazySource) {
|
|
|
25
20
|
const paramsWithDefaults = {
|
|
26
21
|
pixelsPerBin: 2,
|
|
27
22
|
channel: "x",
|
|
23
|
+
debounce: 200,
|
|
24
|
+
debounceMode: "window",
|
|
28
25
|
...params,
|
|
29
26
|
};
|
|
30
27
|
|
|
@@ -36,25 +33,21 @@ export default class BigWigSource extends windowedMixin(SingleAxisLazySource) {
|
|
|
36
33
|
throw new Error("No URL provided for BigWigSource");
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
this.
|
|
40
|
-
this.doRequest.bind(this),
|
|
41
|
-
200,
|
|
42
|
-
false
|
|
43
|
-
);
|
|
36
|
+
this.setupDebouncing(this.params);
|
|
44
37
|
|
|
45
38
|
this.initializedPromise = new Promise((resolve) => {
|
|
46
39
|
Promise.all([
|
|
47
40
|
import("@gmod/bbi"),
|
|
48
41
|
import("generic-filehandle"),
|
|
49
42
|
]).then(([{ BigWig }, { RemoteFile }]) => {
|
|
50
|
-
this
|
|
43
|
+
this.#bbi = new BigWig({
|
|
51
44
|
filehandle: new RemoteFile(
|
|
52
45
|
addBaseUrl(this.params.url, this.view.getBaseUrl())
|
|
53
46
|
),
|
|
54
47
|
});
|
|
55
48
|
|
|
56
|
-
this
|
|
57
|
-
this
|
|
49
|
+
this.#bbi.getHeader().then((header) => {
|
|
50
|
+
this.#reductionLevels =
|
|
58
51
|
/** @type {{reductionLevel: number}[]} */ (
|
|
59
52
|
header.zoomLevels
|
|
60
53
|
)
|
|
@@ -63,7 +56,7 @@ export default class BigWigSource extends windowedMixin(SingleAxisLazySource) {
|
|
|
63
56
|
|
|
64
57
|
// Add the non-reduced level. Not sure if this is the best way to do it.
|
|
65
58
|
// Afaik, the non-reduced bin size is not available in the header.
|
|
66
|
-
this
|
|
59
|
+
this.#reductionLevels.push(1);
|
|
67
60
|
|
|
68
61
|
resolve();
|
|
69
62
|
});
|
|
@@ -85,7 +78,7 @@ export default class BigWigSource extends windowedMixin(SingleAxisLazySource) {
|
|
|
85
78
|
const reductionLevel = findReductionLevel(
|
|
86
79
|
domain,
|
|
87
80
|
length,
|
|
88
|
-
this
|
|
81
|
+
this.#reductionLevels
|
|
89
82
|
);
|
|
90
83
|
|
|
91
84
|
// The sensible minimum window size actually depends on the non-reduced data density...
|
|
@@ -95,51 +88,24 @@ export default class BigWigSource extends windowedMixin(SingleAxisLazySource) {
|
|
|
95
88
|
const quantizedInterval = this.quantizeInterval(domain, windowSize);
|
|
96
89
|
|
|
97
90
|
if (this.checkAndUpdateLastInterval(quantizedInterval)) {
|
|
98
|
-
this.
|
|
91
|
+
this.loadInterval(quantizedInterval, reductionLevel);
|
|
99
92
|
}
|
|
100
93
|
}
|
|
101
94
|
|
|
102
95
|
/**
|
|
103
|
-
* Listen to the domain change event and update data when the covered windows change.
|
|
104
|
-
*
|
|
105
96
|
* @param {number[]} interval linearized domain
|
|
106
97
|
* @param {number} reductionLevel
|
|
107
98
|
*/
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
// @ts-expect-error
|
|
100
|
+
async loadInterval(interval, reductionLevel) {
|
|
101
|
+
const scale = 1 / 2 / reductionLevel / this.params.pixelsPerBin;
|
|
102
|
+
const featureChunks = await this.discretizeAndLoad(
|
|
110
103
|
interval,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Discard late responses
|
|
115
|
-
if (featureResponse.requestId < this.lastRequestId) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.publishData(featureResponse.features);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
*
|
|
124
|
-
* @param {number[]} interval
|
|
125
|
-
* @param {number} scale
|
|
126
|
-
*/
|
|
127
|
-
async getFeatures(interval, scale) {
|
|
128
|
-
let requestId = ++this.lastRequestId;
|
|
129
|
-
|
|
130
|
-
// TODO: Abort previous requests
|
|
131
|
-
const abortController = new AbortController();
|
|
132
|
-
|
|
133
|
-
const discreteChromosomeIntervals =
|
|
134
|
-
this.genome.continuousToDiscreteChromosomeIntervals(interval);
|
|
135
|
-
|
|
136
|
-
// TODO: Error handling
|
|
137
|
-
const featuresWithChrom = await Promise.all(
|
|
138
|
-
discreteChromosomeIntervals.map((d) =>
|
|
139
|
-
this.bbi
|
|
104
|
+
(d, signal) =>
|
|
105
|
+
this.#bbi
|
|
140
106
|
.getFeatures(d.chrom, d.startPos, d.endPos, {
|
|
141
107
|
scale,
|
|
142
|
-
signal
|
|
108
|
+
signal,
|
|
143
109
|
})
|
|
144
110
|
.then((features) =>
|
|
145
111
|
features.map((f) => ({
|
|
@@ -149,14 +115,11 @@ export default class BigWigSource extends windowedMixin(SingleAxisLazySource) {
|
|
|
149
115
|
score: f.score,
|
|
150
116
|
}))
|
|
151
117
|
)
|
|
152
|
-
)
|
|
153
118
|
);
|
|
154
119
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
features: featuresWithChrom.flat(), // TODO: Use batches, not flat
|
|
159
|
-
};
|
|
120
|
+
if (featureChunks) {
|
|
121
|
+
this.publishData(featureChunks);
|
|
122
|
+
}
|
|
160
123
|
}
|
|
161
124
|
}
|
|
162
125
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gff3Source.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/gff3Source.js"],"names":[],"mappings":"AAEA;;GAEG;AACH;IAII;;;OAGG;IACH,oBAHW,OAAO,uBAAuB,EAAE,SAAS,QACzC,OAAO,uBAAuB,EAAE,OAAO,EASjD;IAED;;OAEG;IACH,sBAFW,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"gff3Source.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/gff3Source.js"],"names":[],"mappings":"AAEA;;GAEG;AACH;IAII;;;OAGG;IACH,oBAHW,OAAO,uBAAuB,EAAE,SAAS,QACzC,OAAO,uBAAuB,EAAE,OAAO,EASjD;IAED;;OAEG;IACH,sBAFW,MAAM,EAAE,OAUlB;;CACJ;wBAlCuB,kBAAkB"}
|
|
@@ -24,6 +24,7 @@ export default class Gff3Source extends TabixSource {
|
|
|
24
24
|
* @param {string[]} lines
|
|
25
25
|
*/
|
|
26
26
|
_parseFeatures(lines) {
|
|
27
|
+
// Hmm. It's silly that we have to first collect individual lines and then join them.
|
|
27
28
|
// eslint-disable-next-line no-sync
|
|
28
29
|
const features = this.#gff?.parseStringSync(lines.join("\n"), {
|
|
29
30
|
parseSequences: false,
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
new (...args: any[]): {
|
|
3
|
-
[x: string]: any;
|
|
4
|
-
lastQuantizedInterval: number[];
|
|
5
|
-
quantizeInterval(interval: number[], windowSize: number): number[];
|
|
6
|
-
checkAndUpdateLastInterval(interval: number[]): boolean;
|
|
7
|
-
};
|
|
8
|
-
} & typeof SingleAxisLazySource;
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
*/
|
|
12
|
-
export default class IndexedFastaSource extends IndexedFastaSource_base {
|
|
1
|
+
export default class IndexedFastaSource extends SingleAxisWindowedSource {
|
|
13
2
|
/**
|
|
14
3
|
* @param {import("../../../spec/data.js").IndexedFastaData} params
|
|
15
4
|
* @param {import("../../../view/view.js").default} view
|
|
@@ -18,13 +7,6 @@ export default class IndexedFastaSource extends IndexedFastaSource_base {
|
|
|
18
7
|
params: import("../../../spec/data.js").IndexedFastaData;
|
|
19
8
|
initializedPromise: Promise<any>;
|
|
20
9
|
fasta: import("@gmod/indexedfasta").IndexedFasta;
|
|
21
|
-
/**
|
|
22
|
-
* Listen to the domain change event and update data when the covered windows change.
|
|
23
|
-
*
|
|
24
|
-
* @param {number[]} domain Linearized domain
|
|
25
|
-
*/
|
|
26
|
-
onDomainChanged(domain: number[]): Promise<void>;
|
|
27
10
|
}
|
|
28
|
-
import
|
|
29
|
-
export {};
|
|
11
|
+
import SingleAxisWindowedSource from "./singleAxisWindowedSource.js";
|
|
30
12
|
//# sourceMappingURL=indexedFastaSource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexedFastaSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/indexedFastaSource.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"indexedFastaSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/indexedFastaSource.js"],"names":[],"mappings":"AAGA;IACI;;;OAGG;IACH,oBAHW,OAAO,uBAAuB,EAAE,gBAAgB,QAChD,OAAO,uBAAuB,EAAE,OAAO,EA+CjD;IAjCG,yDAAgC;IAQhC,iCAwBE;IATM,iDAKE;CA6BjB;qCA5EoC,+BAA+B"}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import SingleAxisLazySource from "./singleAxisLazySource.js";
|
|
2
|
-
import windowedMixin from "./windowedMixin.js";
|
|
3
1
|
import addBaseUrl from "../../../utils/addBaseUrl.js";
|
|
2
|
+
import SingleAxisWindowedSource from "./singleAxisWindowedSource.js";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
export default class IndexedFastaSource extends windowedMixin(
|
|
9
|
-
SingleAxisLazySource
|
|
10
|
-
) {
|
|
4
|
+
export default class IndexedFastaSource extends SingleAxisWindowedSource {
|
|
11
5
|
/**
|
|
12
6
|
* @param {import("../../../spec/data.js").IndexedFastaData} params
|
|
13
7
|
* @param {import("../../../view/view.js").default} view
|
|
@@ -17,6 +11,8 @@ export default class IndexedFastaSource extends windowedMixin(
|
|
|
17
11
|
const paramsWithDefaults = {
|
|
18
12
|
channel: "x",
|
|
19
13
|
windowSize: 7000,
|
|
14
|
+
debounce: 200,
|
|
15
|
+
debounceMode: "window",
|
|
20
16
|
...params,
|
|
21
17
|
};
|
|
22
18
|
|
|
@@ -28,6 +24,8 @@ export default class IndexedFastaSource extends windowedMixin(
|
|
|
28
24
|
throw new Error("No URL provided for IndexedFastaSource");
|
|
29
25
|
}
|
|
30
26
|
|
|
27
|
+
this.setupDebouncing(this.params);
|
|
28
|
+
|
|
31
29
|
this.initializedPromise = new Promise((resolve) => {
|
|
32
30
|
Promise.all([
|
|
33
31
|
import("buffer"),
|
|
@@ -56,41 +54,25 @@ export default class IndexedFastaSource extends windowedMixin(
|
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* @param {number[]} domain Linearized domain
|
|
57
|
+
* @param {number[]} interval linearized domain
|
|
62
58
|
*/
|
|
63
|
-
async
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
quantizedInterval
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
// TODO: Error handling
|
|
81
|
-
const sequencesWithChrom = await Promise.all(
|
|
82
|
-
discreteChromosomeIntervals.map((d) =>
|
|
83
|
-
this.fasta
|
|
84
|
-
.getSequence(d.chrom, d.startPos, d.endPos)
|
|
85
|
-
.then((sequence) => ({
|
|
86
|
-
chrom: d.chrom,
|
|
87
|
-
start: d.startPos,
|
|
88
|
-
sequence,
|
|
89
|
-
}))
|
|
90
|
-
)
|
|
91
|
-
);
|
|
59
|
+
async loadInterval(interval) {
|
|
60
|
+
const features = await this.discretizeAndLoad(
|
|
61
|
+
interval,
|
|
62
|
+
async (d, signal) =>
|
|
63
|
+
this.fasta
|
|
64
|
+
.getSequence(d.chrom, d.startPos, d.endPos, {
|
|
65
|
+
signal,
|
|
66
|
+
})
|
|
67
|
+
.then((sequence) => ({
|
|
68
|
+
chrom: d.chrom,
|
|
69
|
+
start: d.startPos,
|
|
70
|
+
sequence,
|
|
71
|
+
}))
|
|
72
|
+
);
|
|
92
73
|
|
|
93
|
-
|
|
74
|
+
if (features) {
|
|
75
|
+
this.publishData([features]);
|
|
94
76
|
}
|
|
95
77
|
}
|
|
96
78
|
}
|
|
@@ -7,6 +7,11 @@ export default class SingleAxisLazySource extends DataSource {
|
|
|
7
7
|
* @param {import("../../../spec/channel.js").PrimaryPositionalChannel} channel
|
|
8
8
|
*/
|
|
9
9
|
constructor(view: import("../../../view/view.js").default, channel: import("../../../spec/channel.js").PrimaryPositionalChannel);
|
|
10
|
+
/**
|
|
11
|
+
* Has to be resolved before any data can be requested upon domain changes.
|
|
12
|
+
* @protected
|
|
13
|
+
*/
|
|
14
|
+
protected initializedPromise: Promise<void>;
|
|
10
15
|
view: import("../../../view/view.js").default;
|
|
11
16
|
/** @type {import("../../../spec/channel.js").PrimaryPositionalChannel} */
|
|
12
17
|
channel: import("../../../spec/channel.js").PrimaryPositionalChannel;
|
|
@@ -18,25 +23,39 @@ export default class SingleAxisLazySource extends DataSource {
|
|
|
18
23
|
getAxisLength(): number;
|
|
19
24
|
/**
|
|
20
25
|
* Convenience getter for genome.
|
|
26
|
+
*
|
|
27
|
+
* @protected
|
|
21
28
|
*/
|
|
22
|
-
get genome(): import("../../../genome/genome.js").default;
|
|
29
|
+
protected get genome(): import("../../../genome/genome.js").default;
|
|
23
30
|
/**
|
|
24
31
|
* Listen to the domain change event and update data when the covered windows change.
|
|
25
32
|
*
|
|
26
33
|
* @param {number[]} domain Linearized domain
|
|
27
34
|
* @param {import("../../../spec/genome.js").ChromosomalLocus[]} complexDomain Chrom/Pos domain
|
|
35
|
+
* @abstract
|
|
28
36
|
*/
|
|
29
37
|
onDomainChanged(domain: number[], complexDomain: import("../../../spec/genome.js").ChromosomalLocus[]): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Sets the loading status of the data source. The status is shown in the UI.
|
|
40
|
+
*
|
|
41
|
+
* @param {boolean} status true if loading, false otherwise
|
|
42
|
+
* @protected
|
|
43
|
+
*/
|
|
44
|
+
protected setLoadingStatus(status: boolean): void;
|
|
30
45
|
/**
|
|
31
46
|
* TODO: Get rid of this method.
|
|
32
47
|
* Rendering should be requested by the collector.
|
|
48
|
+
*
|
|
49
|
+
* @protected
|
|
33
50
|
*/
|
|
34
|
-
requestRender(): void;
|
|
51
|
+
protected requestRender(): void;
|
|
35
52
|
/**
|
|
53
|
+
* Resets the data flow and propagates the data.
|
|
36
54
|
*
|
|
37
|
-
* @param {import("../../flowNode.js").Datum[]} data
|
|
55
|
+
* @param {import("../../flowNode.js").Datum[][]} chunks An array of data chunks.
|
|
56
|
+
* @protected
|
|
38
57
|
*/
|
|
39
|
-
publishData(
|
|
58
|
+
protected publishData(chunks: import("../../flowNode.js").Datum[][]): void;
|
|
40
59
|
}
|
|
41
60
|
import DataSource from "../dataSource.js";
|
|
42
61
|
//# sourceMappingURL=singleAxisLazySource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"singleAxisLazySource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/singleAxisLazySource.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;
|
|
1
|
+
{"version":3,"file":"singleAxisLazySource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/singleAxisLazySource.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;IAOI;;;OAGG;IACH,kBAHW,OAAO,uBAAuB,EAAE,OAAO,WACvC,OAAO,0BAA0B,EAAE,wBAAwB,EAiDrE;IAzDD;;;OAGG;IACH,4CAAuC;IASnC,8CAAgB;IAYhB,2EAA2E;IAC3E,SADW,OAAO,0BAA0B,EAAE,wBAAwB,CAChD;IAEtB,oEAA4D;IA+BhE;;;OAGG;IACH,wBAWC;IAED;;;;OAIG;IACH,oEAEC;IAED;;;;;;OAMG;IACH,wBAJW,MAAM,EAAE,iBACR,OAAO,yBAAyB,EAAE,gBAAgB,EAAE,iBAK9D;IAED;;;;;OAKG;IACH,mCAHW,OAAO,QAKjB;IAED;;;;;OAKG;IACH,gCAGC;IAQD;;;;;OAKG;IACH,8BAHW,OAAO,mBAAmB,EAAE,KAAK,EAAE,EAAE,QAkB/C;CACJ;uBAxJsB,kBAAkB"}
|
|
@@ -6,6 +6,12 @@ import { reconfigureScales } from "../../../view/scaleResolution.js";
|
|
|
6
6
|
* Base class for data sources that listen a domain and propagate data lazily.
|
|
7
7
|
*/
|
|
8
8
|
export default class SingleAxisLazySource extends DataSource {
|
|
9
|
+
/**
|
|
10
|
+
* Has to be resolved before any data can be requested upon domain changes.
|
|
11
|
+
* @protected
|
|
12
|
+
*/
|
|
13
|
+
initializedPromise = Promise.resolve();
|
|
14
|
+
|
|
9
15
|
/**
|
|
10
16
|
* @param {import("../../../view/view.js").default} view
|
|
11
17
|
* @param {import("../../../spec/channel.js").PrimaryPositionalChannel} channel
|
|
@@ -78,6 +84,8 @@ export default class SingleAxisLazySource extends DataSource {
|
|
|
78
84
|
|
|
79
85
|
/**
|
|
80
86
|
* Convenience getter for genome.
|
|
87
|
+
*
|
|
88
|
+
* @protected
|
|
81
89
|
*/
|
|
82
90
|
get genome() {
|
|
83
91
|
return this.scaleResolution.getGenome();
|
|
@@ -88,14 +96,27 @@ export default class SingleAxisLazySource extends DataSource {
|
|
|
88
96
|
*
|
|
89
97
|
* @param {number[]} domain Linearized domain
|
|
90
98
|
* @param {import("../../../spec/genome.js").ChromosomalLocus[]} complexDomain Chrom/Pos domain
|
|
99
|
+
* @abstract
|
|
91
100
|
*/
|
|
92
101
|
async onDomainChanged(domain, complexDomain) {
|
|
93
102
|
// Override me
|
|
94
103
|
}
|
|
95
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Sets the loading status of the data source. The status is shown in the UI.
|
|
107
|
+
*
|
|
108
|
+
* @param {boolean} status true if loading, false otherwise
|
|
109
|
+
* @protected
|
|
110
|
+
*/
|
|
111
|
+
setLoadingStatus(status) {
|
|
112
|
+
this.view.context.setDataLoadingStatus(this.view, status);
|
|
113
|
+
}
|
|
114
|
+
|
|
96
115
|
/**
|
|
97
116
|
* TODO: Get rid of this method.
|
|
98
117
|
* Rendering should be requested by the collector.
|
|
118
|
+
*
|
|
119
|
+
* @protected
|
|
99
120
|
*/
|
|
100
121
|
requestRender() {
|
|
101
122
|
// An awfully hacky way.
|
|
@@ -109,15 +130,19 @@ export default class SingleAxisLazySource extends DataSource {
|
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
/**
|
|
133
|
+
* Resets the data flow and propagates the data.
|
|
112
134
|
*
|
|
113
|
-
* @param {import("../../flowNode.js").Datum[]} data
|
|
135
|
+
* @param {import("../../flowNode.js").Datum[][]} chunks An array of data chunks.
|
|
136
|
+
* @protected
|
|
114
137
|
*/
|
|
115
|
-
publishData(
|
|
138
|
+
publishData(chunks) {
|
|
116
139
|
this.reset();
|
|
117
140
|
this.beginBatch({ type: "file" });
|
|
118
141
|
|
|
119
|
-
for (const
|
|
120
|
-
|
|
142
|
+
for (const data of chunks) {
|
|
143
|
+
for (const d of data) {
|
|
144
|
+
this._propagate(d);
|
|
145
|
+
}
|
|
121
146
|
}
|
|
122
147
|
|
|
123
148
|
this.complete();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @abstract
|
|
3
|
+
*/
|
|
4
|
+
export default class SingleAxisWindowedSource extends SingleAxisLazySource {
|
|
5
|
+
/**
|
|
6
|
+
* @type {{windowSize?: number}}
|
|
7
|
+
* @protected
|
|
8
|
+
*/
|
|
9
|
+
protected params: {
|
|
10
|
+
windowSize?: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* @param {import("../../../spec/data.js").DebouncedData} debounceParams
|
|
14
|
+
* @protected
|
|
15
|
+
*/
|
|
16
|
+
protected setupDebouncing(debounceParams: import("../../../spec/data.js").DebouncedData): void;
|
|
17
|
+
/**
|
|
18
|
+
* Listen to the domain change event and update data when the covered windows change.
|
|
19
|
+
*
|
|
20
|
+
* @param {number[]} domain Linearized domain
|
|
21
|
+
*/
|
|
22
|
+
onDomainChanged(domain: number[]): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Listen to the domain change event and update data when the covered windows change.
|
|
25
|
+
*
|
|
26
|
+
* @param {number[]} interval linearized domain
|
|
27
|
+
* @protected
|
|
28
|
+
*/
|
|
29
|
+
protected loadInterval(interval: number[]): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Splits the interval into discrete chromosomal intervals – one for each chromosome –
|
|
32
|
+
* and loads the data for each of them. Handles abort signals and errors.
|
|
33
|
+
*
|
|
34
|
+
* @param {number[]} interval
|
|
35
|
+
* @param {(discreteInteval: import("@genome-spy/core/genome/genome.js").DiscreteChromosomeInterval, signal: AbortSignal) => Promise<T>} loader
|
|
36
|
+
* @return {Promise<T[]>}
|
|
37
|
+
* @template T
|
|
38
|
+
* @protected
|
|
39
|
+
*/
|
|
40
|
+
protected discretizeAndLoad<T>(interval: number[], loader: (discreteInteval: import("@genome-spy/core/genome/genome.js").DiscreteChromosomeInterval, signal: AbortSignal) => Promise<T>): Promise<T[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Returns three consecutive windows. The idea is to immediately have some data
|
|
43
|
+
* to show to the user when they pan the view. The windows are conceptually
|
|
44
|
+
* similar to "tiles" but they are never loaded separately.
|
|
45
|
+
*
|
|
46
|
+
* @param {number[]} interval
|
|
47
|
+
* @param {number} windowSize
|
|
48
|
+
* @protected
|
|
49
|
+
*/
|
|
50
|
+
protected quantizeInterval(interval: number[], windowSize: number): number[];
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param {number[]} interval
|
|
54
|
+
* @protected
|
|
55
|
+
*/
|
|
56
|
+
protected checkAndUpdateLastInterval(interval: number[]): boolean;
|
|
57
|
+
#private;
|
|
58
|
+
}
|
|
59
|
+
import SingleAxisLazySource from "./singleAxisLazySource.js";
|
|
60
|
+
//# sourceMappingURL=singleAxisWindowedSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singleAxisWindowedSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/dynamic/singleAxisWindowedSource.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;IAQI;;;OAGG;IACH,kBAHU;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAC,CAGxB;IAEP;;;OAGG;IACH,0CAHW,OAAO,uBAAuB,EAAE,aAAa,QAmBvD;IAED;;;;OAIG;IACH,wBAFW,MAAM,EAAE,iBAiBlB;IAED;;;;;OAKG;IACH,iCAHW,MAAM,EAAE,iBAKlB;IAED;;;;;;;;;OASG;IACH,yCANW,MAAM,EAAE,4BACU,OAAO,mCAAmC,EAAE,0BAA0B,UAAU,WAAW,+BAqCvH;IAED;;;;;;;;OAQG;IACH,qCAJW,MAAM,EAAE,cACR,MAAM,YAWhB;IAED;;;;OAIG;IACH,+CAHW,MAAM,EAAE,WAUlB;;CACJ;iCAvJgC,2BAA2B"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import SingleAxisLazySource from "./singleAxisLazySource.js";
|
|
2
|
+
import { shallowArrayEquals } from "../../../utils/arrayUtils.js";
|
|
3
|
+
import { debounce } from "@genome-spy/core/utils/debounce.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @abstract
|
|
7
|
+
*/
|
|
8
|
+
export default class SingleAxisWindowedSource extends SingleAxisLazySource {
|
|
9
|
+
#abortController = new AbortController();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @type {number[]}
|
|
13
|
+
*/
|
|
14
|
+
#lastQuantizedInterval = [0, 0];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @type {{windowSize?: number}}
|
|
18
|
+
* @protected
|
|
19
|
+
*/
|
|
20
|
+
params;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {import("../../../spec/data.js").DebouncedData} debounceParams
|
|
24
|
+
* @protected
|
|
25
|
+
*/
|
|
26
|
+
setupDebouncing(debounceParams) {
|
|
27
|
+
if (debounceParams.debounce > 0) {
|
|
28
|
+
if (debounceParams.debounceMode == "domain") {
|
|
29
|
+
this.onDomainChanged = debounce(
|
|
30
|
+
this.onDomainChanged.bind(this),
|
|
31
|
+
debounceParams.debounce,
|
|
32
|
+
false
|
|
33
|
+
);
|
|
34
|
+
} else if (debounceParams.debounceMode == "window") {
|
|
35
|
+
this.loadInterval = debounce(
|
|
36
|
+
this.loadInterval.bind(this),
|
|
37
|
+
debounceParams.debounce,
|
|
38
|
+
false
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Listen to the domain change event and update data when the covered windows change.
|
|
46
|
+
*
|
|
47
|
+
* @param {number[]} domain Linearized domain
|
|
48
|
+
*/
|
|
49
|
+
async onDomainChanged(domain) {
|
|
50
|
+
const windowSize = this.params?.windowSize ?? -1;
|
|
51
|
+
|
|
52
|
+
if (domain[1] - domain[0] > windowSize) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const quantizedInterval = this.quantizeInterval(domain, windowSize);
|
|
57
|
+
|
|
58
|
+
if (this.checkAndUpdateLastInterval(quantizedInterval)) {
|
|
59
|
+
// Possible metadata must be loaded before the first request.
|
|
60
|
+
await this.initializedPromise;
|
|
61
|
+
|
|
62
|
+
this.loadInterval(quantizedInterval);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Listen to the domain change event and update data when the covered windows change.
|
|
68
|
+
*
|
|
69
|
+
* @param {number[]} interval linearized domain
|
|
70
|
+
* @protected
|
|
71
|
+
*/
|
|
72
|
+
async loadInterval(interval) {
|
|
73
|
+
// Override me if needed
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Splits the interval into discrete chromosomal intervals – one for each chromosome –
|
|
78
|
+
* and loads the data for each of them. Handles abort signals and errors.
|
|
79
|
+
*
|
|
80
|
+
* @param {number[]} interval
|
|
81
|
+
* @param {(discreteInteval: import("@genome-spy/core/genome/genome.js").DiscreteChromosomeInterval, signal: AbortSignal) => Promise<T>} loader
|
|
82
|
+
* @return {Promise<T[]>}
|
|
83
|
+
* @template T
|
|
84
|
+
* @protected
|
|
85
|
+
*/
|
|
86
|
+
async discretizeAndLoad(interval, loader) {
|
|
87
|
+
// Abort previous requests
|
|
88
|
+
this.#abortController.abort();
|
|
89
|
+
|
|
90
|
+
this.setLoadingStatus(true);
|
|
91
|
+
|
|
92
|
+
this.#abortController = new AbortController();
|
|
93
|
+
const signal = this.#abortController.signal;
|
|
94
|
+
|
|
95
|
+
// GMOD libraries expect a single chromosome/sequence for each request.
|
|
96
|
+
// Thus, we split the interval into discrete intervals representing
|
|
97
|
+
// individual chromosomes and load the data for each of them separately
|
|
98
|
+
// but in parallel.
|
|
99
|
+
const discreteChromosomeIntervals =
|
|
100
|
+
this.genome.continuousToDiscreteChromosomeIntervals(interval);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const resultByChrom = await Promise.all(
|
|
104
|
+
discreteChromosomeIntervals.map(async (d) => loader(d, signal))
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (!signal.aborted) {
|
|
108
|
+
this.setLoadingStatus(false);
|
|
109
|
+
return resultByChrom;
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
if (!signal.aborted) {
|
|
113
|
+
// TODO: Nice reporting of errors
|
|
114
|
+
this.setLoadingStatus(false);
|
|
115
|
+
throw e;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns three consecutive windows. The idea is to immediately have some data
|
|
122
|
+
* to show to the user when they pan the view. The windows are conceptually
|
|
123
|
+
* similar to "tiles" but they are never loaded separately.
|
|
124
|
+
*
|
|
125
|
+
* @param {number[]} interval
|
|
126
|
+
* @param {number} windowSize
|
|
127
|
+
* @protected
|
|
128
|
+
*/
|
|
129
|
+
quantizeInterval(interval, windowSize) {
|
|
130
|
+
return [
|
|
131
|
+
Math.max(Math.floor(interval[0] / windowSize - 1) * windowSize, 0),
|
|
132
|
+
Math.min(
|
|
133
|
+
Math.ceil(interval[1] / windowSize + 1) * windowSize,
|
|
134
|
+
this.genome.totalSize // Perhaps scale domain should be used here
|
|
135
|
+
),
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* @param {number[]} interval
|
|
142
|
+
* @protected
|
|
143
|
+
*/
|
|
144
|
+
checkAndUpdateLastInterval(interval) {
|
|
145
|
+
if (shallowArrayEquals(this.#lastQuantizedInterval, interval)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.#lastQuantizedInterval = interval;
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|