higlass 1.13.2 → 1.13.4

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.
Files changed (244) hide show
  1. package/README.md +0 -2
  2. package/app/globals.d.ts +4 -4
  3. package/app/missing-types.d.ts +3 -3
  4. package/app/scripts/AddTrackDialog.jsx +1 -0
  5. package/app/scripts/AddTrackPositionMenu.jsx +17 -4
  6. package/app/scripts/Annotations1dTrack.js +1 -0
  7. package/app/scripts/Annotations2dTrack.js +1 -0
  8. package/app/scripts/ArrowheadDomainsTrack.js +1 -0
  9. package/app/scripts/Autocomplete.jsx +1 -0
  10. package/app/scripts/AxisPixi.js +1 -0
  11. package/app/scripts/BarTrack.js +1 -0
  12. package/app/scripts/BedLikeTrack.js +1 -0
  13. package/app/scripts/Button.jsx +1 -0
  14. package/app/scripts/CNVIntervalTrack.js +1 -0
  15. package/app/scripts/CenterTiledPlot.jsx +1 -0
  16. package/app/scripts/CenterTrack.jsx +1 -0
  17. package/app/scripts/Chromosome2DAnnotations.js +1 -0
  18. package/app/scripts/Chromosome2DLabels.js +1 -0
  19. package/app/scripts/ChromosomeGrid.js +1 -0
  20. package/app/scripts/ChromosomeInfo.js +1 -0
  21. package/app/scripts/CloseTrackMenu.jsx +1 -0
  22. package/app/scripts/CombinedTrack.js +1 -0
  23. package/app/scripts/ConfigTrackMenu.jsx +1 -0
  24. package/app/scripts/ConfigViewMenu.jsx +1 -0
  25. package/app/scripts/ConfigureSeriesMenu.jsx +1 -0
  26. package/app/scripts/ContextMenuContainer.jsx +1 -0
  27. package/app/scripts/ContextMenuItem.jsx +1 -0
  28. package/app/scripts/Cross.jsx +1 -0
  29. package/app/scripts/CrossRule.js +1 -0
  30. package/app/scripts/CustomTrackDialog.jsx +1 -0
  31. package/app/scripts/Dialog.jsx +1 -0
  32. package/app/scripts/DivergentBarTrack.js +1 -0
  33. package/app/scripts/DragListeningDiv.jsx +1 -0
  34. package/app/scripts/DraggableDiv.jsx +1 -0
  35. package/app/scripts/ExportLinkDialog.jsx +1 -0
  36. package/app/scripts/FixedTrack.jsx +1 -0
  37. package/app/scripts/GalleryTracks.jsx +1 -0
  38. package/app/scripts/GenomePositionSearchBox.jsx +1 -0
  39. package/app/scripts/HeatmapOptions.jsx +1 -0
  40. package/app/scripts/HeatmapTiledPixiTrack.js +1 -0
  41. package/app/scripts/HiGlassComponent.jsx +1 -0
  42. package/app/scripts/HiGlassTrackComponent.jsx +1 -0
  43. package/app/scripts/Horizontal1dHeatmapTrack.js +1 -0
  44. package/app/scripts/Horizontal2DDomainsTrack.js +1 -0
  45. package/app/scripts/HorizontalChromosomeLabels.js +1 -0
  46. package/app/scripts/HorizontalGeneAnnotationsTrack.js +1 -0
  47. package/app/scripts/HorizontalHeatmapTrack.js +1 -0
  48. package/app/scripts/HorizontalItem.jsx +1 -0
  49. package/app/scripts/HorizontalLine1DPixiTrack.js +1 -0
  50. package/app/scripts/HorizontalMultivecTrack.js +1 -0
  51. package/app/scripts/HorizontalPoint1DPixiTrack.js +1 -0
  52. package/app/scripts/HorizontalRule.js +1 -0
  53. package/app/scripts/HorizontalTiled1DPixiTrack.js +1 -0
  54. package/app/scripts/HorizontalTiledPlot.jsx +1 -0
  55. package/app/scripts/HorizontalTrack.jsx +1 -0
  56. package/app/scripts/Id2DTiledPixiTrack.js +1 -0
  57. package/app/scripts/IdHorizontal1DTiledPixiTrack.js +1 -0
  58. package/app/scripts/IdVertical1DTiledPixiTrack.js +1 -0
  59. package/app/scripts/LeftAxisTrack.js +1 -0
  60. package/app/scripts/LeftTrackModifier.js +1 -0
  61. package/app/scripts/ListWrapper.jsx +1 -0
  62. package/app/scripts/MapboxTilesTrack.js +1 -0
  63. package/app/scripts/Modal.jsx +1 -0
  64. package/app/scripts/MoveableTrack.jsx +1 -0
  65. package/app/scripts/NestedContextMenu.jsx +1 -0
  66. package/app/scripts/OSMTileIdsTrack.js +1 -0
  67. package/app/scripts/OSMTilesTrack.js +1 -0
  68. package/app/scripts/OverlayTrack.js +1 -0
  69. package/app/scripts/PixiTrack.js +0 -1
  70. package/app/scripts/PlotTypeChooser.jsx +1 -0
  71. package/app/scripts/PopupMenu.jsx +1 -0
  72. package/app/scripts/RasterTilesTrack.js +1 -0
  73. package/app/scripts/RuleMixin.js +1 -0
  74. package/app/scripts/SVGTrack.js +1 -0
  75. package/app/scripts/SearchField.js +1 -0
  76. package/app/scripts/SeriesListItems.jsx +1 -0
  77. package/app/scripts/SeriesListMenu.jsx +1 -0
  78. package/app/scripts/SeriesListSubmenuMixin.jsx +1 -0
  79. package/app/scripts/SketchInlinePicker.jsx +1 -0
  80. package/app/scripts/SortableList.jsx +1 -0
  81. package/app/scripts/SquareMarkersTrack.js +1 -0
  82. package/app/scripts/Tiled1DPixiTrack.js +1 -0
  83. package/app/scripts/TiledPixiTrack.js +1 -0
  84. package/app/scripts/TiledPlot.jsx +9 -0
  85. package/app/scripts/TilesetFinder.jsx +1 -0
  86. package/app/scripts/TopAxisTrack.js +1 -0
  87. package/app/scripts/Track.js +0 -1
  88. package/app/scripts/TrackArea.jsx +1 -0
  89. package/app/scripts/TrackControl.jsx +1 -0
  90. package/app/scripts/TrackRenderer.jsx +0 -1
  91. package/app/scripts/UnknownPixiTrack.js +1 -0
  92. package/app/scripts/ValueIntervalTrack.js +1 -0
  93. package/app/scripts/VerticalItem.jsx +1 -0
  94. package/app/scripts/VerticalRule.js +1 -0
  95. package/app/scripts/VerticalTiled1DPixiTrack.js +1 -0
  96. package/app/scripts/VerticalTiledPlot.jsx +1 -0
  97. package/app/scripts/VerticalTrack.jsx +1 -0
  98. package/app/scripts/ViewConfigEditor.jsx +1 -0
  99. package/app/scripts/ViewContextMenu.jsx +1 -0
  100. package/app/scripts/ViewHeader.jsx +1 -0
  101. package/app/scripts/ViewportTracker2D.js +1 -0
  102. package/app/scripts/ViewportTracker2DPixi.js +1 -0
  103. package/app/scripts/ViewportTrackerHorizontal.js +1 -0
  104. package/app/scripts/ViewportTrackerVertical.js +1 -0
  105. package/app/scripts/api.js +1 -0
  106. package/app/scripts/configs/available-track-types.js +1 -0
  107. package/app/scripts/configs/colormaps.js +1 -0
  108. package/app/scripts/configs/datatype-to-track-type.js +1 -0
  109. package/app/scripts/configs/default-tracks-for-datatype.js +0 -1
  110. package/app/scripts/configs/dense-data-extrema-config.js +1 -0
  111. package/app/scripts/configs/globals.js +1 -0
  112. package/app/scripts/configs/index.js +1 -0
  113. package/app/scripts/configs/positions-by-datatype.js +1 -0
  114. package/app/scripts/configs/primitives.js +0 -1
  115. package/app/scripts/configs/themes.js +1 -0
  116. package/app/scripts/configs/tracks-info-by-type.js +0 -1
  117. package/app/scripts/configs/tracks-info.js +0 -1
  118. package/app/scripts/d3-context-menu.js +1 -0
  119. package/app/scripts/data-fetchers/DataFetcher.js +193 -153
  120. package/app/scripts/data-fetchers/genbank-fetcher.js +205 -87
  121. package/app/scripts/data-fetchers/index.js +1 -0
  122. package/app/scripts/data-fetchers/local-tile-fetcher.js +37 -4
  123. package/app/scripts/hglib.jsx +1 -0
  124. package/app/scripts/hocs/with-modal.jsx +1 -0
  125. package/app/scripts/hocs/with-pub-sub.jsx +1 -0
  126. package/app/scripts/hocs/with-theme.jsx +1 -0
  127. package/app/scripts/icons.jsx +1 -0
  128. package/app/scripts/mixwith.js +1 -0
  129. package/app/scripts/options-info.js +1 -0
  130. package/app/scripts/plugins/available-for-plugins.js +1 -0
  131. package/app/scripts/plugins/get-data-fetcher.js +1 -0
  132. package/app/scripts/plugins/index.js +1 -0
  133. package/app/scripts/services/chrom-info.js +1 -0
  134. package/app/scripts/services/dom-event.js +1 -0
  135. package/app/scripts/services/element-resize-listener.js +1 -0
  136. package/app/scripts/services/index.js +1 -0
  137. package/app/scripts/services/tile-proxy.js +14 -10
  138. package/app/scripts/services/worker.js +161 -84
  139. package/app/scripts/symbol.js +1 -0
  140. package/app/scripts/test-helpers/index.js +1 -0
  141. package/app/scripts/test-helpers/test-helpers.jsx +1 -0
  142. package/app/scripts/track-utils.js +1 -0
  143. package/app/scripts/types.ts +59 -22
  144. package/app/scripts/utils/DenseDataExtrema1D.js +32 -19
  145. package/app/scripts/utils/DenseDataExtrema2D.js +51 -31
  146. package/app/scripts/utils/LruCache.js +1 -0
  147. package/app/scripts/utils/abs-to-chr.js +0 -1
  148. package/app/scripts/utils/accessor-transposition.js +0 -2
  149. package/app/scripts/utils/add-arrays.js +0 -2
  150. package/app/scripts/utils/add-class.js +0 -1
  151. package/app/scripts/utils/add-event-listener-once.js +0 -2
  152. package/app/scripts/utils/background-task-scheduler.js +0 -2
  153. package/app/scripts/utils/base64-to-canvas.js +0 -2
  154. package/app/scripts/utils/chr-to-abs.js +0 -2
  155. package/app/scripts/utils/chrom-info-bisector.js +0 -1
  156. package/app/scripts/utils/clone-event.js +0 -2
  157. package/app/scripts/utils/color-domain-to-rgba-array.js +1 -0
  158. package/app/scripts/utils/color-to-hex.js +0 -1
  159. package/app/scripts/utils/color-to-rgba.js +0 -1
  160. package/app/scripts/utils/data-to-genomic-loci.js +0 -1
  161. package/app/scripts/utils/debounce.js +0 -2
  162. package/app/scripts/utils/dec-to-hex-str.js +0 -1
  163. package/app/scripts/utils/dict-from-tuples.js +0 -2
  164. package/app/scripts/utils/dict-items.js +0 -2
  165. package/app/scripts/utils/dict-keys.js +0 -2
  166. package/app/scripts/utils/dict-values.js +0 -2
  167. package/app/scripts/utils/download.js +0 -2
  168. package/app/scripts/utils/expand-combined-tracks.js +1 -0
  169. package/app/scripts/utils/fill-in-min-widths.js +1 -0
  170. package/app/scripts/utils/flatten.js +1 -0
  171. package/app/scripts/utils/for-each.js +0 -1
  172. package/app/scripts/utils/forward-event.js +0 -1
  173. package/app/scripts/utils/genome-loci-to-pixels.js +0 -1
  174. package/app/scripts/utils/genomic-range-to-chromosome-chunks.js +0 -2
  175. package/app/scripts/utils/get-aggregation-function.js +6 -12
  176. package/app/scripts/utils/get-default-track-for-datatype.js +1 -0
  177. package/app/scripts/utils/get-element-dim.js +0 -1
  178. package/app/scripts/utils/get-higlass-components.js +1 -0
  179. package/app/scripts/utils/get-track-by-uid.js +1 -0
  180. package/app/scripts/utils/get-track-conf-from-hgc.js +1 -0
  181. package/app/scripts/utils/get-track-obj-by-id.js +1 -0
  182. package/app/scripts/utils/get-track-position-by-uid.js +1 -0
  183. package/app/scripts/utils/get-xylofon.js +1 -0
  184. package/app/scripts/utils/gradient.js +0 -2
  185. package/app/scripts/utils/has-class.js +0 -2
  186. package/app/scripts/utils/has-parent.js +1 -0
  187. package/app/scripts/utils/hex-string-to-int.js +0 -1
  188. package/app/scripts/utils/index.js +0 -1
  189. package/app/scripts/utils/interval-tree.js +1 -0
  190. package/app/scripts/utils/into-the-void.js +0 -1
  191. package/app/scripts/utils/is-track-or-child-track.js +0 -1
  192. package/app/scripts/utils/is-track-range-selectable.js +0 -1
  193. package/app/scripts/utils/is-within.js +0 -2
  194. package/app/scripts/utils/lat-to-y.js +0 -2
  195. package/app/scripts/utils/lng-to-x.js +0 -1
  196. package/app/scripts/utils/load-chrom-infos.js +1 -0
  197. package/app/scripts/utils/map.js +0 -1
  198. package/app/scripts/utils/max-non-zero.js +0 -1
  199. package/app/scripts/utils/max.js +0 -1
  200. package/app/scripts/utils/min-non-zero.js +0 -1
  201. package/app/scripts/utils/min.js +0 -1
  202. package/app/scripts/utils/mod.js +0 -1
  203. package/app/scripts/utils/ndarray-assign.js +1 -0
  204. package/app/scripts/utils/ndarray-flatten.js +1 -0
  205. package/app/scripts/utils/ndarray-to-list.js +1 -0
  206. package/app/scripts/utils/numericify-version.js +0 -1
  207. package/app/scripts/utils/obj-vals.js +1 -0
  208. package/app/scripts/utils/or.js +0 -1
  209. package/app/scripts/utils/parse-chromsizes-rows.js +0 -2
  210. package/app/scripts/utils/pixi-text-to-svg.js +1 -0
  211. package/app/scripts/utils/q.js +1 -0
  212. package/app/scripts/utils/rad-to-deg.js +0 -1
  213. package/app/scripts/utils/range-query-2d.js +1 -0
  214. package/app/scripts/utils/reduce.js +1 -0
  215. package/app/scripts/utils/rel-to-abs-chrom-pos.js +0 -2
  216. package/app/scripts/utils/remove-class.js +0 -1
  217. package/app/scripts/utils/reset-d3-brush-style.js +0 -2
  218. package/app/scripts/utils/rgb-to-hex.js +0 -2
  219. package/app/scripts/utils/scales-center-and-k.js +0 -2
  220. package/app/scripts/utils/scales-to-genome-loci.js +0 -1
  221. package/app/scripts/utils/segments-to-rows.js +1 -0
  222. package/app/scripts/utils/selected-items-to-cum-weights.js +0 -1
  223. package/app/scripts/utils/selected-items-to-size.js +0 -1
  224. package/app/scripts/utils/show-mouse-position.js +0 -1
  225. package/app/scripts/utils/some.js +0 -1
  226. package/app/scripts/utils/sum.js +0 -1
  227. package/app/scripts/utils/svg-line.js +1 -0
  228. package/app/scripts/utils/throttle-and-debounce.js +0 -1
  229. package/app/scripts/utils/tile-to-canvas.js +0 -1
  230. package/app/scripts/utils/timeout.js +0 -1
  231. package/app/scripts/utils/to-void.js +0 -1
  232. package/app/scripts/utils/total-track-pixel-height.js +0 -1
  233. package/app/scripts/utils/trim-trailing-slash.js +0 -1
  234. package/app/scripts/utils/type-guards.js +0 -2
  235. package/app/scripts/utils/value-to-color.js +0 -1
  236. package/app/scripts/utils/visit-positioned-tracks.js +0 -1
  237. package/app/scripts/utils/visit-tracks.js +0 -1
  238. package/dist/esm.html +13 -14
  239. package/dist/hglib.css +1 -1724
  240. package/dist/hglib.js +122664 -28
  241. package/dist/hglib.min.js +116 -119
  242. package/dist/higlass.mjs +122646 -28
  243. package/dist/index.html +5 -6
  244. package/package.json +23 -16
@@ -12,11 +12,55 @@ import {
12
12
  // Services
13
13
  import { tileProxy } from '../services';
14
14
 
15
+ /** @typedef {import('../types').DataConfig} DataConfig */
16
+ /** @typedef {import('../types').TilesetInfo} TilesetInfo */
17
+ /**
18
+ * @template T
19
+ * @typedef {import('../types').AbstractDataFetcher<T>} AbstractDataFetcher
20
+ */
21
+
22
+ /**
23
+ * @typedef Tile
24
+ * @property {number} min_value
25
+ * @property {number} max_value
26
+ * @property {DenseDataExtrema1D | DenseDataExtrema2D} denseDataExtrema
27
+ * @property {number} minNonZero
28
+ * @property {number} maxNonZero
29
+ * @property {Array<number> | Float32Array} dense
30
+ * @property {string} dtype
31
+ * @property {string} server
32
+ * @property {number[]} tilePos
33
+ * @property {string} tilePositionId
34
+ * @property {string} tilesetUid
35
+ * @property {number} zoomLevel
36
+ */
37
+
38
+ /** @typedef {Pick<Tile, 'zoomLevel' | 'tilePos' | 'tilePositionId'>} DividedTileA */
39
+ /** @typedef {Pick<Tile, 'zoomLevel' | 'tilePos' | 'tilePositionId' | 'dense' | 'denseDataExtrema' | 'minNonZero' | 'maxNonZero'>} DividedTileB */
40
+ /** @typedef {DividedTileA | DividedTileB} DividedTile */
41
+ /** @typedef {Omit<DataConfig, 'children'> & { children?: DataFetcher[], tilesetUid?: string, tilesetInfo: TilesetInfo }} ResolvedDataConfig */
42
+
43
+ /**
44
+ * @template T
45
+ * @param {Array<T>} x
46
+ * @returns {x is [T, T]}
47
+ */
48
+ function isTuple(x) {
49
+ return x.length === 2;
50
+ }
51
+
52
+ /** @implements {AbstractDataFetcher<Tile | DividedTile>} */
15
53
  export default class DataFetcher {
54
+ /**
55
+ * @param {import('../types').DataConfig} dataConfig
56
+ * @param {import('pub-sub-es').PubSub} pubSub
57
+ */
16
58
  constructor(dataConfig, pubSub) {
59
+ /** @type {boolean} */
17
60
  this.tilesetInfoLoading = true;
18
61
 
19
62
  if (!dataConfig) {
63
+ // Trevor: This should probably throw?
20
64
  console.error('No dataconfig provided');
21
65
  return;
22
66
  }
@@ -24,11 +68,14 @@ export default class DataFetcher {
24
68
  // copy the dataConfig so that it doesn't dirty so that
25
69
  // it doesn't get modified when we make objects of its
26
70
  // children below
71
+ /** @type {ResolvedDataConfig} */
27
72
  this.dataConfig = JSON.parse(JSON.stringify(dataConfig));
73
+ /** @type {string} */
28
74
  this.uuid = slugid.nice();
75
+ /** @type {import('pub-sub-es').PubSub} */
29
76
  this.pubSub = pubSub;
30
77
 
31
- if (this.dataConfig.children) {
78
+ if (dataConfig.children) {
32
79
  // convert each child into an object
33
80
  this.dataConfig.children = dataConfig.children.map(
34
81
  (c) => new DataFetcher(c, pubSub),
@@ -41,10 +88,11 @@ export default class DataFetcher {
41
88
  * and server. Using these, we can use the server to fullfill tile requests
42
89
  * from this dataset.
43
90
  *
44
- * @param {string} server The server api location (e.g. 'localhost:8000/api/v1')
45
- * @param {string} url The location of the data file (e.g. 'encode.org/my.file.bigwig')
46
- * @param {string} filetype The type of file being served (e.g. 'bigwig')
47
- * @param {string} coordSystem The coordinate system being served (e.g. 'hg38')
91
+ * @param {object} opts
92
+ * @param {string} opts.server - The server api location (e.g. 'localhost:8000/api/v1')
93
+ * @param {string} opts.url - The location of the data file (e.g. 'encode.org/my.file.bigwig')
94
+ * @param {string} opts.filetype - The type of file being served (e.g. 'bigwig')
95
+ * @param {string=} opts.coordSystem - The coordinate system being served (e.g. 'hg38')
48
96
  */
49
97
  async registerFileUrl({ server, url, filetype, coordSystem }) {
50
98
  const serverUrl = `${tts(server)}/register_url/`;
@@ -64,15 +112,16 @@ export default class DataFetcher {
64
112
  });
65
113
  }
66
114
 
115
+ /**
116
+ * Obtain tileset infos for all of the tilesets listed
117
+ * @param {import('../types').HandleTilesetInfoFinished} finished - A callback that will be called
118
+ */
67
119
  tilesetInfo(finished) {
68
120
  // if this track has a url, server and filetype
69
121
  // then we need to register those with the server
70
- if (
71
- this.dataConfig.server &&
72
- this.dataConfig.url &&
73
- this.dataConfig.filetype
74
- ) {
75
- return this.registerFileUrl(this.dataConfig)
122
+ const { server, url, filetype, coordSystem } = this.dataConfig;
123
+ if (server && url && filetype) {
124
+ return this.registerFileUrl({ server, url, filetype, coordSystem })
76
125
  .then((data) => data.json())
77
126
  .then((data) => {
78
127
  this.dataConfig.tilesetUid = data.uid;
@@ -96,18 +145,15 @@ export default class DataFetcher {
96
145
  * infos have the same dimensions and then return a common
97
146
  * one.
98
147
  *
99
- * Paremeters
100
- * ----------
101
- * @param {function} finished A callback that will be called
148
+ * @param {import('../types').HandleTilesetInfoFinished} finished - A callback that will be called
102
149
  * when all tileset infos are loaded
103
150
  */
104
- tilesetInfoAfterRegister(finished /* , errorCb */) {
105
- // console.log('dataConfig', this.dataConfig);
106
-
151
+ tilesetInfoAfterRegister(finished) {
107
152
  if (!this.dataConfig.children) {
108
- // this data source has no children so we just need to retrieve one tileset
109
- // info
110
- if (!this.dataConfig.server || !this.dataConfig.tilesetUid) {
153
+ // this data source has no children so we
154
+ // just need to retrieve one tileset info
155
+ const { server, tilesetUid } = this.dataConfig;
156
+ if (!server || !tilesetUid) {
111
157
  console.warn(
112
158
  'No dataConfig children, server or tilesetUid:',
113
159
  this.dataConfig,
@@ -116,23 +162,17 @@ export default class DataFetcher {
116
162
  } else {
117
163
  // pass in the callback
118
164
  tileProxy.trackInfo(
119
- this.dataConfig.server,
120
- this.dataConfig.tilesetUid,
121
- (tilesetInfo) => {
165
+ server,
166
+ tilesetUid,
167
+ (/** @type {Record<string, TilesetInfo>} */ tilesetInfo) => {
122
168
  // tileset infos are indxed by by tilesetUids, we can just resolve
123
169
  // that here before passing it back to the track
124
- this.dataConfig.tilesetInfo =
125
- tilesetInfo[this.dataConfig.tilesetUid];
126
- finished(
127
- tilesetInfo[this.dataConfig.tilesetUid],
128
- this.dataConfig.tilesetUid,
129
- );
170
+ this.dataConfig.tilesetInfo = tilesetInfo[tilesetUid];
171
+ finished(tilesetInfo[tilesetUid], tilesetUid);
130
172
  },
131
- (error) => {
173
+ (/** @type {string} */ error) => {
132
174
  this.tilesetInfoLoading = false;
133
- finished({
134
- error,
135
- });
175
+ finished({ error });
136
176
  },
137
177
  this.pubSub,
138
178
  );
@@ -142,6 +182,7 @@ export default class DataFetcher {
142
182
  // all of their tileset infos in order to return them to the track
143
183
  const promises = this.dataConfig.children.map(
144
184
  (x) =>
185
+ /** @type {Promise<TilesetInfo>} */
145
186
  new Promise((resolve) => {
146
187
  x.tilesetInfo(resolve);
147
188
  }),
@@ -155,50 +196,42 @@ export default class DataFetcher {
155
196
  }
156
197
  }
157
198
 
199
+ /**
200
+ * @param {string} tilesetUid - Uid of the tileset on the server
201
+ * @param {string} tileId - The tileId of the tile
202
+ * @returns {string} The full tile id that the server will parse.
203
+ *
204
+ * @example
205
+ * ```javascript
206
+ * // returns 'xyxx.0.0.0'
207
+ * fullTileId('xyxx', '0.0.0');
208
+ * ```
209
+ */
158
210
  fullTileId(tilesetUid, tileId) {
159
- /**
160
- * Convert a tilesetUid and tileId into a full tile
161
- * identifier
162
- *
163
- * Parameters
164
- * ----------
165
- * tilesetUid: string
166
- * Uid of the tileset on the server
167
- *
168
- * tileId: string
169
- * The tileId of the tile
170
- *
171
- * Returns
172
- * -------
173
- * fullTileId: string
174
- * The full tile id that the server will parse.
175
- * E.g. xyxx.0.0.0.default
176
- */
177
-
178
211
  return `${tilesetUid}.${tileId}`;
179
212
  }
180
213
 
214
+ /**
215
+ * Fetch a set of tiles.
216
+ *
217
+ * Because the track shouldn't care about tileset ids, the tile ids
218
+ * should just include positions and any necessary transforms.
219
+ *
220
+ * @param {(tiles: Record<string, DividedTile | Tile>) => void} receivedTiles - A function to call once the tiles have been fetched
221
+ * @param {string[]} tileIds - The tile ids to fetch
222
+ * @returns {Promise<Record<string, DividedTile | Tile>>}
223
+ */
181
224
  fetchTilesDebounced(receivedTiles, tileIds) {
182
- /**
183
- * Fetch a set of tiles.
184
- *
185
- * Because the track shouldn't care about tileset ids, the tile ids
186
- * should just include positions and any necessary transforms.
187
- *
188
- * Parameters
189
- * ----------
190
- * receivedTiles: callback
191
- * A function to call once the tiles have been
192
- * fetched
193
- * tileIds: []
194
- * The tile ids to fetch
195
- */
196
225
  if (this.dataConfig.type === 'horizontal-section') {
197
- this.fetchHorizontalSection(receivedTiles, tileIds);
198
- } else if (this.dataConfig.type === 'vertical-section') {
199
- this.fetchHorizontalSection(receivedTiles, tileIds, true);
200
- } else if (!this.dataConfig.children && this.dataConfig.tilesetUid) {
226
+ return this.fetchHorizontalSection(receivedTiles, tileIds);
227
+ }
228
+ if (this.dataConfig.type === 'vertical-section') {
229
+ return this.fetchHorizontalSection(receivedTiles, tileIds, true);
230
+ }
231
+
232
+ if (!this.dataConfig.children && this.dataConfig.tilesetUid) {
201
233
  // no children, just return the fetched tiles as is
234
+ /** @type {Promise<Record<string, Tile>>} */
202
235
  const promise = new Promise((resolve) => {
203
236
  tileProxy.fetchTilesDebounced(
204
237
  {
@@ -213,8 +246,9 @@ export default class DataFetcher {
213
246
  );
214
247
  });
215
248
 
216
- promise.then((returnedTiles) => {
249
+ return promise.then((returnedTiles) => {
217
250
  const tilesetUid = dictValues(returnedTiles)[0].tilesetUid;
251
+ /** @type {Record<string, Tile>} */
218
252
  const newTiles = {};
219
253
 
220
254
  for (let i = 0; i < tileIds.length; i++) {
@@ -223,60 +257,50 @@ export default class DataFetcher {
223
257
  returnedTiles[fullTileId].tilePositionId = tileIds[i];
224
258
  newTiles[tileIds[i]] = returnedTiles[fullTileId];
225
259
  }
226
-
227
260
  receivedTiles(newTiles);
261
+ return newTiles;
228
262
  });
229
- } else {
230
- // multiple child tracks, need to wait for all of them to
231
- // fetch their data before returning to the parent
232
- const promises = this.dataConfig.children.map(
233
- (x) =>
234
- new Promise(
235
- (resolve) => {
236
- x.fetchTilesDebounced(resolve, tileIds);
237
- },
238
- this.pubSub,
239
- true,
240
- ),
241
- );
242
-
243
- Promise.all(promises).then((returnedTiles) => {
244
- // if we're trying to divide two datasets,
245
- if (this.dataConfig.type === 'divided') {
246
- const newTiles = this.makeDivided(returnedTiles, tileIds);
263
+ }
247
264
 
248
- receivedTiles(newTiles);
249
- } else {
250
- // assume we're just returning raw tiles
251
- console.warn(
252
- 'Unimplemented dataConfig type. Returning first data source.',
253
- this.dataConfig,
254
- );
265
+ // multiple child tracks, need to wait for all of them to
266
+ // fetch their data before returning to the parent
267
+ /** @type {Promise<Record<string, DividedTile | Tile>>[]} Tiles */
268
+ const promises =
269
+ this.dataConfig.children?.map(
270
+ (x) =>
271
+ /** @type {Promise<Record<string, Tile | DividedTile>>} */
272
+ new Promise((resolve) => {
273
+ x.fetchTilesDebounced(resolve, tileIds);
274
+ }),
275
+ ) ?? [];
255
276
 
256
- receivedTiles(returnedTiles[0]);
257
- }
258
- });
259
- }
277
+ return Promise.all(promises).then((returnedTiles) => {
278
+ // if we're trying to divide two datasets,
279
+ if (this.dataConfig.type === 'divided' && isTuple(returnedTiles)) {
280
+ const newTiles = this.makeDivided(returnedTiles, tileIds);
281
+ receivedTiles(newTiles);
282
+ return newTiles;
283
+ }
284
+ // assume we're just returning raw tiles
285
+ console.warn(
286
+ 'Unimplemented dataConfig type. Returning first data source.',
287
+ this.dataConfig,
288
+ );
289
+ receivedTiles(returnedTiles[0]);
290
+ return returnedTiles[0];
291
+ });
260
292
  }
261
293
 
294
+ /**
295
+ * Return an array consisting of the division of the numerator
296
+ * array by the denominator array
297
+ *
298
+ * @param {ArrayLike<number>} numeratorData - An array of numerical values
299
+ * @param {ArrayLike<number>} denominatorData - An array of numerical values
300
+ *
301
+ * @returns {Float32Array} An array consisting of the division of the numerator by the denominator
302
+ */
262
303
  divideData(numeratorData, denominatorData) {
263
- /**
264
- * Return an array consisting of the division of the numerator
265
- * array by the denominator array
266
- *
267
- * Parameters
268
- * ----------
269
- * numeratorData: array
270
- * An array of numerical values
271
- * denominatorData:
272
- * An array of numerical values
273
- *
274
- * Returns
275
- * -------
276
- * divided: array
277
- * An array consisting of the division of the
278
- * numerator by the denominator
279
- */
280
304
  const result = new Float32Array(numeratorData.length);
281
305
 
282
306
  for (let i = 0; i < result.length; i++) {
@@ -301,11 +325,12 @@ export default class DataFetcher {
301
325
  /**
302
326
  * Extract a slice from a matrix at a given position.
303
327
  *
304
- * @param {array} inputData: An array containing a matrix stored row-wise
305
- * @param {array} arrayShape: The shape of the array, should be a
328
+ * @param {Array<number>} inputData - An array containing a matrix stored row-wise
329
+ * @param {Array<number>} arrayShape - The shape of the array, should be a
306
330
  * two element array e.g. [256,256].
307
- * @param {int} sliceIndex: The index across which to take the slice
308
- * @returns {array} an array corresponding to a slice of this matrix
331
+ * @param {number} sliceIndex - The index across which to take the slice
332
+ * @param {number=} axis - The axis along which to take the slice
333
+ * @returns {Array<number>} an array corresponding to a slice of this matrix
309
334
  */
310
335
  extractDataSlice(inputData, arrayShape, sliceIndex, axis) {
311
336
  if (!axis) {
@@ -322,14 +347,28 @@ export default class DataFetcher {
322
347
  return returnArray;
323
348
  }
324
349
 
350
+ /**
351
+ * Fetch a horizontal section of a 2D dataset
352
+ * @param {(tiles: Record<string, Tile>) => void} receivedTiles - A function to call once the tiles have been fetched
353
+ * @param {string[]} tileIds - The tile ids to fetch
354
+ * @param {boolean=} vertical - Whether to fetch a vertical section
355
+ * @returns {Promise<Record<string, Tile>>}
356
+ */
325
357
  fetchHorizontalSection(receivedTiles, tileIds, vertical = false) {
326
358
  // We want to take a horizontal section of a 2D dataset
327
359
  // that means that a 1D track is requesting data from a 2D source
328
360
  // because the 1D track only requests 1D tiles, we need to calculate
329
361
  // the 2D tile from which to take the slice
362
+ /** @type {string[]} */
330
363
  const newTileIds = [];
364
+ /** @type {boolean[]} */
331
365
  const mirrored = [];
332
366
 
367
+ const { slicePos, tilesetInfo } = this.dataConfig;
368
+ if (!slicePos || !tilesetInfo) {
369
+ throw new Error('No slice position or tileset info');
370
+ }
371
+
333
372
  for (const tileId of tileIds) {
334
373
  const parts = tileId.split('.');
335
374
  const zoomLevel = +parts[0];
@@ -338,37 +377,31 @@ export default class DataFetcher {
338
377
  // this is a dummy scale that we'll use to fetch tile positions
339
378
  // along the y-axis of the 2D dataset (we already have the x positions
340
379
  // from the track that is querying this data)
341
- const scale = scaleLinear().domain([
342
- this.dataConfig.slicePos,
343
- this.dataConfig.slicePos,
344
- ]);
380
+ const scale = scaleLinear().domain([slicePos, slicePos]);
345
381
 
346
382
  // there's two different ways of calculating tile positions
347
383
  // this needs to be consolidated into one function eventually
348
384
  let yTiles = [];
349
385
 
350
- if (
351
- this.dataConfig.tilesetInfo &&
352
- this.dataConfig.tilesetInfo.resolutions
353
- ) {
354
- const sortedResolutions = this.dataConfig.tilesetInfo.resolutions
386
+ if ('resolutions' in tilesetInfo) {
387
+ const sortedResolutions = tilesetInfo.resolutions
355
388
  .map((x) => +x)
356
389
  .sort((a, b) => b - a);
357
390
 
358
391
  yTiles = tileProxy.calculateTilesFromResolution(
359
392
  sortedResolutions[zoomLevel],
360
393
  scale,
361
- this.dataConfig.tilesetInfo.min_pos[vertical ? 1 : 0],
362
- this.dataConfig.tilesetInfo.max_pos[vertical ? 1 : 0],
394
+ tilesetInfo.min_pos[vertical ? 1 : 0],
395
+ tilesetInfo.max_pos[vertical ? 1 : 0],
363
396
  );
364
397
  } else {
365
398
  yTiles = tileProxy.calculateTiles(
366
399
  zoomLevel,
367
400
  scale,
368
- this.dataConfig.tilesetInfo.min_pos[vertical ? 1 : 0],
369
- this.dataConfig.tilesetInfo.max_pos[vertical ? 1 : 0],
370
- this.dataConfig.tilesetInfo.max_zoom,
371
- this.dataConfig.tilesetInfo.max_width,
401
+ tilesetInfo.min_pos[vertical ? 1 : 0],
402
+ tilesetInfo.max_pos[vertical ? 1 : 0],
403
+ tilesetInfo.max_zoom,
404
+ tilesetInfo.max_width,
372
405
  );
373
406
  }
374
407
  const sortedPosition = [xTilePos, yTiles[0]].sort((a, b) => a - b);
@@ -398,12 +431,13 @@ export default class DataFetcher {
398
431
  true,
399
432
  );
400
433
  });
401
- promise.then((returnedTiles) => {
434
+ return promise.then((returnedTiles) => {
402
435
  // we've received some new tiles, but they're 2D
403
436
  // we need to extract the row corresponding to the data we need
404
437
 
405
438
  const tilesetUid = dictValues(returnedTiles)[0].tilesetUid;
406
439
  // console.log('tilesetUid:', tilesetUid);
440
+ /** @type {Record<string, Tile>} */
407
441
  const newTiles = {};
408
442
 
409
443
  for (let i = 0; i < newTileIds.length; i++) {
@@ -413,11 +447,15 @@ export default class DataFetcher {
413
447
  const yTilePos = +parts[2];
414
448
 
415
449
  const sliceIndex = tileProxy.calculateTileAndPosInTile(
416
- this.dataConfig.tilesetInfo,
417
- this.dataConfig.tilesetInfo.max_width,
418
- this.dataConfig.tilesetInfo.min_pos[1],
450
+ tilesetInfo,
451
+ // @ts-expect-error - This is undefined for legacy tilesets, but
452
+ // `calculateTileAndPosInTile` ignores this argument with `resolutions`.
453
+ // We should probably refactor `calculateTileAndPosInTile` to just take
454
+ // the `tilesetInfo` object.
455
+ tilesetInfo.max_width,
456
+ tilesetInfo.min_pos[1],
419
457
  zoomLevel,
420
- +this.dataConfig.slicePos,
458
+ +slicePos,
421
459
  )[1];
422
460
 
423
461
  const fullTileId = this.fullTileId(tilesetUid, newTileIds[i]);
@@ -469,9 +507,16 @@ export default class DataFetcher {
469
507
  }
470
508
 
471
509
  receivedTiles(newTiles);
510
+ return newTiles;
472
511
  });
473
512
  }
474
513
 
514
+ /**
515
+ * @typedef {{ zoomLevel: number, tilePos: number[], dense?: ArrayLike<number> }} Dividable
516
+ * @param {[Record<string, Dividable>, Record<string, Dividable>]} returnedTiles
517
+ * @param {string[]} tileIds
518
+ * @returns {Record<string, DividedTile>}
519
+ */
475
520
  makeDivided(returnedTiles, tileIds) {
476
521
  if (returnedTiles.length < 2) {
477
522
  console.warn(
@@ -480,9 +525,7 @@ export default class DataFetcher {
480
525
  );
481
526
  }
482
527
 
483
- // const numeratorTilesetUid = dictValues(returnedTiles[0])[0].tilesetUid;
484
- // const denominatorTilesetUid = dictValues(returnedTiles[1])[0].tilesetUid;
485
-
528
+ /** @type {Record<string, DividedTile>} */
486
529
  const newTiles = {};
487
530
 
488
531
  for (let i = 0; i < tileIds.length; i++) {
@@ -491,21 +534,18 @@ export default class DataFetcher {
491
534
  const zoomLevel = returnedTiles[0][tileIds[i]].zoomLevel;
492
535
  const tilePos = returnedTiles[0][tileIds[i]].tilePos;
493
536
 
537
+ /** @type {DividedTile} */
494
538
  let newTile = {
495
539
  zoomLevel,
496
540
  tilePos,
497
541
  tilePositionId: tileIds[i],
498
542
  };
499
543
 
500
- if (
501
- returnedTiles[0][tileIds[i]].dense &&
502
- returnedTiles[1][tileIds[i]].dense
503
- ) {
504
- const newData = this.divideData(
505
- returnedTiles[0][tileIds[i]].dense,
506
- returnedTiles[1][tileIds[i]].dense,
507
- );
544
+ const denseA = returnedTiles[0][tileIds[i]].dense;
545
+ const denseB = returnedTiles[1][tileIds[i]].dense;
508
546
 
547
+ if (denseA && denseB) {
548
+ const newData = this.divideData(denseA, denseB);
509
549
  const dde =
510
550
  tilePos.length === 2
511
551
  ? new DenseDataExtrema2D(newData)