gridviz 3.0.34 → 3.0.36
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/README.md +129 -129
- package/dist/gridviz.js +17487 -17471
- package/dist/gridviz.min.js +2 -2
- package/package.json +73 -73
- package/src/button/Button.js +67 -67
- package/src/button/FullscreenButton.js +121 -121
- package/src/button/ZoomButtons.js +103 -103
- package/src/core/Dataset.js +154 -154
- package/src/core/Drawable.js +78 -78
- package/src/core/GeoCanvas.js +443 -443
- package/src/core/Layer.js +23 -23
- package/src/core/Legend.js +108 -108
- package/src/core/Map.js +568 -552
- package/src/core/MultiResolutionDataset.js +93 -93
- package/src/core/Style.js +98 -98
- package/src/core/Tooltip.js +182 -182
- package/src/dataset/COGeo.js +25 -25
- package/src/dataset/CSVGrid.js +112 -112
- package/src/dataset/GeoTIFF.js +122 -122
- package/src/dataset/JSGrid.js +55 -55
- package/src/dataset/TiledGrid.js +302 -302
- package/src/dataset/worker.js +18 -18
- package/src/index.js +79 -79
- package/src/layer/BackgroundLayer.js +173 -173
- package/src/layer/BackgroundLayerImage.js +81 -81
- package/src/layer/BackgroundLayerWMS.js +104 -104
- package/src/layer/GeoJSONLayer.js +276 -276
- package/src/layer/GridLayer.js +166 -166
- package/src/layer/LabelLayer.js +194 -194
- package/src/legend/ColorCategoryLegend.js +109 -109
- package/src/legend/ColorDiscreteLegend.js +100 -100
- package/src/legend/ColorLegend.js +115 -115
- package/src/legend/OrientationLegend.js +89 -89
- package/src/legend/SizeLegend.js +224 -224
- package/src/legend/TernaryLegend.js +302 -302
- package/src/style/CompositionStyle.js +291 -291
- package/src/style/DotDensityStyle.js +133 -133
- package/src/style/ImageStyle.js +100 -100
- package/src/style/Interpolator.js +258 -258
- package/src/style/IsoFenceStyle.js +217 -217
- package/src/style/JoyPlotStyle.js +180 -180
- package/src/style/LegoStyle.js +132 -132
- package/src/style/MosaicStyle.js +99 -99
- package/src/style/NinjaStarStyle.js +102 -102
- package/src/style/PillarStyle.js +207 -207
- package/src/style/SegmentStyle.js +105 -105
- package/src/style/ShadingRayStyle.js +280 -280
- package/src/style/ShadingStyle.js +72 -72
- package/src/style/ShapeColorSizeStyle.js +147 -147
- package/src/style/SideCategoryStyle.js +112 -112
- package/src/style/SideStyle.js +217 -217
- package/src/style/SideTanakaStyle.js +154 -154
- package/src/style/SquareColorCategoryWebGLStyle.js +307 -294
- package/src/style/SquareColorWebGLStyle.js +126 -126
- package/src/style/StrokeStyle.js +135 -135
- package/src/style/TextStyle.js +110 -110
- package/src/style/TimeSeriesStyle.js +222 -222
- package/src/utils/WebGLRectangleColoring.js +106 -106
- package/src/utils/WebGLSquareColoring.js +118 -118
- package/src/utils/WebGLSquareColoringAdvanced.js +227 -227
- package/src/utils/WebGLSquareColoringKS.js +153 -153
- package/src/utils/scale.js +255 -255
- package/src/utils/stretching.js +120 -120
- package/src/utils/ternary.js +208 -208
- package/src/utils/utils.js +125 -125
- package/src/utils/webGLUtils.js +73 -73
- package/src/utils/zoomUtils.js +142 -142
package/src/core/Map.js
CHANGED
|
@@ -1,552 +1,568 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
// internal imports
|
|
5
|
-
import { GeoCanvas } from './GeoCanvas.js'
|
|
6
|
-
import { Tooltip } from './Tooltip.js'
|
|
7
|
-
import { ZoomButtons } from '../button/ZoomButtons.js'
|
|
8
|
-
import { FullscreenButton } from '../button/FullscreenButton.js'
|
|
9
|
-
|
|
10
|
-
// external imports
|
|
11
|
-
import { select } from 'd3-selection'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* A gridviz application.
|
|
15
|
-
*
|
|
16
|
-
* @module core
|
|
17
|
-
* @author Joseph Davies, Julien Gaffuri
|
|
18
|
-
*/
|
|
19
|
-
export class Map {
|
|
20
|
-
/**
|
|
21
|
-
* @param {HTMLDivElement} container
|
|
22
|
-
* @param {object} opts
|
|
23
|
-
*/
|
|
24
|
-
constructor(container, opts) {
|
|
25
|
-
opts = opts || {}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* The layers.
|
|
29
|
-
* @type {Array.<import("./Layer.js").Layer>}
|
|
30
|
-
* */
|
|
31
|
-
this.layers = opts.layers || []
|
|
32
|
-
|
|
33
|
-
//get container element
|
|
34
|
-
this.container = container || document.getElementById('gridviz')
|
|
35
|
-
if (!this.container) {
|
|
36
|
-
console.error('Cannot find gridviz container element.')
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
//https://css-tricks.com/absolute-positioning-inside-relative-positioning/
|
|
41
|
-
this.container.style.position = 'relative' // container element must have relative positioning
|
|
42
|
-
|
|
43
|
-
//set dimensions
|
|
44
|
-
/** @type {number} */
|
|
45
|
-
this.w = opts.w || this.container.offsetWidth
|
|
46
|
-
/** @type {number} */
|
|
47
|
-
this.h = opts.h || this.container.offsetHeight
|
|
48
|
-
|
|
49
|
-
// Create the main canvas (for rendering to screen)
|
|
50
|
-
/** @type {HTMLCanvasElement} */
|
|
51
|
-
this._canvas = opts.canvas || this.initialiseCanvas()
|
|
52
|
-
|
|
53
|
-
/** Initialize GeoCanvas
|
|
54
|
-
* @type {GeoCanvas}
|
|
55
|
-
* @private */
|
|
56
|
-
this.geoCanvas = new GeoCanvas(this._canvas, opts.x, opts.y, opts.z, opts)
|
|
57
|
-
|
|
58
|
-
this.geoCanvas.redraw = () => {
|
|
59
|
-
this.redraw()
|
|
60
|
-
}
|
|
61
|
-
this.geoCanvas.cancelCurrentRequests = () => {
|
|
62
|
-
// when the zoom level changes, avoid drawing outdated tiles, and ensure that requests are properly aborted when necessary
|
|
63
|
-
for (const layer of this.layers) {
|
|
64
|
-
//multires
|
|
65
|
-
if (layer.dataset?.datasets) {
|
|
66
|
-
for (const dataset of layer.dataset?.datasets) {
|
|
67
|
-
if (dataset?.cancelCurrentRequests) dataset.cancelCurrentRequests()
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
//single res
|
|
71
|
-
if (layer.dataset?.cancelCurrentRequests) layer.dataset?.cancelCurrentRequests()
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// legend div
|
|
76
|
-
this.legend = opts.legendContainer
|
|
77
|
-
? select(opts.legendContainer) // Wrap the provided HTML node in a D3 selection
|
|
78
|
-
: null
|
|
79
|
-
this._legendProvidedByUser = !!opts.legendContainer
|
|
80
|
-
if (!this.legend) this.initialiseLegend()
|
|
81
|
-
|
|
82
|
-
//tooltip
|
|
83
|
-
|
|
84
|
-
// set App container as default parent element for tooltip
|
|
85
|
-
if (!opts.tooltip) opts.tooltip = {}
|
|
86
|
-
if (!opts.tooltip.parentElement) opts.tooltip.parentElement = this.container
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @private
|
|
90
|
-
* @type {Tooltip} */
|
|
91
|
-
this.tooltip = new Tooltip(opts.tooltip)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.geoCanvas.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
this.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
.
|
|
149
|
-
|
|
150
|
-
.style('
|
|
151
|
-
.style('
|
|
152
|
-
.style('
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//draw
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
ctx.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
ctx.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
ctx.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (!
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
this.geoCanvas.view.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
/** @
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
/** @returns {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
this
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
// internal imports
|
|
5
|
+
import { GeoCanvas } from './GeoCanvas.js'
|
|
6
|
+
import { Tooltip } from './Tooltip.js'
|
|
7
|
+
import { ZoomButtons } from '../button/ZoomButtons.js'
|
|
8
|
+
import { FullscreenButton } from '../button/FullscreenButton.js'
|
|
9
|
+
|
|
10
|
+
// external imports
|
|
11
|
+
import { select } from 'd3-selection'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A gridviz application.
|
|
15
|
+
*
|
|
16
|
+
* @module core
|
|
17
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
18
|
+
*/
|
|
19
|
+
export class Map {
|
|
20
|
+
/**
|
|
21
|
+
* @param {HTMLDivElement} container
|
|
22
|
+
* @param {object} opts
|
|
23
|
+
*/
|
|
24
|
+
constructor(container, opts) {
|
|
25
|
+
opts = opts || {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The layers.
|
|
29
|
+
* @type {Array.<import("./Layer.js").Layer>}
|
|
30
|
+
* */
|
|
31
|
+
this.layers = opts.layers || []
|
|
32
|
+
|
|
33
|
+
//get container element
|
|
34
|
+
this.container = container || document.getElementById('gridviz')
|
|
35
|
+
if (!this.container) {
|
|
36
|
+
console.error('Cannot find gridviz container element.')
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//https://css-tricks.com/absolute-positioning-inside-relative-positioning/
|
|
41
|
+
this.container.style.position = 'relative' // container element must have relative positioning
|
|
42
|
+
|
|
43
|
+
//set dimensions
|
|
44
|
+
/** @type {number} */
|
|
45
|
+
this.w = opts.w || this.container.offsetWidth
|
|
46
|
+
/** @type {number} */
|
|
47
|
+
this.h = opts.h || this.container.offsetHeight
|
|
48
|
+
|
|
49
|
+
// Create the main canvas (for rendering to screen)
|
|
50
|
+
/** @type {HTMLCanvasElement} */
|
|
51
|
+
this._canvas = opts.canvas || this.initialiseCanvas()
|
|
52
|
+
|
|
53
|
+
/** Initialize GeoCanvas
|
|
54
|
+
* @type {GeoCanvas}
|
|
55
|
+
* @private */
|
|
56
|
+
this.geoCanvas = new GeoCanvas(this._canvas, opts.x, opts.y, opts.z, opts)
|
|
57
|
+
|
|
58
|
+
this.geoCanvas.redraw = () => {
|
|
59
|
+
this.redraw()
|
|
60
|
+
}
|
|
61
|
+
this.geoCanvas.cancelCurrentRequests = () => {
|
|
62
|
+
// when the zoom level changes, avoid drawing outdated tiles, and ensure that requests are properly aborted when necessary
|
|
63
|
+
for (const layer of this.layers) {
|
|
64
|
+
//multires
|
|
65
|
+
if (layer.dataset?.datasets) {
|
|
66
|
+
for (const dataset of layer.dataset?.datasets) {
|
|
67
|
+
if (dataset?.cancelCurrentRequests) dataset.cancelCurrentRequests()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//single res
|
|
71
|
+
if (layer.dataset?.cancelCurrentRequests) layer.dataset?.cancelCurrentRequests()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// legend div
|
|
76
|
+
this.legend = opts.legendContainer
|
|
77
|
+
? select(opts.legendContainer) // Wrap the provided HTML node in a D3 selection
|
|
78
|
+
: null
|
|
79
|
+
this._legendProvidedByUser = !!opts.legendContainer
|
|
80
|
+
if (!this.legend) this.initialiseLegend()
|
|
81
|
+
|
|
82
|
+
//tooltip
|
|
83
|
+
|
|
84
|
+
// set App container as default parent element for tooltip
|
|
85
|
+
if (!opts.tooltip) opts.tooltip = {}
|
|
86
|
+
if (!opts.tooltip.parentElement) opts.tooltip.parentElement = this.container
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @private
|
|
90
|
+
* @type {Tooltip} */
|
|
91
|
+
this.tooltip = new Tooltip(opts.tooltip)
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Set to true so that the tooltip appears for the first layer starting from top where a cell is found.
|
|
95
|
+
* Otherwise, it shows a tooltip only for the first layer from the top with a cellInfoHTML property, even if there is no cell at the mouse position for this layer.
|
|
96
|
+
* @type {boolean} */
|
|
97
|
+
this.digForTooltip = opts.digForTooltip || false
|
|
98
|
+
|
|
99
|
+
// add event listeners to container
|
|
100
|
+
this.mouseOverHandler = (e) => this.focusCell(e)
|
|
101
|
+
this.mouseMoveHandler = (e) => this.focusCell(e)
|
|
102
|
+
this.mouseOutHandler = (e) => this.tooltip.hide()
|
|
103
|
+
this.geoCanvas.canvas.addEventListener('mouseover', this.mouseOverHandler)
|
|
104
|
+
this.geoCanvas.canvas.addEventListener('mousemove', this.mouseMoveHandler)
|
|
105
|
+
this.geoCanvas.canvas.addEventListener('mouseout', this.mouseOutHandler)
|
|
106
|
+
|
|
107
|
+
// listen for resize events on the App's container and handle them
|
|
108
|
+
this.defineResizeObserver()
|
|
109
|
+
|
|
110
|
+
// add extra logic to onZoomStartFun
|
|
111
|
+
this.geoCanvas.onZoomStartFun = (e) => {
|
|
112
|
+
if (opts.onZoomStartFun) opts.onZoomStartFun(e)
|
|
113
|
+
this.tooltip.hide()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
//for mouse over
|
|
117
|
+
/**
|
|
118
|
+
* @private
|
|
119
|
+
* @type {HTMLCanvasElement|null} */
|
|
120
|
+
this.canvasSave = null
|
|
121
|
+
|
|
122
|
+
this.selectionRectangleColor = opts.selectionRectangleColor || '#FF6347'
|
|
123
|
+
this.selectionRectangleWidthPix = opts.selectionRectangleWidthPix || (() => 4) //(r,z) => {}
|
|
124
|
+
|
|
125
|
+
// transparent background (e.g. leaflet) 'red painting' fix
|
|
126
|
+
this.transparentBackground = opts.transparentBackground
|
|
127
|
+
|
|
128
|
+
//set default globalCompositeOperation
|
|
129
|
+
this.defaultGlobalCompositeOperation =
|
|
130
|
+
opts.defaultGlobalCompositeOperation || this.geoCanvas.ctx.globalCompositeOperation
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @protected
|
|
135
|
+
* @returns {HTMLCanvasElement}
|
|
136
|
+
*/
|
|
137
|
+
initialiseCanvas() {
|
|
138
|
+
const canvas = document.createElement('canvas')
|
|
139
|
+
canvas.setAttribute('width', '' + this.w)
|
|
140
|
+
canvas.setAttribute('height', '' + this.h)
|
|
141
|
+
this.container.appendChild(canvas)
|
|
142
|
+
return canvas
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
initialiseLegend() {
|
|
146
|
+
this.legend = select(this.container)
|
|
147
|
+
.append('div') // Create a new container
|
|
148
|
+
.attr('id', 'gridviz-legend')
|
|
149
|
+
.style('position', 'absolute')
|
|
150
|
+
.style('width', 'auto')
|
|
151
|
+
.style('height', 'auto')
|
|
152
|
+
.style('background', '#FFFFFF')
|
|
153
|
+
//.style("padding", this.padding)
|
|
154
|
+
.style('border', '0px')
|
|
155
|
+
//.style('border-radius', '5px')
|
|
156
|
+
.style('box-shadow', '3px 3px 3px grey, -3px -3px 3px #ddd')
|
|
157
|
+
.style('font-family', 'Helvetica, Arial, sans-serif')
|
|
158
|
+
.style('bottom', '15px')
|
|
159
|
+
.style('right', '15px')
|
|
160
|
+
//hide
|
|
161
|
+
//.style("visibility", "hidden")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Set/get layer stack.
|
|
166
|
+
*
|
|
167
|
+
* @param {undefined|import("./Layer.js").Layer|import("./Layer.js").Layer[]} layers
|
|
168
|
+
* @returns { this | import("./Layer.js").Layer[] }
|
|
169
|
+
*/
|
|
170
|
+
layers_(layers) {
|
|
171
|
+
if (arguments.length === 0) return this.layers
|
|
172
|
+
if (arguments.length === 1)
|
|
173
|
+
if (Array.isArray(layers)) this.layers = layers
|
|
174
|
+
else this.layers = [layers]
|
|
175
|
+
else this.layers = arguments
|
|
176
|
+
return this
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** @returns {this} */
|
|
180
|
+
redraw() {
|
|
181
|
+
//remove legend elements
|
|
182
|
+
if (this.legend) this.legend.selectAll('*').remove()
|
|
183
|
+
|
|
184
|
+
//clear
|
|
185
|
+
this.geoCanvas.initCanvasTransform()
|
|
186
|
+
this.geoCanvas.clear(this.geoCanvas.backgroundColor)
|
|
187
|
+
|
|
188
|
+
const z = this.geoCanvas.view.z
|
|
189
|
+
this.updateExtentGeo()
|
|
190
|
+
|
|
191
|
+
const ctx = this.geoCanvas.offscreenCtx
|
|
192
|
+
|
|
193
|
+
//go through the layers
|
|
194
|
+
for (const layer of this.layers) {
|
|
195
|
+
//check if layer is visible
|
|
196
|
+
if (layer.visible && !layer.visible(z)) continue
|
|
197
|
+
|
|
198
|
+
//set layer alpha and blend mode
|
|
199
|
+
if (layer.alpha || layer.blendOperation) {
|
|
200
|
+
ctx.save()
|
|
201
|
+
if (layer.alpha) ctx.globalAlpha = layer.alpha(z)
|
|
202
|
+
if (layer.blendOperation) ctx.globalCompositeOperation = layer.blendOperation(z)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//set affin transform to draw with geographical coordinates
|
|
206
|
+
this.geoCanvas.setCanvasTransform()
|
|
207
|
+
|
|
208
|
+
//draw layer
|
|
209
|
+
layer.draw(this.geoCanvas, this.legend)
|
|
210
|
+
|
|
211
|
+
//draw layer filter
|
|
212
|
+
if (layer.filterColor) layer.drawFilter(this.geoCanvas)
|
|
213
|
+
|
|
214
|
+
//restore default alpha and blend operation
|
|
215
|
+
if (layer.alpha || layer.blendOperation) ctx.restore()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// one drawImage call: draw the offscreen canvas to the main canvas
|
|
219
|
+
this.geoCanvas.initCanvasTransform()
|
|
220
|
+
this.geoCanvas.ctx.drawImage(this.geoCanvas.offscreenCanvas, 0, 0)
|
|
221
|
+
|
|
222
|
+
this.canvasSave = null
|
|
223
|
+
|
|
224
|
+
return this
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @param {number} marginPx
|
|
229
|
+
* @returns {import('./GeoCanvas.js').Envelope}
|
|
230
|
+
* @public
|
|
231
|
+
*/
|
|
232
|
+
updateExtentGeo(marginPx = 20) {
|
|
233
|
+
return this.geoCanvas.updateExtentGeo(marginPx)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** @param {MouseEvent} e */
|
|
237
|
+
focusCell(e) {
|
|
238
|
+
// Don’t process hover events during active zoom
|
|
239
|
+
if (this.geoCanvas._isZooming) return;
|
|
240
|
+
|
|
241
|
+
//compute mouse geo position
|
|
242
|
+
const mousePositionGeo = {
|
|
243
|
+
x: this.geoCanvas.pixToGeoX(e.offsetX + this.tooltip.xMouseOffset),
|
|
244
|
+
y: this.geoCanvas.pixToGeoY(e.offsetY + this.tooltip.yMouseOffset),
|
|
245
|
+
}
|
|
246
|
+
/** @type {{cell:import('./Dataset.js').Cell,html:string,resolution:number} | undefined} */
|
|
247
|
+
const focus = this.getCellFocusInfo(mousePositionGeo)
|
|
248
|
+
|
|
249
|
+
// Transparent background (Leaflet): redraw base, then draw focus rect directly on the visible canvas
|
|
250
|
+
if (this.transparentBackground) {
|
|
251
|
+
// Always restore the base (offscreen) into the visible canvas
|
|
252
|
+
this.geoCanvas.initCanvasTransform();
|
|
253
|
+
this.geoCanvas.ctx.clearRect(0, 0, this.w, this.h);
|
|
254
|
+
this.geoCanvas.ctx.drawImage(this.geoCanvas.offscreenCanvas, 0, 0);
|
|
255
|
+
|
|
256
|
+
if (focus) {
|
|
257
|
+
this.tooltip.html(focus.html);
|
|
258
|
+
this.tooltip.setPosition(e);
|
|
259
|
+
this.tooltip.show();
|
|
260
|
+
|
|
261
|
+
const rectWPix = this.selectionRectangleWidthPix
|
|
262
|
+
? this.selectionRectangleWidthPix(focus.resolution, this.geoCanvas.view.z)
|
|
263
|
+
: 4;
|
|
264
|
+
|
|
265
|
+
const ctx = this.geoCanvas.ctx; // draw directly on visible canvas
|
|
266
|
+
ctx.save();
|
|
267
|
+
ctx.strokeStyle = this.selectionRectangleColor;
|
|
268
|
+
ctx.lineWidth = rectWPix;
|
|
269
|
+
ctx.beginPath();
|
|
270
|
+
// compute in pixel space (no geo transform on ctx)
|
|
271
|
+
const xPix = this.geoCanvas.geoToPixX(focus.cell.x);
|
|
272
|
+
const yPix = this.geoCanvas.geoToPixY(focus.cell.y);
|
|
273
|
+
const wPix = focus.resolution / this.geoCanvas.view.z;
|
|
274
|
+
const hPix = -wPix; // y axis inverted in geoToPix
|
|
275
|
+
ctx.rect(
|
|
276
|
+
xPix - rectWPix / 2,
|
|
277
|
+
yPix + rectWPix / 2,
|
|
278
|
+
wPix + rectWPix,
|
|
279
|
+
hPix - rectWPix
|
|
280
|
+
);
|
|
281
|
+
ctx.stroke();
|
|
282
|
+
ctx.restore();
|
|
283
|
+
} else {
|
|
284
|
+
this.tooltip.hide();
|
|
285
|
+
}
|
|
286
|
+
return; // handled
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (focus) {
|
|
290
|
+
this.geoCanvas.canvas.style.cursor = 'pointer';
|
|
291
|
+
this.tooltip.html(focus.html)
|
|
292
|
+
this.tooltip.setPosition(e)
|
|
293
|
+
this.tooltip.show()
|
|
294
|
+
|
|
295
|
+
//show cell position as a rectangle
|
|
296
|
+
if (!this.canvasSave) {
|
|
297
|
+
this.canvasSave = document.createElement('canvas')
|
|
298
|
+
this.canvasSave.setAttribute('width', '' + this.w)
|
|
299
|
+
this.canvasSave.setAttribute('height', '' + this.h)
|
|
300
|
+
this.canvasSave.getContext('2d')?.drawImage(this.geoCanvas.offscreenCanvas, 0, 0)
|
|
301
|
+
} else {
|
|
302
|
+
this.geoCanvas.offscreenCtx.drawImage(this.canvasSave, 0, 0)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
//draw image saved + draw rectangle
|
|
306
|
+
const rectWPix = this.selectionRectangleWidthPix
|
|
307
|
+
? this.selectionRectangleWidthPix(focus.resolution, this.geoCanvas.view.z)
|
|
308
|
+
: 4
|
|
309
|
+
this.geoCanvas.initCanvasTransform()
|
|
310
|
+
const ctx = this.geoCanvas.offscreenCtx
|
|
311
|
+
ctx.strokeStyle = this.selectionRectangleColor
|
|
312
|
+
ctx.lineWidth = rectWPix
|
|
313
|
+
ctx.beginPath()
|
|
314
|
+
|
|
315
|
+
ctx.rect(
|
|
316
|
+
this.geoCanvas.geoToPixX(focus.cell.x) - rectWPix / 2,
|
|
317
|
+
this.geoCanvas.geoToPixY(focus.cell.y) + rectWPix / 2,
|
|
318
|
+
focus.resolution / this.geoCanvas.view.z + rectWPix,
|
|
319
|
+
-focus.resolution / this.geoCanvas.view.z - rectWPix
|
|
320
|
+
)
|
|
321
|
+
ctx.stroke()
|
|
322
|
+
this.geoCanvas.ctx.drawImage(this.geoCanvas.offscreenCanvas, 0, 0)
|
|
323
|
+
} else {
|
|
324
|
+
this.geoCanvas.canvas.style.cursor = 'default';
|
|
325
|
+
this.tooltip.hide()
|
|
326
|
+
if (this.canvasSave) this.geoCanvas.ctx.drawImage(this.canvasSave, 0, 0)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Return the cell HTML info at a given geo position.
|
|
332
|
+
* This is usefull for user interactions, to show this info where the user clicks for example.
|
|
333
|
+
*
|
|
334
|
+
* @param {{x:number,y:number}} posGeo
|
|
335
|
+
* @returns {{cell:import('./Dataset.js').Cell,html:string,resolution:number} | undefined}
|
|
336
|
+
* @protected
|
|
337
|
+
*/
|
|
338
|
+
getCellFocusInfo(posGeo) {
|
|
339
|
+
|
|
340
|
+
// go through the layers, starting from top one
|
|
341
|
+
const z = this.geoCanvas.view.z
|
|
342
|
+
for (let i = this.layers.length - 1; i >= 0; i--) {
|
|
343
|
+
|
|
344
|
+
/** @type {import("./Layer.js").Layer} */
|
|
345
|
+
const layer = this.layers[i]
|
|
346
|
+
|
|
347
|
+
if (layer.visible && !layer.visible(z)) continue
|
|
348
|
+
if (layer.cellInfoHTML === 'none') continue
|
|
349
|
+
if (!layer.cellInfoHTML) continue
|
|
350
|
+
if (!layer.getDataset) continue
|
|
351
|
+
|
|
352
|
+
// get dataset
|
|
353
|
+
const dsc = layer.getDataset(z)
|
|
354
|
+
if (!dsc) continue
|
|
355
|
+
|
|
356
|
+
//get cell at mouse position
|
|
357
|
+
/** @type {import('./Dataset.js').Cell|undefined} */
|
|
358
|
+
const cell = dsc.getCellFromPosition(posGeo, dsc.getViewCache())
|
|
359
|
+
|
|
360
|
+
// no cell found at theis position for this layer
|
|
361
|
+
if (!cell)
|
|
362
|
+
// dig to the next layer below
|
|
363
|
+
if (this.digForTooltip) continue
|
|
364
|
+
// no tooltip to show
|
|
365
|
+
else return undefined
|
|
366
|
+
|
|
367
|
+
//rare case for a dataset with mixed resolutions
|
|
368
|
+
if (dsc.mixedResolution) {
|
|
369
|
+
const r = +dsc.mixedResolution(cell)
|
|
370
|
+
const html = layer.cellInfoHTML(cell, r)
|
|
371
|
+
if (!html) return undefined
|
|
372
|
+
return { cell: cell, html: html, resolution: r }
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const html = layer.cellInfoHTML(cell, dsc.getResolution())
|
|
376
|
+
if (!html) return undefined
|
|
377
|
+
return { cell: cell, html: html, resolution: dsc.getResolution() }
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @param {number} x
|
|
383
|
+
* @param {number} y
|
|
384
|
+
* @param {number|undefined} z
|
|
385
|
+
*/
|
|
386
|
+
setView(x, y, z = undefined) {
|
|
387
|
+
this.geoCanvas.view.x = x
|
|
388
|
+
this.geoCanvas.view.y = y
|
|
389
|
+
if (z != undefined) this.geoCanvas.view.z = z
|
|
390
|
+
return this
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** @returns {import('./GeoCanvas.js').View} */
|
|
394
|
+
getView() {
|
|
395
|
+
return this.geoCanvas.view
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** @returns {number} */
|
|
399
|
+
getZoom() {
|
|
400
|
+
return this.geoCanvas.view.z
|
|
401
|
+
}
|
|
402
|
+
/** @param {number} z @returns {this} */
|
|
403
|
+
setZoom(z) {
|
|
404
|
+
this.geoCanvas.view.z = z
|
|
405
|
+
return this
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** @returns {Array.<number|undefined>} */
|
|
409
|
+
getCenterExtent() {
|
|
410
|
+
return this.geoCanvas.getCenterExtent()
|
|
411
|
+
}
|
|
412
|
+
/** @param {Array.<number>} val @returns {this} */
|
|
413
|
+
setCenterExtent(val) {
|
|
414
|
+
this.geoCanvas.setCenterExtent(val)
|
|
415
|
+
return this
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** @returns {Array.<number>} */
|
|
419
|
+
getZoomExtent() {
|
|
420
|
+
return this.geoCanvas.getZoomExtent()
|
|
421
|
+
}
|
|
422
|
+
/** @param {Array.<number>} val @returns {this} */
|
|
423
|
+
setZoomExtent(val) {
|
|
424
|
+
this.geoCanvas.setZoomExtent(val)
|
|
425
|
+
return this
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** @returns {string} */
|
|
429
|
+
getBackgroundColor() {
|
|
430
|
+
return this.geoCanvas.backgroundColor
|
|
431
|
+
}
|
|
432
|
+
/** @param {string} val @returns {this} */
|
|
433
|
+
setBackgroundColor(val) {
|
|
434
|
+
this.geoCanvas.backgroundColor = val
|
|
435
|
+
return this
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Adds a set of zoom buttons to the map
|
|
440
|
+
*
|
|
441
|
+
* @param {object} opts
|
|
442
|
+
* @returns {this}
|
|
443
|
+
*/
|
|
444
|
+
addZoomButtons(opts) {
|
|
445
|
+
// * opts.id
|
|
446
|
+
// * opts.onZoom - custom event handler function
|
|
447
|
+
// * opts.x
|
|
448
|
+
// * opts.y
|
|
449
|
+
// * opts.delta - zoom delta applied on each click
|
|
450
|
+
|
|
451
|
+
this.zoomButtons = new ZoomButtons({
|
|
452
|
+
map: this,
|
|
453
|
+
id: opts?.id || 'gridviz-zoom-buttons-' + this.container.id,
|
|
454
|
+
class: opts?.class,
|
|
455
|
+
x: opts?.x,
|
|
456
|
+
y: opts?.y,
|
|
457
|
+
onZoom: opts?.onZoom,
|
|
458
|
+
delta: opts?.delta || 0.2,
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
return this
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Adds a fullscreen toggle button to the app
|
|
466
|
+
*
|
|
467
|
+
* @param {object} opts
|
|
468
|
+
* @returns {this}
|
|
469
|
+
*/
|
|
470
|
+
addFullscreenButton(opts) {
|
|
471
|
+
// * opts.map - the gridviz map
|
|
472
|
+
// * opts.id
|
|
473
|
+
// * opts.x
|
|
474
|
+
// * opts.y
|
|
475
|
+
|
|
476
|
+
this.fullscreenButton = new FullscreenButton({
|
|
477
|
+
map: this,
|
|
478
|
+
id: opts?.id || 'gridviz-fullscreen-button',
|
|
479
|
+
class: opts?.class,
|
|
480
|
+
x: opts?.x,
|
|
481
|
+
y: opts?.y,
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
return this
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** @returns {this} */
|
|
488
|
+
setViewFromURL() {
|
|
489
|
+
this.geoCanvas.setViewFromURL()
|
|
490
|
+
return this
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* @description Add a resize event observer to the Apps container and update the canvas accordingly
|
|
495
|
+
* @memberof App
|
|
496
|
+
*/
|
|
497
|
+
defineResizeObserver() {
|
|
498
|
+
// Track whether the observer is currently processing a resize event
|
|
499
|
+
let resizePending = false
|
|
500
|
+
|
|
501
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
502
|
+
if (!Array.isArray(entries) || !entries.length) return
|
|
503
|
+
|
|
504
|
+
let container = this.container
|
|
505
|
+
|
|
506
|
+
// Ensure the container has valid dimensions
|
|
507
|
+
if (container.clientWidth > 0 && container.clientHeight > 0) {
|
|
508
|
+
if (!resizePending) {
|
|
509
|
+
resizePending = true // Prevent overlapping resize triggers
|
|
510
|
+
|
|
511
|
+
window.requestAnimationFrame(() => {
|
|
512
|
+
resizePending = false // Reset the flag after processing
|
|
513
|
+
|
|
514
|
+
// Check for size changes
|
|
515
|
+
if (this.h !== container.clientHeight || this.w !== container.clientWidth) {
|
|
516
|
+
this.h = container.clientHeight
|
|
517
|
+
this.w = container.clientWidth
|
|
518
|
+
|
|
519
|
+
// Update geoCanvas sizes
|
|
520
|
+
this.geoCanvas.h = this.h
|
|
521
|
+
this.geoCanvas.w = this.w
|
|
522
|
+
this.geoCanvas.canvas.setAttribute('width', String(this.w))
|
|
523
|
+
this.geoCanvas.canvas.setAttribute('height', String(this.h))
|
|
524
|
+
this.geoCanvas.offscreenCanvas.setAttribute('width', String(this.w))
|
|
525
|
+
this.geoCanvas.offscreenCanvas.setAttribute('height', String(this.h))
|
|
526
|
+
|
|
527
|
+
this.redraw()
|
|
528
|
+
|
|
529
|
+
// Optionally reposition UI elements
|
|
530
|
+
// if (this.zoomButtons) this.zoomButtons.node.style.left = this.w - 50 + 'px';
|
|
531
|
+
// if (this.fullscreenButton) this.fullscreenButton.node.style.left = this.w - 50 + 'px';
|
|
532
|
+
}
|
|
533
|
+
})
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
resizeObserver.observe(this.container)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* @description Destroy the map and it's event listeners
|
|
543
|
+
* This should significantly reduce the memory used when creating and destroying gridviz map instances (for example in leaflet-gridviz)
|
|
544
|
+
* @memberof App
|
|
545
|
+
*/
|
|
546
|
+
destroy() {
|
|
547
|
+
// clear layers
|
|
548
|
+
this.layers = []
|
|
549
|
+
this.bgLayers = []
|
|
550
|
+
|
|
551
|
+
// remove event listeners from container
|
|
552
|
+
this.container.removeEventListener('mouseover', this.mouseOverHandler)
|
|
553
|
+
this.container.removeEventListener('mousemove', this.mouseMoveHandler)
|
|
554
|
+
this.container.removeEventListener('mouseout', this.mouseOutHandler)
|
|
555
|
+
|
|
556
|
+
// remove canvas
|
|
557
|
+
this.geoCanvas.canvas.remove()
|
|
558
|
+
|
|
559
|
+
// remove legend
|
|
560
|
+
// only remove legend if gridviz created it
|
|
561
|
+
if (!this._legendProvidedByUser) {
|
|
562
|
+
this.legend?.remove()
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// remove tooltip
|
|
566
|
+
this.tooltip.tooltip?.remove()
|
|
567
|
+
}
|
|
568
|
+
}
|