gridviz 3.0.14 → 3.0.16

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 (55) hide show
  1. package/README.md +127 -128
  2. package/dist/gridviz.js +7959 -7860
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +74 -74
  5. package/src/button/ZoomButtons.js +101 -96
  6. package/src/core/Dataset.js +154 -157
  7. package/src/core/Drawable.js +60 -61
  8. package/src/core/GeoCanvas.js +409 -370
  9. package/src/core/Layer.js +23 -25
  10. package/src/core/Legend.js +108 -110
  11. package/src/core/Map.js +495 -495
  12. package/src/core/MultiResolutionDataset.js +93 -97
  13. package/src/core/Style.js +98 -99
  14. package/src/dataset/CSVGrid.js +112 -112
  15. package/src/dataset/GeoTIFF.js +122 -123
  16. package/src/dataset/JSGrid.js +55 -54
  17. package/src/dataset/TiledGrid.js +289 -288
  18. package/src/dataset/worker.js +18 -0
  19. package/src/index.js +77 -71
  20. package/src/layer/BackgroundLayer.js +162 -158
  21. package/src/layer/BackgroundLayerImage.js +81 -84
  22. package/src/layer/BackgroundLayerWMS.js +104 -108
  23. package/src/layer/GeoJSONLayer.js +214 -220
  24. package/src/layer/GridLayer.js +148 -157
  25. package/src/layer/LabelLayer.js +194 -193
  26. package/src/legend/ColorCategoryLegend.js +109 -110
  27. package/src/legend/ColorDiscreteLegend.js +100 -102
  28. package/src/legend/ColorLegend.js +115 -118
  29. package/src/legend/OrientationLegend.js +89 -91
  30. package/src/legend/SizeLegend.js +224 -224
  31. package/src/legend/TernaryLegend.js +302 -210
  32. package/src/style/CompositionStyle.js +291 -288
  33. package/src/style/DotDensityStyle.js +133 -134
  34. package/src/style/ImageStyle.js +100 -102
  35. package/src/style/IsoFenceStyle.js +217 -203
  36. package/src/style/JoyPlotStyle.js +127 -128
  37. package/src/style/LegoStyle.js +110 -110
  38. package/src/style/MosaicStyle.js +99 -99
  39. package/src/style/NinjaStarStyle.js +102 -102
  40. package/src/style/PillarStyle.js +207 -208
  41. package/src/style/SegmentStyle.js +105 -105
  42. package/src/style/ShapeColorSizeStyle.js +147 -132
  43. package/src/style/SideCategoryStyle.js +112 -114
  44. package/src/style/SideStyle.js +212 -205
  45. package/src/style/SquareColorCategoryWebGLStyle.js +107 -108
  46. package/src/style/SquareColorWebGLStyle.js +126 -126
  47. package/src/style/StrokeStyle.js +135 -136
  48. package/src/style/TanakaStyle.js +69 -68
  49. package/src/style/TextStyle.js +110 -111
  50. package/src/style/TimeSeriesStyle.js +222 -229
  51. package/src/utils/scale.js +218 -228
  52. package/src/utils/stretching.js +120 -133
  53. package/src/utils/ternary.js +203 -203
  54. package/src/utils/utils.js +58 -63
  55. package/src/utils/webGLUtils.js +72 -70
@@ -1,288 +1,289 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- /** @typedef {{ dims: object, crs: string, tileSizeCell: number, originPoint: {x:number,y:number}, resolutionGeo: number, tilingBounds:import("../core/GeoCanvas.js").Envelope }} GridInfo */
5
-
6
- // internal
7
- import { Dataset } from '../core/Dataset.js'
8
- //import { monitor, monitorDuration } from '../utils/Utils.js'
9
-
10
- // external
11
- import { json, csv } from 'd3-fetch'
12
-
13
- /**
14
- * A tiled dataset, composed of CSV tiles.
15
- *
16
- * @module dataset
17
- * @author Joseph Davies, Julien Gaffuri
18
- */
19
- export class TiledGrid extends Dataset {
20
- /**
21
- * @param {import("../core/Map.js").Map} map The map.
22
- * @param {string} url The URL of the dataset.
23
- * @param {{preprocess?:(function(import("../core/Dataset.js").Cell):boolean) }} opts
24
- */
25
- constructor(map, url, opts = {}) {
26
- super(map, url, 0, opts)
27
-
28
- /**
29
- * The grid info object, from the info.json file.
30
- * @type {GridInfo | undefined}
31
- * @private
32
- * */
33
- this.info = undefined
34
-
35
- /**
36
- * @type {string}
37
- * @private */
38
- this.infoLoadingStatus = 'notLoaded'
39
-
40
- /**
41
- * The cache of the loaded tiles. It is double indexed: by xT and then yT.
42
- * Example: this.cache[xT][yT] returns the tile at [xT][yT] location.
43
- *
44
- * @type {object}
45
- * */
46
- this.cache = {}
47
-
48
- //launch loading
49
- this.loadInfo()
50
- }
51
-
52
- /**
53
- * Load the info.json from the url.
54
- * @returns this
55
- */
56
- loadInfo() {
57
- if (!this.info && this.infoLoadingStatus === 'notLoaded') {
58
- ; (async () => {
59
- try {
60
- const data = await json(this.url + 'info.json')
61
- this.info = data
62
- this.resolution = data.resolutionGeo
63
- this.infoLoadingStatus = 'loaded'
64
- this.map.redraw()
65
- } catch (error) {
66
- //mark as failed
67
- this.infoLoadingStatus = 'failed'
68
- }
69
- })()
70
- } else if ((this.infoLoadingStatus === 'loaded' || this.infoLoadingStatus === 'failed'))
71
- this.map.redraw()
72
- return this
73
- }
74
-
75
- /**
76
- * Compute a tiling envelope from a geographical envelope.
77
- * This is the function to use to know which tiles to download for a geographical view.
78
- *
79
- * @param {import("../core/GeoCanvas.js").Envelope} e
80
- * @returns {import("../core/GeoCanvas.js").Envelope|undefined}
81
- */
82
- getTilingEnvelope(e) {
83
- if (!this.info) {
84
- this.loadInfo()
85
- return
86
- }
87
-
88
- const po = this.info.originPoint,
89
- r = this.info.resolutionGeo,
90
- s = this.info.tileSizeCell
91
-
92
- return {
93
- xMin: Math.floor((e.xMin - po.x) / (r * s)),
94
- xMax: Math.floor((e.xMax - po.x) / (r * s)),
95
- yMin: Math.floor((e.yMin - po.y) / (r * s)),
96
- yMax: Math.floor((e.yMax - po.y) / (r * s)),
97
- }
98
- }
99
-
100
- /**
101
- * Request data within a geographic envelope.
102
- *
103
- * @param {import('../core/GeoCanvas.js').Envelope} extGeo
104
- * @returns {this}
105
- */
106
- getData(extGeo) {
107
- //TODO empty cache when it gets too big ?
108
-
109
- //check if info has been loaded
110
- if (!this.info) return this
111
-
112
- //tiles within the scope
113
- /** @type {import("../core/GeoCanvas.js").Envelope|undefined} */
114
- const tb = this.getTilingEnvelope(extGeo)
115
- if (!tb) return this
116
-
117
- //grid bounds
118
- /** @type {import("../core/GeoCanvas.js").Envelope} */
119
- const gb = this.info.tilingBounds
120
-
121
- for (let xT = Math.max(tb.xMin, gb.xMin); xT <= Math.min(tb.xMax, gb.xMax); xT++) {
122
- for (let yT = Math.max(tb.yMin, gb.yMin); yT <= Math.min(tb.yMax, gb.yMax); yT++) {
123
- //prepare cache
124
- if (!this.cache[xT]) this.cache[xT] = {}
125
-
126
- //check if tile exists in the cache
127
- /** @type {object} */
128
- let tile = this.cache[xT][yT]
129
- if (tile) continue
130
-
131
- //mark tile as loading
132
- this.cache[xT][yT] = "loading";
133
- (async () => {
134
- //request tile
135
- /** @type {Array.<import("../core/Dataset.js").Cell>} */
136
- let cells
137
-
138
- try {
139
- /** @type {Array.<import("../core/Dataset.js").Cell>} */
140
- // @ts-ignore
141
- const data = await csv(this.url + xT + '/' + yT + '.csv')
142
-
143
- //if (monitor) monitorDuration('*** TiledGrid parse start')
144
-
145
- //preprocess/filter
146
- if (this.preprocess) {
147
- cells = []
148
- for (const c of data) {
149
- const b = this.preprocess(c)
150
- if (b == false) continue
151
- cells.push(c)
152
- }
153
- } else {
154
- cells = data
155
- }
156
-
157
- //if (monitor) monitorDuration('preprocess / filter')
158
- } catch (error) {
159
- //mark as failed
160
- this.cache[xT][yT] = 'failed'
161
- return
162
- }
163
-
164
- //store tile in cache
165
- if (!this.info) {
166
- console.error('Tile info inknown')
167
- return
168
- }
169
- const tile_ = getGridTile(cells, xT, yT, this.info)
170
- this.cache[xT][yT] = tile_
171
-
172
- //if (monitor) monitorDuration('storage')
173
-
174
- //if no redraw is specified, then leave
175
- this.map.redraw()
176
-
177
- //check if redraw is really needed, that is if:
178
-
179
- // 1. the dataset belongs to a layer which is visible at the current zoom level
180
- let redraw = false
181
- //go through the layers
182
- const z = this.map.getZoom()
183
- for (const lay of this.map.layers) {
184
- if (lay.visible && !lay.visible(z)) continue
185
- if (!lay.getDataset) continue
186
- if (lay.getDataset(z) != this) continue
187
- //found one layer. No need to seek more.
188
- redraw = true
189
- break
190
- }
191
- //if (monitor) monitorDuration('check redraw 1')
192
-
193
- if (!redraw) return
194
-
195
- // 2. the tile is within the view, that is its geo envelope intersects the viewer geo envelope.
196
- const env = this.map.updateExtentGeo()
197
- const envT = tile_.extGeo
198
- if (env.xMax <= envT.xMin) return
199
- if (env.xMin >= envT.xMax) return
200
- if (env.yMax <= envT.yMin) return
201
- if (env.yMin >= envT.yMax) return
202
-
203
- //if (monitor) monitorDuration('check redraw 2')
204
- //if (monitor) monitorDuration('*** TiledGrid parse end')
205
-
206
- //redraw
207
- this.map.redraw()
208
- })()
209
- }
210
- }
211
- return this
212
- }
213
-
214
- /**
215
- * Fill the view cache with all cells which are within a geographical envelope.
216
- * @abstract
217
- * @param {import("../core/GeoCanvas.js").Envelope} extGeo
218
- * @returns {void}
219
- */
220
- updateViewCache(extGeo) {
221
- //
222
- this.cellsViewCache = []
223
-
224
- //check if info has been loaded
225
- if (!this.info) return
226
-
227
- //tiles within the scope
228
- /** @type {import("../core/GeoCanvas.js").Envelope|undefined} */
229
- const tb = this.getTilingEnvelope(extGeo)
230
- if (!tb) return
231
-
232
- //grid bounds
233
- /** @type {import("../core/GeoCanvas.js").Envelope} */
234
- const gb = this.info.tilingBounds
235
-
236
- for (let xT = Math.max(tb.xMin, gb.xMin); xT <= Math.min(tb.xMax, gb.xMax); xT++) {
237
- if (!this.cache[xT]) continue
238
- for (let yT = Math.max(tb.yMin, gb.yMin); yT <= Math.min(tb.yMax, gb.yMax); yT++) {
239
- //get tile
240
- /** @type {object} */
241
- const tile = this.cache[xT][yT]
242
- if (!tile || typeof tile === 'string') continue
243
-
244
- //get cells
245
- //this.cellsViewCache = this.cellsViewCache.concat(tile.cells)
246
-
247
- for (const cell of tile.cells) {
248
- if (+cell.x + this.resolution < extGeo.xMin) continue
249
- if (+cell.x - this.resolution > extGeo.xMax) continue
250
- if (+cell.y + this.resolution < extGeo.yMin) continue
251
- if (+cell.y - this.resolution > extGeo.yMax) continue
252
- this.cellsViewCache.push(cell)
253
- }
254
- }
255
- }
256
- }
257
- }
258
-
259
- function getGridTile(cells, xT, yT, gridInfo) {
260
-
261
- const tile = {}
262
-
263
- /** @type {Array.<import("../core/Dataset").Cell>} */
264
- tile.cells = cells
265
- /** @type {number} */
266
- tile.x = xT
267
- /** @type {number} */
268
- tile.y = yT
269
-
270
- const r = gridInfo.resolutionGeo
271
- const s = gridInfo.tileSizeCell
272
-
273
- /** @type {import("../core/GeoCanvas").Envelope} */
274
- tile.extGeo = {
275
- xMin: gridInfo.originPoint.x + r * s * tile.x,
276
- xMax: gridInfo.originPoint.x + r * s * (tile.x + 1),
277
- yMin: gridInfo.originPoint.y + r * s * tile.y,
278
- yMax: gridInfo.originPoint.y + r * s * (tile.y + 1),
279
- }
280
-
281
- //convert cell coordinates into geographical coordinates
282
- for (let cell of tile.cells) {
283
- cell.x = tile.extGeo.xMin + cell.x * r
284
- cell.y = tile.extGeo.yMin + cell.y * r
285
- }
286
-
287
- return tile
288
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ /** @typedef {{ dims: object, crs: string, tileSizeCell: number, originPoint: {x:number,y:number}, resolutionGeo: number, tilingBounds:import("../core/GeoCanvas.js").Envelope }} GridInfo */
5
+
6
+ // internal
7
+ import { Dataset } from '../core/Dataset.js'
8
+ //import { monitor, monitorDuration } from '../utils/Utils.js'
9
+
10
+ // external
11
+ import { json, csv } from 'd3-fetch'
12
+
13
+ /**
14
+ * A tiled dataset, composed of CSV tiles.
15
+ *
16
+ * @module dataset
17
+ * @author Joseph Davies, Julien Gaffuri
18
+ */
19
+ export class TiledGrid extends Dataset {
20
+ /**
21
+ * @param {import("../core/Map.js").Map} map The map.
22
+ * @param {string} url The URL of the dataset.
23
+ * @param {{preprocess?:(function(import("../core/Dataset.js").Cell):boolean), onlyDrawWhenAllTilesReady:boolean}} opts
24
+ */
25
+ constructor(map, url, opts = {}) {
26
+ super(map, url, 0, opts)
27
+ this.onlyDrawWhenAllTilesReady = opts.onlyDrawWhenAllTilesReady || false
28
+ /**
29
+ * The grid info object, from the info.json file.
30
+ * @type {GridInfo | undefined}
31
+ * @private
32
+ * */
33
+ this.info = undefined
34
+
35
+ /**
36
+ * @type {string}
37
+ * @private */
38
+ this.infoLoadingStatus = 'notLoaded'
39
+
40
+ /**
41
+ * The cache of the loaded tiles. It is double indexed: by xT and then yT.
42
+ * Example: this.cache[xT][yT] returns the tile at [xT][yT] location.
43
+ *
44
+ * @type {object}
45
+ * */
46
+ this.cache = {}
47
+
48
+ //launch loading
49
+ this.loadInfo()
50
+ }
51
+
52
+ /**
53
+ * Load the info.json from the url.
54
+ * @returns this
55
+ */
56
+ loadInfo() {
57
+ if (!this.info && this.infoLoadingStatus === 'notLoaded') {
58
+ ;(async () => {
59
+ try {
60
+ const data = await json(this.url + 'info.json')
61
+ this.info = data
62
+ this.resolution = data.resolutionGeo
63
+ this.infoLoadingStatus = 'loaded'
64
+ this.map.redraw()
65
+ } catch (error) {
66
+ //mark as failed
67
+ this.infoLoadingStatus = 'failed'
68
+ }
69
+ })()
70
+ } else if (this.infoLoadingStatus === 'loaded' || this.infoLoadingStatus === 'failed')
71
+ this.map.redraw()
72
+ return this
73
+ }
74
+
75
+ /**
76
+ * Compute a tiling envelope from a geographical envelope.
77
+ * This is the function to use to know which tiles to download for a geographical view.
78
+ *
79
+ * @param {import("../core/GeoCanvas.js").Envelope} e
80
+ * @returns {import("../core/GeoCanvas.js").Envelope|undefined}
81
+ */
82
+ getTilingEnvelope(e) {
83
+ if (!this.info) {
84
+ this.loadInfo()
85
+ return
86
+ }
87
+
88
+ const po = this.info.originPoint,
89
+ r = this.info.resolutionGeo,
90
+ s = this.info.tileSizeCell
91
+
92
+ return {
93
+ xMin: Math.floor((e.xMin - po.x) / (r * s)),
94
+ xMax: Math.floor((e.xMax - po.x) / (r * s)),
95
+ yMin: Math.floor((e.yMin - po.y) / (r * s)),
96
+ yMax: Math.floor((e.yMax - po.y) / (r * s)),
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Request data within a geographic envelope.
102
+ *
103
+ * @param {import('../core/GeoCanvas.js').Envelope} extGeo
104
+ * @returns {this}
105
+ */
106
+ async getData(extGeo) {
107
+ if (!this.info) return this
108
+
109
+ // Get the tiling envelope and check bounds
110
+ const tb = this.getTilingEnvelope(extGeo)
111
+ if (!tb) return this
112
+
113
+ const { xMin: gbXMin, xMax: gbXMax, yMin: gbYMin, yMax: gbYMax } = this.info.tilingBounds
114
+
115
+ const xMin = Math.max(tb.xMin, gbXMin)
116
+ const xMax = Math.min(tb.xMax, gbXMax)
117
+ const yMin = Math.max(tb.yMin, gbYMin)
118
+ const yMax = Math.min(tb.yMax, gbYMax)
119
+
120
+ // Count the total number of tiles
121
+ const totalTiles = (xMax - xMin + 1) * (yMax - yMin + 1)
122
+
123
+ let processedTiles = 0 // Counter for loaded tiles
124
+ const tilePromises = []
125
+
126
+ // Iterate over tiles within bounds
127
+ for (let xT = xMin; xT <= xMax; xT++) {
128
+ for (let yT = yMin; yT <= yMax; yT++) {
129
+ if (!this.cache[xT]) this.cache[xT] = {}
130
+
131
+ // Skip already loaded or loading tiles
132
+ if (this.cache[xT][yT]) {
133
+ ++processedTiles
134
+ continue
135
+ }
136
+
137
+ // Mark tile as loading
138
+ this.cache[xT][yT] = 'loading'
139
+
140
+ // Prepare the tile request and store it in tilePromises
141
+ tilePromises.push(
142
+ this.loadTile(xT, yT)
143
+ .then((tile) => {
144
+ // Before we increment processedTiles, check if it's the last tile
145
+ const isLastTile = ++processedTiles === totalTiles
146
+
147
+ // Store the tile in the cache
148
+ this.cache[xT][yT] = tile
149
+
150
+ // Pass the isLastTile argument to checkAndRedraw
151
+ this.checkAndRedraw(tile, isLastTile)
152
+ })
153
+ .catch(() => {
154
+ // Mark as failed but allow processing to continue
155
+ this.cache[xT][yT] = 'failed'
156
+ ++processedTiles
157
+ })
158
+ )
159
+ }
160
+ }
161
+
162
+ // Handle all tile promises, including failures
163
+ await Promise.allSettled(tilePromises)
164
+ return this
165
+ }
166
+
167
+ async loadTile(xT, yT) {
168
+ try {
169
+ const data = await csv(`${this.url}${xT}/${yT}.csv`)
170
+
171
+ // Preprocess or filter the data if needed
172
+ const cells = this.preprocess ? data.filter((cell) => this.preprocess(cell) !== false) : data
173
+
174
+ if (!this.info) throw new Error('Tile info unknown')
175
+
176
+ return getGridTile(cells, xT, yT, this.info)
177
+ } catch (error) {
178
+ console.warn(`Failed to load tile ${xT}, ${yT}:`, error)
179
+ throw error
180
+ }
181
+ }
182
+
183
+ checkAndRedraw(tile, isLastTile) {
184
+ // Check if any visible layer depends on this dataset
185
+ //check if redraw is really needed, that is if:
186
+ // 1. the dataset belongs to a layer which is visible at the current zoom level
187
+ let needsRedraw = false
188
+ //go through the layers
189
+ const z = this.map.getZoom()
190
+ for (const lay of this.map.layers) {
191
+ if (lay.visible && !lay.visible(z)) continue
192
+ if (!lay.getDataset) continue
193
+ if (lay.getDataset(z) != this) continue
194
+ //found one layer. No need to seek more.
195
+ needsRedraw = true
196
+ break
197
+ }
198
+
199
+ if (!needsRedraw) return
200
+
201
+ // Check if tile intersects the current view
202
+ const env = this.map.updateExtentGeo()
203
+ const { xMin, xMax, yMin, yMax } = tile.extGeo
204
+ if (env.xMax <= xMin || env.xMin >= xMax || env.yMax <= yMin || env.yMin >= yMax) return
205
+
206
+ // Trigger redraw
207
+ if (this.onlyDrawWhenAllTilesReady) {
208
+ if (isLastTile) {
209
+ this.map.redraw()
210
+ }
211
+ } else {
212
+ this.map.redraw()
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Fill the view cache with all cells which are within a geographical envelope.
218
+ * @abstract
219
+ * @param {import("../core/GeoCanvas.js").Envelope} extGeo
220
+ * @returns {void}
221
+ */
222
+ updateViewCache(extGeo) {
223
+ //
224
+ this.cellsViewCache = []
225
+
226
+ //check if info has been loaded
227
+ if (!this.info) return
228
+
229
+ //tiles within the scope
230
+ /** @type {import("../core/GeoCanvas.js").Envelope|undefined} */
231
+ const tb = this.getTilingEnvelope(extGeo)
232
+ if (!tb) return
233
+
234
+ //grid bounds
235
+ /** @type {import("../core/GeoCanvas.js").Envelope} */
236
+ const gb = this.info.tilingBounds
237
+
238
+ for (let xT = Math.max(tb.xMin, gb.xMin); xT <= Math.min(tb.xMax, gb.xMax); xT++) {
239
+ if (!this.cache[xT]) continue
240
+ for (let yT = Math.max(tb.yMin, gb.yMin); yT <= Math.min(tb.yMax, gb.yMax); yT++) {
241
+ //get tile
242
+ /** @type {object} */
243
+ const tile = this.cache[xT][yT]
244
+ if (!tile || typeof tile === 'string') continue
245
+
246
+ //get cells
247
+ //this.cellsViewCache = this.cellsViewCache.concat(tile.cells)
248
+
249
+ for (const cell of tile.cells) {
250
+ if (+cell.x + this.resolution < extGeo.xMin) continue
251
+ if (+cell.x - this.resolution > extGeo.xMax) continue
252
+ if (+cell.y + this.resolution < extGeo.yMin) continue
253
+ if (+cell.y - this.resolution > extGeo.yMax) continue
254
+ this.cellsViewCache.push(cell)
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ function getGridTile(cells, xT, yT, gridInfo) {
262
+ const tile = {}
263
+
264
+ /** @type {Array.<import("../core/Dataset").Cell>} */
265
+ tile.cells = cells
266
+ /** @type {number} */
267
+ tile.x = xT
268
+ /** @type {number} */
269
+ tile.y = yT
270
+
271
+ const r = gridInfo.resolutionGeo
272
+ const s = gridInfo.tileSizeCell
273
+
274
+ /** @type {import("../core/GeoCanvas").Envelope} */
275
+ tile.extGeo = {
276
+ xMin: gridInfo.originPoint.x + r * s * tile.x,
277
+ xMax: gridInfo.originPoint.x + r * s * (tile.x + 1),
278
+ yMin: gridInfo.originPoint.y + r * s * tile.y,
279
+ yMax: gridInfo.originPoint.y + r * s * (tile.y + 1),
280
+ }
281
+
282
+ //convert cell coordinates into geographical coordinates
283
+ for (let cell of tile.cells) {
284
+ cell.x = tile.extGeo.xMin + cell.x * r
285
+ cell.y = tile.extGeo.yMin + cell.y * r
286
+ }
287
+
288
+ return tile
289
+ }
@@ -0,0 +1,18 @@
1
+ self.onmessage = function (e) {
2
+ const { type, cells, preprocess } = e.data
3
+ if (type === 'processCells') {
4
+ const result = processCells(cells, preprocess)
5
+ self.postMessage({ type: 'processedCells', result })
6
+ }
7
+ }
8
+
9
+ function processCells(cells, preprocess) {
10
+ // If no preprocess function is provided, return the cells as they are
11
+ if (!preprocess) {
12
+ return cells
13
+ }
14
+
15
+ // Apply preprocess function to each cell if available
16
+ const preprocessedCells = cells.map((cell) => preprocess(cell)).filter((cell) => cell !== false)
17
+ return preprocessedCells
18
+ }