gridviz 2.0.28 → 2.0.31

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 (57) hide show
  1. package/dist/gridviz.js +34368 -0
  2. package/dist/gridviz.min.js +1 -1
  3. package/package.json +10 -6
  4. package/src/App.js +654 -0
  5. package/src/BackgroundLayer.js +182 -0
  6. package/src/BackgroundLayerWMS.js +131 -0
  7. package/src/Dataset.js +76 -0
  8. package/src/DatasetComponent.js +103 -0
  9. package/src/GeoCanvas.js +419 -0
  10. package/src/LabelLayer.js +190 -0
  11. package/src/Layer.js +92 -0
  12. package/src/Legend.js +86 -0
  13. package/src/LineLayer.js +150 -0
  14. package/src/Style.js +114 -0
  15. package/src/Tooltip.js +150 -0
  16. package/src/dataset/COGeo.js +24 -0
  17. package/src/dataset/CSVGrid.js +104 -0
  18. package/src/dataset/GeoTIFF.js +127 -0
  19. package/src/dataset/GridTile.js +41 -0
  20. package/src/dataset/ParquetGrid.js +122 -0
  21. package/src/dataset/TiledGrid.js +296 -0
  22. package/src/index.js +177 -0
  23. package/src/legend/ColorCategoryLegend.js +124 -0
  24. package/src/legend/ColorDiscreteLegend.js +105 -0
  25. package/src/legend/ColorLegend.js +125 -0
  26. package/src/legend/SegmentOrientationLegend.js +87 -0
  27. package/src/legend/SegmentWidthLegend.js +97 -0
  28. package/src/legend/SizeLegend.js +141 -0
  29. package/src/style/CompositionStyle.js +300 -0
  30. package/src/style/ContourStyle.js +54 -0
  31. package/src/style/DotDensityStyle.js +156 -0
  32. package/src/style/JoyPlotStyle.js +135 -0
  33. package/src/style/KernelSmoothingOldStyle.js +221 -0
  34. package/src/style/KernelSmoothingStyle.js +115 -0
  35. package/src/style/KernelSmoothingWGLStyle.js +130 -0
  36. package/src/style/LegoStyle.js +112 -0
  37. package/src/style/MosaicStyle.js +107 -0
  38. package/src/style/NinjaStarStyle.js +124 -0
  39. package/src/style/PillarStyle.js +230 -0
  40. package/src/style/SegmentStyle.js +142 -0
  41. package/src/style/ShapeColorSizeStyle.js +126 -0
  42. package/src/style/SideCatStyle.js +172 -0
  43. package/src/style/SideStyle.js +218 -0
  44. package/src/style/SquareColorCatWGLStyle.js +120 -0
  45. package/src/style/SquareColorWGLStyle.js +146 -0
  46. package/src/style/StrokeStyle.js +131 -0
  47. package/src/style/TanakaStyle.js +160 -0
  48. package/src/style/TextStyle.js +132 -0
  49. package/src/utils/Utils.js +35 -0
  50. package/src/utils/WebGLRectangleColoring.js +106 -0
  51. package/src/utils/WebGLSquareColoring.js +118 -0
  52. package/src/utils/WebGLSquareColoringAdvanced.js +230 -0
  53. package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -0
  54. package/src/utils/WebGLSquareColoringKS.js +153 -0
  55. package/src/utils/stretching.js +131 -0
  56. package/src/utils/webGLUtils.js +71 -0
  57. package/src/utils/zoomUtils.js +142 -0
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "gridviz",
3
- "version": "2.0.28",
3
+ "version": "2.0.31",
4
4
  "description": "Visualization tool for gridded statistics",
5
- "type": "module",
6
5
  "keywords": [
7
6
  "statistics",
8
7
  "grid",
@@ -28,6 +27,7 @@
28
27
  "url": "https://github.com/eurostat/gridviz/issues",
29
28
  "email": "Joseph.DAVIES@ext.ec.europa.eu"
30
29
  },
30
+ "type": "module",
31
31
  "main": "dist/gridviz.min.js",
32
32
  "module": "dist/gridviz.min.js",
33
33
  "exports": {
@@ -35,15 +35,16 @@
35
35
  "default": "./src/index.js"
36
36
  },
37
37
  "scripts": {
38
- "start": "export NODE_OPTIONS=--openssl-legacy-provider && webpack --config webpack.config.dev.js",
39
- "start-dev": "webpack --config webpack.config.dev.js",
38
+ "start": "export NODE_OPTIONS=--openssl-legacy-provider && webpack --config webpack.config.dev.cjs",
39
+ "start-dev": "webpack --config webpack.config.dev.cjs",
40
40
  "server": "webpack serve -c webpack.config.dev.js --static ./",
41
41
  "doc": "./node_modules/.bin/jsdoc src/ src/dataset src/legend src/style -d docs/jsdoc",
42
- "build": "webpack --config webpack.config.prod.js",
42
+ "build": "webpack --config webpack.config.prod.cjs",
43
43
  "test": "jest"
44
44
  },
45
45
  "files": [
46
- "dist/gridviz.min.js"
46
+ "dist/**/*.js",
47
+ "src/**/*.js"
47
48
  ],
48
49
  "dependencies": {
49
50
  "apache-arrow": "^10.0.1",
@@ -70,5 +71,8 @@
70
71
  "webpack-cli": "^5.0.0",
71
72
  "webpack-dev-server": "^4.11.1",
72
73
  "webpack-livereload-plugin": "^3.0.2"
74
+ },
75
+ "engines": {
76
+ "node": ">=14"
73
77
  }
74
78
  }
package/src/App.js ADDED
@@ -0,0 +1,654 @@
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ // internal imports
5
+ import { GeoCanvas } from './GeoCanvas.js'
6
+ import { Layer } from './Layer.js'
7
+ import { Dataset } from './Dataset.js'
8
+ import { Tooltip } from './Tooltip.js'
9
+ import { CSVGrid } from './dataset/CSVGrid.js'
10
+ //import { ParquetGrid } from './dataset/ParquetGrid';
11
+ import { TiledGrid } from './dataset/TiledGrid.js'
12
+ import { BackgroundLayer } from './BackgroundLayer.js'
13
+ import { BackgroundLayerWMS } from './BackgroundLayerWMS.js'
14
+ import { LabelLayer } from './LabelLayer.js'
15
+ import { LineLayer } from './LineLayer.js'
16
+ import { monitor, monitorDuration } from './utils/Utils.js'
17
+
18
+ // external imports
19
+ import { select } from 'd3-selection'
20
+
21
+ /**
22
+ * A gridviz application.
23
+ *
24
+ * @author Joseph Davies, Julien Gaffuri
25
+ */
26
+ export class App {
27
+ /**
28
+ * @param {HTMLDivElement} container
29
+ * @param {object} opts
30
+ */
31
+ constructor(container, opts) {
32
+ opts = opts || {}
33
+
34
+ /**
35
+ * The layers.
36
+ * @type {Array.<Layer>}
37
+ * */
38
+ this.layers = []
39
+
40
+ //get container element
41
+ container = container || document.getElementById('gridviz')
42
+ if (!container) {
43
+ console.error('Cannot find gridviz container element.')
44
+ return
45
+ }
46
+
47
+ //set dimensions
48
+ /** @type {number} */
49
+ this.w = opts.w || container.offsetWidth
50
+ /** @type {number} */
51
+ this.h = opts.h || container.offsetHeight
52
+
53
+ //create canvas element if user doesnt specify one
54
+ /** @type {HTMLCanvasElement} */
55
+ let canvas = opts.canvas || null
56
+ if (!canvas) {
57
+ canvas = document.createElement('canvas')
58
+ canvas.setAttribute('width', '' + this.w)
59
+ canvas.setAttribute('height', '' + this.h)
60
+ container.appendChild(canvas)
61
+ }
62
+
63
+ /** Make geo canvas
64
+ * @type {GeoCanvas}
65
+ * @private */
66
+ this.cg = new GeoCanvas(canvas, undefined, 1, opts)
67
+ this.cg.redraw = (strong = true) => {
68
+ if (monitor) monitorDuration('Start redraw')
69
+ //console.log("?x=" + this.cg.getCenter().x + "&y=" + this.cg.getCenter().y + "&z=" + this.cg.getZf())
70
+
71
+ //remove legend elements
72
+ if (this.legend && strong) this.legend.selectAll('*').remove()
73
+
74
+ //clear
75
+ this.cg.initCanvasTransform()
76
+ this.cg.clear(this.cg.backgroundColor)
77
+
78
+ const zf = this.getZoomFactor()
79
+ this.updateExtentGeo()
80
+
81
+ //go through the background layers
82
+ for (const layer of this.bgLayers) {
83
+ //check if layer is visible
84
+ if (!layer.visible) continue
85
+ if (zf > layer.maxZoom) continue
86
+ if (zf < layer.minZoom) continue
87
+
88
+ //draw layer
89
+ layer.draw(this.cg)
90
+ }
91
+
92
+ //go through the layers
93
+ for (const layer of this.layers) {
94
+ //check if layer is visible
95
+ if (!layer.visible) continue
96
+ if (zf > layer.maxZoom) continue
97
+ if (zf < layer.minZoom) continue
98
+
99
+ //get layer dataset component
100
+ /** @type {import('./DatasetComponent').DatasetComponent|undefined} */
101
+ const dsc = layer.getDatasetComponent(zf)
102
+ if (!dsc) continue
103
+
104
+ //launch data download, if necessary
105
+ if (strong)
106
+ dsc.getData(this.cg.extGeo, () => {
107
+ this.cg.redraw()
108
+ })
109
+
110
+ //update dataset view cache
111
+ if (strong) dsc.updateViewCache(this.cg.extGeo)
112
+
113
+ //draw cells, style by style
114
+ if (strong)
115
+ for (const s of layer.styles) {
116
+ if (zf > s.maxZoom) continue
117
+ if (zf < s.minZoom) continue
118
+ s.draw(dsc.getViewCache(), dsc.getResolution(), this.cg)
119
+ }
120
+
121
+ //add legend element
122
+ if (this.legend && strong)
123
+ for (const s of layer.styles) {
124
+ if (zf > s.maxZoom) continue
125
+ if (zf < s.minZoom) continue
126
+ for (const lg of s.legends) {
127
+ //console.log(s, lg)
128
+ //this.legend.append(lg.div)
129
+ //s1.node().appendChild(s2.node())
130
+ this.legend.node().append(lg.div.node())
131
+ }
132
+
133
+ //case for styles of styles, like kernel smoothing
134
+ //TODO do better
135
+ if (s['styles']) {
136
+ for (const s2 of s.styles) {
137
+ if (zf > s2.maxZoom) continue
138
+ if (zf < s2.minZoom) continue
139
+ for (const lg of s2.legends) {
140
+ //console.log(s, lg)
141
+ //this.legend.append(lg.div)
142
+ //s1.node().appendChild(s2.node())
143
+ this.legend.node().append(lg.div.node())
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ //draw boundary layer
151
+ //if (strong)
152
+ if (this.showBoundaries && this.boundaryLayer) this.boundaryLayer.draw(this.cg)
153
+
154
+ //draw label layer
155
+ //if (strong)
156
+ if (this.showLabels && this.labelLayer) this.labelLayer.draw(this.cg)
157
+
158
+ //
159
+ this.canvasSave = null
160
+
161
+ if (monitor) monitorDuration('End redraw')
162
+
163
+ return this
164
+ }
165
+
166
+ /** @type {Array.<BackgroundLayer|BackgroundLayerWMS>} */
167
+ this.bgLayers = []
168
+ /** @type {boolean} */
169
+ this.showBgLayers = true
170
+
171
+ /** @type {LabelLayer | undefined} */
172
+ this.labelLayer = undefined
173
+ /** @type {boolean} */
174
+ this.showLabels = true
175
+
176
+ /** @type {LineLayer | undefined} */
177
+ this.boundaryLayer = undefined
178
+ /** @type {boolean} */
179
+ this.showBoundaries = true
180
+
181
+ //legend div
182
+
183
+ this.legendDivId = opts.legendDivId || 'gvizLegend'
184
+ this.legend = select('#' + this.legendDivId)
185
+ if (this.legend.empty()) {
186
+ this.legend = select('body')
187
+ .append('div')
188
+ .attr('id', this.legendDivId)
189
+ .style('position', 'absolute')
190
+ .style('width', 'auto')
191
+ .style('height', 'auto')
192
+ .style('background', '#FFFFFFCC')
193
+ //.style("padding", this.padding)
194
+ .style('border', '0px')
195
+ .style('border-radius', '5px')
196
+ .style('box-shadow', '3px 3px 3px grey, -3px -3px 3px #ddd')
197
+ .style('font-family', 'Helvetica, Arial, sans-serif')
198
+ .style('top', '20px')
199
+ .style('right', '20px')
200
+ //hide
201
+ //.style("visibility", "hidden")
202
+ }
203
+
204
+ //tooltip
205
+
206
+ /**
207
+ * @private
208
+ * @type {Tooltip} */
209
+ this.tooltip = new Tooltip(opts.tooltip)
210
+
211
+ /** @param {MouseEvent} e */
212
+ const focusCell = (e) => {
213
+ //compute mouse geo position
214
+ const mousePositionGeo = {
215
+ x: this.cg.pixToGeoX(e.offsetX + this.tooltip.xMouseOffset),
216
+ y: this.cg.pixToGeoY(e.offsetY + this.tooltip.yMouseOffset),
217
+ }
218
+ /** @type {{cell:import('./Dataset').Cell,html:string,resolution:number} | undefined} */
219
+ const focus = this.getCellFocusInfo(mousePositionGeo)
220
+
221
+ // transparent background (e.g. leaflet) 'red painting' fix
222
+ if (opts.transparentBackground) {
223
+ if (focus) {
224
+ this.tooltip.setPosition(e)
225
+ this.tooltip.show()
226
+ this.tooltip.html(focus.html)
227
+ } else {
228
+ this.tooltip.hide()
229
+ }
230
+ this.canvasSave = document.createElement('canvas')
231
+ this.canvasSave.setAttribute('width', '' + this.w)
232
+ this.canvasSave.setAttribute('height', '' + this.h)
233
+ this.canvasSave.getContext('2d').drawImage(this.cg.canvas, 0, 0)
234
+ this.cg.initCanvasTransform()
235
+ return
236
+ }
237
+
238
+ if (focus) {
239
+ this.tooltip.setPosition(e)
240
+ this.tooltip.show()
241
+ this.tooltip.html(focus.html)
242
+
243
+ //show cell position as a rectangle
244
+ if (!this.canvasSave) {
245
+ this.canvasSave = document.createElement('canvas')
246
+ this.canvasSave.setAttribute('width', '' + this.w)
247
+ this.canvasSave.setAttribute('height', '' + this.h)
248
+ this.canvasSave.getContext('2d').drawImage(this.cg.canvas, 0, 0)
249
+ } else {
250
+ this.cg.ctx.drawImage(this.canvasSave, 0, 0)
251
+ }
252
+
253
+ //draw image saved + draw rectangle
254
+ const rectWPix = this.selectionRectangleWidthPix
255
+ ? this.selectionRectangleWidthPix(focus.resolution, this.getZoomFactor())
256
+ : 4
257
+ this.cg.initCanvasTransform()
258
+ this.cg.ctx.strokeStyle = this.selectionRectangleColor
259
+ this.cg.ctx.lineWidth = rectWPix
260
+ this.cg.ctx.beginPath()
261
+
262
+ this.cg.ctx.rect(
263
+ this.cg.geoToPixX(focus.cell.x) - rectWPix / 2,
264
+ this.cg.geoToPixY(focus.cell.y) + rectWPix / 2,
265
+ focus.resolution / this.getZoomFactor() + rectWPix,
266
+ -focus.resolution / this.getZoomFactor() - rectWPix
267
+ )
268
+ this.cg.ctx.stroke()
269
+ } else {
270
+ this.tooltip.hide()
271
+ if (this.canvasSave) this.cg.ctx.drawImage(this.canvasSave, 0, 0)
272
+ }
273
+ }
274
+ container.addEventListener('mouseover', (e) => {
275
+ focusCell(e)
276
+ })
277
+ container.addEventListener('mousemove', (e) => {
278
+ focusCell(e)
279
+ })
280
+ container.addEventListener('mouseout', () => {
281
+ this.tooltip.hide()
282
+ })
283
+ this.cg.onZoomStartFun = () => {
284
+ this.tooltip.hide()
285
+ }
286
+
287
+ //for mouse over
288
+ /**
289
+ * @private
290
+ * @type {HTMLCanvasElement|null} */
291
+ this.canvasSave = null
292
+
293
+ this.selectionRectangleColor = opts.selectionRectangleColor || 'red'
294
+ this.selectionRectangleWidthPix = opts.selectionRectangleWidthPix || (() => 4) //(r,zf) => {}
295
+
296
+ //
297
+ //canvas.addEventListener("keydown", e => { console.log(arguments) });
298
+ }
299
+
300
+ /**
301
+ * @param {number} marginPx
302
+ * @returns {import('./Dataset').Envelope}
303
+ * @public
304
+ */
305
+ updateExtentGeo(marginPx = 20) {
306
+ return this.cg.updateExtentGeo(marginPx)
307
+ }
308
+
309
+ /**
310
+ * Return the cell HTML info at a given geo position.
311
+ * This is usefull for user interactions, to show this info where the user clicks for example.
312
+ *
313
+ * @param {{x:number,y:number}} posGeo
314
+ * @returns {{cell:import('./Dataset').Cell,html:string,resolution:number} | undefined}
315
+ * @protected
316
+ */
317
+ getCellFocusInfo(posGeo) {
318
+ //go through the layers, starting from top
319
+ const zf = this.getZoomFactor()
320
+ for (let i = this.layers.length - 1; i >= 0; i--) {
321
+ /** @type {Layer} */
322
+ const layer = this.layers[i]
323
+ if (!layer.visible) continue
324
+ if (!layer.cellInfoHTML) continue
325
+ if (layer.cellInfoHTML === 'none') continue
326
+ const dsc = layer.getDatasetComponent(zf)
327
+ if (!dsc) continue
328
+
329
+ //get cell at mouse position
330
+ /** @type {import('./Dataset').Cell|undefined} */
331
+ const cell = dsc.getCellFromPosition(posGeo, dsc.getViewCache())
332
+ if (!cell) return undefined
333
+ const html = layer.cellInfoHTML(cell, dsc.getResolution())
334
+ if (!html) return undefined
335
+ return { cell: cell, html: html, resolution: dsc.getResolution() }
336
+ }
337
+ }
338
+
339
+ //getters and setters
340
+
341
+ /** @returns {{x:number,y:number}} */
342
+ getGeoCenter() {
343
+ return this.cg.getCenter()
344
+ }
345
+ /** @param {{x:number,y:number}} val @returns {this} */
346
+ setGeoCenter(val) {
347
+ this.cg.setCenter(val)
348
+ return this
349
+ }
350
+
351
+ /** @returns {number} */
352
+ getZoomFactor() {
353
+ return this.cg.getZf()
354
+ }
355
+ /** @param {number} val @returns {this} */
356
+ setZoomFactor(val) {
357
+ this.cg.setZf(val)
358
+ return this
359
+ }
360
+
361
+ /** @returns {Array.<number>} */
362
+ getZoomFactorExtent() {
363
+ return this.cg.getZfExtent()
364
+ }
365
+ /** @param {Array.<number>} val @returns {this} */
366
+ setZoomFactorExtent(val) {
367
+ this.cg.setZfExtent(val)
368
+ return this
369
+ }
370
+
371
+ /** @returns {string} */
372
+ getBackgroundColor() {
373
+ return this.cg.backgroundColor
374
+ }
375
+ /** @param {string} val @returns {this} */
376
+ setBackgroundColor(val) {
377
+ this.cg.backgroundColor = val
378
+ return this
379
+ }
380
+
381
+ /** @returns {LineLayer | undefined} */
382
+ getBoundaryLayer() {
383
+ return this.boundaryLayer
384
+ }
385
+ /** @param {object} opts @returns {this} */
386
+ setBoundaryLayer(opts) {
387
+ this.boundaryLayer = new LineLayer(opts)
388
+ return this
389
+ }
390
+
391
+ /** @returns {LabelLayer | undefined} */
392
+ getLabelLayer() {
393
+ return this.labelLayer
394
+ }
395
+ /** @param {object} opts @returns {this} */
396
+ setLabelLayer(opts) {
397
+ this.labelLayer = new LabelLayer(opts)
398
+ return this
399
+ }
400
+
401
+ /** @returns {this} */
402
+ redraw() {
403
+ this.cg.redraw()
404
+ return this
405
+ }
406
+
407
+ /**
408
+ * Add a layer to the app.
409
+ *
410
+ * @param {Dataset} dataset The dataset of the layer
411
+ * @param {Array.<import('./Style').Style>} styles The styles of the layer
412
+ * @param {{visible?:boolean,minZoom?:number,maxZoom?:number,pixNb?:number,cellInfoHTML?:function(import('./Dataset').Cell):string}} opts The layer options.
413
+ * @returns {this}
414
+ */
415
+ addLayerFromDataset(dataset, styles, opts) {
416
+ const lay = new Layer(dataset, styles, opts)
417
+ this.layers.push(lay)
418
+ return this
419
+ }
420
+
421
+ //dataset creation
422
+
423
+ /**
424
+ * Make a CSV grid dataset.
425
+ *
426
+ * @param {string} url The URL of the dataset.
427
+ * @param {number} resolution The dataset resolution in geographical unit.
428
+ * @param {object=} opts The parameters of the dataset.
429
+ * @returns {Dataset}
430
+ */
431
+ makeCSVGridDataset(url, resolution, opts) {
432
+ return new Dataset(
433
+ [
434
+ new CSVGrid(url, resolution, opts).getData(undefined, () => {
435
+ this.cg.redraw()
436
+ }),
437
+ ],
438
+ [],
439
+ opts
440
+ )
441
+ }
442
+
443
+ /**
444
+ * Make a parquet grid dataset.
445
+ *
446
+ * @param {string} url The URL of the dataset.
447
+ * @param {number} resolution The dataset resolution in geographical unit.
448
+ * @param {object=} opts The parameters of the dataset.
449
+ * @returns {Dataset}
450
+ */
451
+ /*makeParquetGridDataset(url, resolution, opts) {
452
+ return new Dataset([new ParquetGrid(url, resolution, opts).getData(undefined, () => { this.cg.redraw(); })], [], opts)
453
+ }*/
454
+
455
+ /**
456
+ * Make a tiled grid dataset.
457
+ *
458
+ * @param {string} url
459
+ * @param {{preprocess?:function(import('./Dataset').Cell):boolean}} opts
460
+ * @returns {Dataset}
461
+ */
462
+ makeTiledGridDataset(url, opts) {
463
+ return new Dataset(
464
+ [
465
+ new TiledGrid(url, this, opts).loadInfo(() => {
466
+ this.cg.redraw()
467
+ }),
468
+ ],
469
+ [],
470
+ opts
471
+ )
472
+ }
473
+
474
+ //multi scale dataset creation
475
+
476
+ /**
477
+ * Make a multi scale parquet grid dataset.
478
+ *
479
+ * @param {Array.<number>} resolutions
480
+ * @param {function(number):string} resToURL
481
+ * @param {{preprocess?:function(import('./Dataset').Cell):boolean}} opts
482
+ * @returns {Dataset}
483
+ */
484
+ /*makeMultiScaleParquetGridDataset(resolutions, resToURL, opts) {
485
+ return Dataset.make(
486
+ resolutions,
487
+ (res) => new ParquetGrid(resToURL(res), res, opts).getData(undefined, () => { this.cg.redraw(); }),
488
+ opts
489
+ )
490
+ }*/
491
+
492
+ /**
493
+ * Make a multi scale CSV grid dataset.
494
+ *
495
+ * @param {Array.<number>} resolutions
496
+ * @param {function(number):string} resToURL
497
+ * @param {{preprocess?:function(import('./Dataset').Cell):boolean}} opts
498
+ * @returns {Dataset}
499
+ */
500
+ makeMultiScaleCSVGridDataset(resolutions, resToURL, opts) {
501
+ return Dataset.make(
502
+ resolutions,
503
+ (res) =>
504
+ new CSVGrid(resToURL(res), res, opts).getData(undefined, () => {
505
+ this.cg.redraw()
506
+ }),
507
+ opts
508
+ )
509
+ }
510
+
511
+ //tiled multiscale
512
+
513
+ /**
514
+ * Make a multi scale tiled grid dataset.
515
+ *
516
+ * @param {Array.<number>} resolutions
517
+ * @param {function(number):string} resToURL
518
+ * @param {{preprocess?:function(import('./Dataset').Cell):boolean}} opts
519
+ * @returns {Dataset}
520
+ */
521
+ makeMultiScaleTiledGridDataset(resolutions, resToURL, opts) {
522
+ return Dataset.make(
523
+ resolutions,
524
+ (res) =>
525
+ new TiledGrid(resToURL(res), this, opts).loadInfo(() => {
526
+ this.cg.redraw()
527
+ }),
528
+ opts
529
+ )
530
+ }
531
+
532
+ // direct layer creation
533
+
534
+ /**
535
+ * Add a layer from a CSV grid dataset.
536
+ *
537
+ * @param {string} url The URL of the dataset.
538
+ * @param {number} resolution The dataset resolution in geographical unit.
539
+ * @param {Array.<import('./Style').Style>} styles The styles, ordered in drawing order.
540
+ * @param {object=} opts The parameters of the dataset and layer.
541
+ * @returns {this}
542
+ */
543
+ addCSVGridLayer(url, resolution, styles, opts) {
544
+ const ds = this.makeCSVGridDataset(url, resolution, opts)
545
+ return this.addLayerFromDataset(ds, styles, opts)
546
+ }
547
+
548
+ /**
549
+ * Add a layer from a parquet grid dataset.
550
+ *
551
+ * @param {string} url The URL of the dataset.
552
+ * @param {number} resolution The dataset resolution in geographical unit.
553
+ * @param {Array.<import('./Style').Style>} styles The styles, ordered in drawing order.
554
+ * @param {object=} opts The parameters of the dataset and layer.
555
+ * @returns {this}
556
+ */
557
+ /*addParquetGridLayer(url, resolution, styles, opts) {
558
+ const ds = this.makeParquetGridDataset(url, resolution, opts)
559
+ return this.addLayerFromDataset(ds, styles, opts);
560
+ }*/
561
+
562
+ /**
563
+ *
564
+ * @param {string} url
565
+ * @param {Array.<import('./Style').Style>} styles
566
+ * @param {{visible?:boolean,minZoom?:number,maxZoom?:number,pixNb?:number,cellInfoHTML?:function(import('./Dataset').Cell):string, preprocess?:function(import('./Dataset').Cell):boolean}} opts
567
+ * @returns {this}
568
+ */
569
+ addTiledGridLayer(url, styles, opts) {
570
+ const ds = this.makeTiledGridDataset(url, opts)
571
+ return this.addLayerFromDataset(ds, styles, opts)
572
+ }
573
+
574
+ /**
575
+ * Add a layer from a CSV grid dataset.
576
+ *
577
+ * @param {Array.<number>} resolutions
578
+ * @param {function(number):string} resToURL
579
+ * @param {Array.<import('./Style').Style>} styles The styles, ordered in drawing order.
580
+ * @param {object=} opts The parameters of the dataset and layer.
581
+ * @returns {this}
582
+ */
583
+ addMultiScaleCSVGridLayer(resolutions, resToURL, styles, opts) {
584
+ const ds = this.makeMultiScaleCSVGridDataset(resolutions, resToURL, opts)
585
+ return this.addLayerFromDataset(ds, styles, opts)
586
+ }
587
+
588
+ /**
589
+ * Add a layer from a parquet grid dataset.
590
+ *
591
+ * @param {Array.<number>} resolutions
592
+ * @param {function(number):string} resToURL
593
+ * @param {Array.<import('./Style').Style>} styles The styles, ordered in drawing order.
594
+ * @param {object=} opts The parameters of the dataset and layer.
595
+ * @returns {this}
596
+ */
597
+ /*addMultiScaleParquetGridLayer(resolutions, resToURL, styles, opts) {
598
+ const ds = this.makeMultiScaleParquetGridDataset(resolutions, resToURL, opts)
599
+ return this.addLayerFromDataset(ds, styles, opts);
600
+ }*/
601
+
602
+ /**
603
+ * @param {Array.<number>} resolutions
604
+ * @param {function(number):string} resToURL
605
+ * @param {Array.<import('./Style').Style>} styles
606
+ * @param {{visible?:boolean,minZoom?:number,maxZoom?:number,pixNb?:number,cellInfoHTML?:function(import('./Dataset').Cell):string, preprocess?:function(import('./Dataset').Cell):boolean}} opts
607
+ * @returns {this}
608
+ */
609
+ addMultiScaleTiledGridLayer(resolutions, resToURL, styles, opts) {
610
+ const ds = this.makeMultiScaleTiledGridDataset(resolutions, resToURL, opts)
611
+ return this.addLayerFromDataset(ds, styles, opts)
612
+ }
613
+
614
+ /**
615
+ * Add a background layer to the app.
616
+ *
617
+ * @param {object} opts
618
+ * @returns {this}
619
+ */
620
+ addBackgroundLayer(opts) {
621
+ this.bgLayers.push(new BackgroundLayer(opts))
622
+ this.redraw()
623
+ return this
624
+ }
625
+
626
+ /**
627
+ * Add a WMS background layer to the app.
628
+ *
629
+ * @param {object} opts
630
+ * @returns {this}
631
+ */
632
+ addBackgroundLayerWMS(opts) {
633
+ this.bgLayers.push(new BackgroundLayerWMS(opts))
634
+ this.redraw()
635
+ return this
636
+ }
637
+
638
+ /**
639
+ *
640
+ * @param {string} id
641
+ * @param {object} opts
642
+ * @returns {this}
643
+ */
644
+ addZoomSlider(id, opts) {
645
+ this.cg.addZoomSlider(id, opts)
646
+ return this
647
+ }
648
+
649
+ /** @returns {this} */
650
+ setViewFromURL() {
651
+ this.cg.setViewFromURL()
652
+ return this
653
+ }
654
+ }