gridviz 3.0.17 → 3.0.19
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 +1 -0
- package/dist/gridviz.js +195 -135
- package/dist/gridviz.min.js +1 -1
- package/package.json +1 -1
- package/src/button/Button.js +1 -1
- package/src/core/GeoCanvas.js +49 -28
- package/src/core/Map.js +54 -32
- package/src/core/Tooltip.js +50 -51
- package/src/dataset/TiledGrid.js +34 -21
- package/src/utils/ternary.js +6 -1
package/package.json
CHANGED
package/src/button/Button.js
CHANGED
|
@@ -46,7 +46,7 @@ export class Button {
|
|
|
46
46
|
this.style('display', 'flex')
|
|
47
47
|
this.style('justify-content', 'center')
|
|
48
48
|
this.style('align-items', 'center')
|
|
49
|
-
this.style('width', '
|
|
49
|
+
this.style('width', '35px')
|
|
50
50
|
this.style('height', '30px')
|
|
51
51
|
// this.style(padding , '4px'
|
|
52
52
|
|
package/src/core/GeoCanvas.js
CHANGED
|
@@ -78,44 +78,24 @@ export class GeoCanvas {
|
|
|
78
78
|
this.extGeo = { xMin: NaN, xMax: NaN, yMin: NaN, yMax: NaN }
|
|
79
79
|
this.updateExtentGeo()
|
|
80
80
|
|
|
81
|
-
//rely on d3
|
|
81
|
+
//rely on d3 for zoom
|
|
82
82
|
if (!opts.disableZoom) {
|
|
83
83
|
let tP = zoomIdentity
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
let debounceTimeout = null // Add a debounce timeout variable
|
|
84
86
|
const z = d3zoom()
|
|
85
|
-
//to make the zooming a bit faster
|
|
87
|
+
// to make the zooming a bit faster
|
|
86
88
|
.wheelDelta((e) => -e.deltaY * (e.deltaMode === 1 ? 0.07 : e.deltaMode ? 1 : 0.004))
|
|
87
89
|
.on('zoom', (e) => {
|
|
88
90
|
const t = e.transform
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
+
const zoomFactor = tP.k / t.k
|
|
92
|
+
if (zoomFactor == 1) {
|
|
91
93
|
//pan
|
|
92
94
|
const dx = tP.x - t.x
|
|
93
95
|
const dy = tP.y - t.y
|
|
94
96
|
this.pan(dx * this.view.z, -dy * this.view.z)
|
|
95
97
|
} else {
|
|
96
|
-
|
|
97
|
-
if (se instanceof WheelEvent) {
|
|
98
|
-
//zoom at the mouse position
|
|
99
|
-
this.zoom(
|
|
100
|
-
f,
|
|
101
|
-
this.pixToGeoX(e.sourceEvent.offsetX),
|
|
102
|
-
this.pixToGeoY(e.sourceEvent.offsetY)
|
|
103
|
-
)
|
|
104
|
-
} else if (se instanceof TouchEvent) {
|
|
105
|
-
if (!se.targetTouches.length) return
|
|
106
|
-
//pinch zoom
|
|
107
|
-
//compute average position of the touches
|
|
108
|
-
let tx = 0,
|
|
109
|
-
ty = 0
|
|
110
|
-
for (let tt of se.targetTouches) {
|
|
111
|
-
tx += tt.clientX
|
|
112
|
-
ty += tt.clientY
|
|
113
|
-
}
|
|
114
|
-
tx /= se.targetTouches.length
|
|
115
|
-
ty /= se.targetTouches.length
|
|
116
|
-
//zoom at this average position
|
|
117
|
-
this.zoom(f, this.pixToGeoX(tx), this.pixToGeoY(ty))
|
|
118
|
-
}
|
|
98
|
+
handleZoom(e, zoomFactor)
|
|
119
99
|
}
|
|
120
100
|
tP = t
|
|
121
101
|
|
|
@@ -134,13 +114,48 @@ export class GeoCanvas {
|
|
|
134
114
|
if (this.onZoomStartFun) this.onZoomStartFun(e)
|
|
135
115
|
})
|
|
136
116
|
.on('end', (e) => {
|
|
137
|
-
// end of zoom event
|
|
117
|
+
// end of pan/zoom event
|
|
138
118
|
this.redraw()
|
|
139
119
|
this.canvasSave = { c: null, dx: 0, dy: 0, f: 1 }
|
|
140
120
|
|
|
141
121
|
if (this.onZoomEndFun) this.onZoomEndFun(e)
|
|
142
122
|
})
|
|
123
|
+
// @ts-ignore
|
|
143
124
|
z(select(this.canvas))
|
|
125
|
+
|
|
126
|
+
const handleZoom = (event, zoomFactor) => {
|
|
127
|
+
// cancel ongoing data requests
|
|
128
|
+
this.cancelCurrentRequests()
|
|
129
|
+
const se = event.sourceEvent
|
|
130
|
+
|
|
131
|
+
if (se instanceof WheelEvent) {
|
|
132
|
+
//zoom at the mouse position
|
|
133
|
+
this.zoom(
|
|
134
|
+
zoomFactor,
|
|
135
|
+
// @ts-ignore
|
|
136
|
+
this.pixToGeoX(se.offsetX),
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
this.pixToGeoY(se.offsetY)
|
|
139
|
+
)
|
|
140
|
+
} else if (se instanceof TouchEvent) {
|
|
141
|
+
//compute average position of the touches
|
|
142
|
+
let tx = 0,
|
|
143
|
+
ty = 0
|
|
144
|
+
for (let tt of se.targetTouches) {
|
|
145
|
+
tx += tt.clientX
|
|
146
|
+
ty += tt.clientY
|
|
147
|
+
}
|
|
148
|
+
tx /= se.targetTouches.length
|
|
149
|
+
ty /= se.targetTouches.length
|
|
150
|
+
|
|
151
|
+
// Adjust for container's offset
|
|
152
|
+
// tx -= containerRect.left
|
|
153
|
+
// ty -= containerRect.top
|
|
154
|
+
|
|
155
|
+
//zoom at this average position
|
|
156
|
+
this.zoom(zoomFactor, this.pixToGeoX(tx), this.pixToGeoY(ty))
|
|
157
|
+
}
|
|
158
|
+
}
|
|
144
159
|
}
|
|
145
160
|
|
|
146
161
|
//center extent
|
|
@@ -215,6 +230,11 @@ export class GeoCanvas {
|
|
|
215
230
|
throw new Error('Method redraw not implemented.')
|
|
216
231
|
}
|
|
217
232
|
|
|
233
|
+
/** When the zoom level changes, ensures that any ongoing requests are aborted before new ones are initiated. */
|
|
234
|
+
cancelCurrentRequests() {
|
|
235
|
+
throw new Error('Method cancelCurrentRequests not implemented.')
|
|
236
|
+
}
|
|
237
|
+
|
|
218
238
|
/**
|
|
219
239
|
* Clear. To be used before a redraw for example.
|
|
220
240
|
* @param {string} color
|
|
@@ -310,6 +330,7 @@ export class GeoCanvas {
|
|
|
310
330
|
this.canvasSave.f /= f
|
|
311
331
|
this.canvasSave.dx = this.geoToPixX(xGeo) * (1 - this.canvasSave.f)
|
|
312
332
|
this.canvasSave.dy = this.geoToPixY(yGeo) * (1 - this.canvasSave.f)
|
|
333
|
+
this.clear(this.backgroundColor)
|
|
313
334
|
this.offscreenCtx.drawImage(
|
|
314
335
|
this.canvasSave.c,
|
|
315
336
|
this.canvasSave.dx,
|
package/src/core/Map.js
CHANGED
|
@@ -58,11 +58,25 @@ export class Map {
|
|
|
58
58
|
this.geoCanvas.redraw = () => {
|
|
59
59
|
this.redraw()
|
|
60
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
|
+
}
|
|
61
74
|
|
|
62
75
|
// legend div
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
this.legend = opts.legendContainer
|
|
77
|
+
? select(opts.legendContainer) // Wrap the provided HTML node in a D3 selection
|
|
78
|
+
: null
|
|
79
|
+
if (!this.legend) this.initialiseLegend()
|
|
66
80
|
|
|
67
81
|
//tooltip
|
|
68
82
|
|
|
@@ -122,9 +136,9 @@ export class Map {
|
|
|
122
136
|
}
|
|
123
137
|
|
|
124
138
|
initialiseLegend() {
|
|
125
|
-
this.legend = select(this.container
|
|
126
|
-
.append('div')
|
|
127
|
-
.attr('id',
|
|
139
|
+
this.legend = select(this.container)
|
|
140
|
+
.append('div') // Create a new container
|
|
141
|
+
.attr('id', 'gridviz-legend')
|
|
128
142
|
.style('position', 'absolute')
|
|
129
143
|
.style('width', 'auto')
|
|
130
144
|
.style('height', 'auto')
|
|
@@ -432,35 +446,43 @@ export class Map {
|
|
|
432
446
|
* @memberof App
|
|
433
447
|
*/
|
|
434
448
|
defineResizeObserver() {
|
|
435
|
-
//
|
|
449
|
+
// Track whether the observer is currently processing a resize event
|
|
450
|
+
let resizePending = false
|
|
451
|
+
|
|
436
452
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
453
|
+
if (!Array.isArray(entries) || !entries.length) return
|
|
454
|
+
|
|
437
455
|
let container = this.container
|
|
438
|
-
|
|
456
|
+
|
|
457
|
+
// Ensure the container has valid dimensions
|
|
439
458
|
if (container.clientWidth > 0 && container.clientHeight > 0) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
459
|
+
if (!resizePending) {
|
|
460
|
+
resizePending = true // Prevent overlapping resize triggers
|
|
461
|
+
|
|
462
|
+
window.requestAnimationFrame(() => {
|
|
463
|
+
resizePending = false // Reset the flag after processing
|
|
464
|
+
|
|
465
|
+
// Check for size changes
|
|
466
|
+
if (this.h !== container.clientHeight || this.w !== container.clientWidth) {
|
|
467
|
+
this.h = container.clientHeight
|
|
468
|
+
this.w = container.clientWidth
|
|
469
|
+
|
|
470
|
+
// Update geoCanvas sizes
|
|
471
|
+
this.geoCanvas.h = this.h
|
|
472
|
+
this.geoCanvas.w = this.w
|
|
473
|
+
this.geoCanvas.canvas.setAttribute('width', String(this.w))
|
|
474
|
+
this.geoCanvas.canvas.setAttribute('height', String(this.h))
|
|
475
|
+
this.geoCanvas.offscreenCanvas.setAttribute('width', String(this.w))
|
|
476
|
+
this.geoCanvas.offscreenCanvas.setAttribute('height', String(this.h))
|
|
477
|
+
|
|
478
|
+
this.redraw()
|
|
479
|
+
|
|
480
|
+
// Optionally reposition UI elements
|
|
481
|
+
// if (this.zoomButtons) this.zoomButtons.node.style.left = this.w - 50 + 'px';
|
|
482
|
+
// if (this.fullscreenButton) this.fullscreenButton.node.style.left = this.w - 50 + 'px';
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
}
|
|
464
486
|
}
|
|
465
487
|
})
|
|
466
488
|
|
package/src/core/Tooltip.js
CHANGED
|
@@ -16,8 +16,6 @@ export class Tooltip {
|
|
|
16
16
|
constructor(opts) {
|
|
17
17
|
opts = opts || {}
|
|
18
18
|
|
|
19
|
-
/** @type {string} */
|
|
20
|
-
this.div = opts.div || 'tooltip_eurostat'
|
|
21
19
|
/** @type {string} */
|
|
22
20
|
this.maxWidth = opts.maxWidth || '20em'
|
|
23
21
|
/** @type {string} */
|
|
@@ -47,21 +45,15 @@ export class Tooltip {
|
|
|
47
45
|
this.xMouseOffset = opts.xMouseOffset || 0
|
|
48
46
|
/** @type {HTMLElement} */
|
|
49
47
|
this.parentElement = opts.parentElement || document.body
|
|
48
|
+
/** @type {HTMLElement} */
|
|
49
|
+
this.tooltipElement = opts.tooltipElement || null
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* @public
|
|
53
53
|
* @type {import("d3-selection").Selection} */
|
|
54
|
-
this.tooltip =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//create tooltip DOM node
|
|
58
|
-
// this.tooltip = select(
|
|
59
|
-
// '#' + this.parentElement.id && this.parentElement.id != ''
|
|
60
|
-
// ? '#' + this.parentElement.id
|
|
61
|
-
// : 'body'
|
|
62
|
-
// )
|
|
63
|
-
this.tooltip = select('body').append('div').attr('id', this.div)
|
|
64
|
-
}
|
|
54
|
+
this.tooltip = opts.tooltipElement
|
|
55
|
+
? select(opts.tooltipElement) // Wrap the provided HTML node in a D3 selection
|
|
56
|
+
: select(this.parentElement).append('div').attr('id', 'gridviz-tooltip').attr('class', 'gridviz-tooltip') // create default element
|
|
65
57
|
|
|
66
58
|
//initialise
|
|
67
59
|
this.tooltip.style('max-width', this.maxWidth)
|
|
@@ -77,6 +69,11 @@ export class Tooltip {
|
|
|
77
69
|
this.tooltip.style('pointer-events', 'none')
|
|
78
70
|
this.tooltip.style('opacity', '0')
|
|
79
71
|
this.tooltip.style('text-wrap', 'nowrap')
|
|
72
|
+
this.tooltip.style('z-index', 99999999) // important for leaflet-gridviz etc
|
|
73
|
+
|
|
74
|
+
// these placeholders are needed to prevent an infinite DOM resizeObserver loop:
|
|
75
|
+
this.tooltip.style('left', '0')
|
|
76
|
+
this.tooltip.style('top', '0')
|
|
80
77
|
|
|
81
78
|
// aria-labels (thanks to wahlatlas)
|
|
82
79
|
this.tooltip.attr('role', 'tooltip').attr('aria-live', 'polite')
|
|
@@ -107,14 +104,51 @@ export class Tooltip {
|
|
|
107
104
|
* @param {MouseEvent} event
|
|
108
105
|
*/
|
|
109
106
|
setPosition(event) {
|
|
107
|
+
// Get the bounding rect of the parent container (map2)
|
|
110
108
|
let parentRect = this.parentElement.getBoundingClientRect()
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
let
|
|
110
|
+
// Get the mouse position (relative to the parent container)
|
|
111
|
+
let x = event.clientX - parentRect.left + this.xOffset // Relative to parent
|
|
112
|
+
let y = event.clientY - parentRect.top - this.yOffset // Relative to parent
|
|
114
113
|
|
|
114
|
+
// Now, apply the position to the tooltip
|
|
115
115
|
this.tooltip.style('left', x + 'px').style('top', y + 'px')
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
// Ensure the tooltip stays inside the parent container
|
|
118
|
+
this.ensureTooltipInsideContainer(event, parentRect, this.tooltip.node())
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @function ensureTooltipInsideContainer
|
|
122
|
+
* @description Prevents the tooltip from overflowing out of the App container (ensures that the tooltip is inside the gridviz container)
|
|
123
|
+
* @param {MouseEvent} event
|
|
124
|
+
* @param {DOMRect} parentRect
|
|
125
|
+
* @param {HTMLElement} tooltipNode
|
|
126
|
+
*/
|
|
127
|
+
ensureTooltipInsideContainer(event, parentRect, tooltipNode) {
|
|
128
|
+
let node = tooltipNode
|
|
129
|
+
let parentWidth = parentRect.width
|
|
130
|
+
let parentHeight = parentRect.height
|
|
131
|
+
|
|
132
|
+
// Ensure tooltip doesn't go beyond the right edge
|
|
133
|
+
if (node.offsetLeft + node.clientWidth > parentWidth) {
|
|
134
|
+
let left = event.clientX - node.clientWidth - this.xOffset
|
|
135
|
+
node.style.left = left + 'px'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Ensure tooltip doesn't go beyond the bottom edge
|
|
139
|
+
if (node.offsetTop + node.clientHeight > parentHeight) {
|
|
140
|
+
node.style.top = parentHeight - node.clientHeight + 'px'
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Ensure tooltip doesn't go above the top edge
|
|
144
|
+
if (node.offsetTop < 0) {
|
|
145
|
+
node.style.top = 0 + 'px'
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Ensure tooltip doesn't go beyond the left edge
|
|
149
|
+
if (node.offsetLeft < 0) {
|
|
150
|
+
node.style.left = 0 + 'px'
|
|
151
|
+
}
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
/*
|
|
@@ -145,39 +179,4 @@ export class Tooltip {
|
|
|
145
179
|
this.tooltip.attr(k, v)
|
|
146
180
|
return this
|
|
147
181
|
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @function ensureTooltipInsideContainer
|
|
151
|
-
* @description Prevents the tooltip from overflowing out of the App container (ensures that the tooltip is inside the gridviz container)
|
|
152
|
-
* @param {MouseEvent} event
|
|
153
|
-
* @param {DOMRect} parentRect
|
|
154
|
-
*/
|
|
155
|
-
ensureTooltipInsideContainer = function (event, parentRect) {
|
|
156
|
-
let node = this.tooltip.node()
|
|
157
|
-
let parentWidth = parentRect.width
|
|
158
|
-
let parentHeight = parentRect.height
|
|
159
|
-
|
|
160
|
-
//too far right
|
|
161
|
-
if (node.offsetLeft > parentRect.left + parentWidth - node.clientWidth) {
|
|
162
|
-
let left = event.x - node.clientWidth - this.xOffset
|
|
163
|
-
node.style.left = left + 'px'
|
|
164
|
-
// check if mouse covers tooltip
|
|
165
|
-
if (node.offsetLeft + node.clientWidth > event.x) {
|
|
166
|
-
//move tooltip left so it doesnt cover mouse
|
|
167
|
-
let left2 = event.x - node.clientWidth - this.xOffset
|
|
168
|
-
node.style.left = left2 + 'px'
|
|
169
|
-
}
|
|
170
|
-
// node.style.top = node.offsetTop + config.yOffset + "px";
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
//too far down
|
|
174
|
-
if (node.offsetTop + node.clientHeight > parentRect.top + parentHeight) {
|
|
175
|
-
node.style.top = node.offsetTop - node.clientHeight + 'px'
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
//too far up
|
|
179
|
-
if (node.offsetTop < parentRect.top) {
|
|
180
|
-
node.style.top = parentRect.top + this.yOffset + 'px'
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
182
|
}
|
package/src/dataset/TiledGrid.js
CHANGED
|
@@ -67,8 +67,7 @@ export class TiledGrid extends Dataset {
|
|
|
67
67
|
this.infoLoadingStatus = 'failed'
|
|
68
68
|
}
|
|
69
69
|
})()
|
|
70
|
-
} else if (this.infoLoadingStatus === 'loaded' || this.infoLoadingStatus === 'failed')
|
|
71
|
-
this.map.redraw()
|
|
70
|
+
} else if (this.infoLoadingStatus === 'loaded' || this.infoLoadingStatus === 'failed') this.map.redraw()
|
|
72
71
|
return this
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -106,6 +105,10 @@ export class TiledGrid extends Dataset {
|
|
|
106
105
|
async getData(extGeo) {
|
|
107
106
|
if (!this.info) return this
|
|
108
107
|
|
|
108
|
+
// Create an AbortController for the current data request
|
|
109
|
+
this.abortController = new AbortController()
|
|
110
|
+
const signal = this.abortController.signal
|
|
111
|
+
|
|
109
112
|
// Get the tiling envelope and check bounds
|
|
110
113
|
const tb = this.getTilingEnvelope(extGeo)
|
|
111
114
|
if (!tb) return this
|
|
@@ -117,10 +120,8 @@ export class TiledGrid extends Dataset {
|
|
|
117
120
|
const yMin = Math.max(tb.yMin, gbYMin)
|
|
118
121
|
const yMax = Math.min(tb.yMax, gbYMax)
|
|
119
122
|
|
|
120
|
-
// Count the total number of tiles
|
|
121
123
|
const totalTiles = (xMax - xMin + 1) * (yMax - yMin + 1)
|
|
122
|
-
|
|
123
|
-
let processedTiles = 0 // Counter for loaded tiles
|
|
124
|
+
let processedTiles = 0
|
|
124
125
|
const tilePromises = []
|
|
125
126
|
|
|
126
127
|
// Iterate over tiles within bounds
|
|
@@ -128,8 +129,8 @@ export class TiledGrid extends Dataset {
|
|
|
128
129
|
for (let yT = yMin; yT <= yMax; yT++) {
|
|
129
130
|
if (!this.cache[xT]) this.cache[xT] = {}
|
|
130
131
|
|
|
131
|
-
// Skip already loaded or
|
|
132
|
-
if (this.cache[xT][yT]) {
|
|
132
|
+
// Skip already loaded tiles or retry failed ones
|
|
133
|
+
if (this.cache[xT][yT] && this.cache[xT][yT] !== 'failed') {
|
|
133
134
|
++processedTiles
|
|
134
135
|
continue
|
|
135
136
|
}
|
|
@@ -137,21 +138,16 @@ export class TiledGrid extends Dataset {
|
|
|
137
138
|
// Mark tile as loading
|
|
138
139
|
this.cache[xT][yT] = 'loading'
|
|
139
140
|
|
|
140
|
-
// Prepare the tile request and store it in tilePromises
|
|
141
141
|
tilePromises.push(
|
|
142
|
-
this.loadTile(xT, yT)
|
|
142
|
+
this.loadTile(xT, yT, signal)
|
|
143
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
144
|
this.cache[xT][yT] = tile
|
|
149
145
|
|
|
150
|
-
//
|
|
146
|
+
// Check if this is the last tile
|
|
147
|
+
const isLastTile = ++processedTiles === totalTiles
|
|
151
148
|
this.checkAndRedraw(tile, isLastTile)
|
|
152
149
|
})
|
|
153
150
|
.catch(() => {
|
|
154
|
-
// Mark as failed but allow processing to continue
|
|
155
151
|
this.cache[xT][yT] = 'failed'
|
|
156
152
|
++processedTiles
|
|
157
153
|
})
|
|
@@ -159,30 +155,47 @@ export class TiledGrid extends Dataset {
|
|
|
159
155
|
}
|
|
160
156
|
}
|
|
161
157
|
|
|
162
|
-
// Handle all tile promises, including failures
|
|
163
158
|
await Promise.allSettled(tilePromises)
|
|
164
159
|
return this
|
|
165
160
|
}
|
|
166
161
|
|
|
167
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Load a tile.
|
|
164
|
+
*
|
|
165
|
+
* @param {number} xT
|
|
166
|
+
* @param {number} yT
|
|
167
|
+
* @param {AbortSignal} signal
|
|
168
|
+
* @returns {Promise<any>}
|
|
169
|
+
*/
|
|
170
|
+
async loadTile(xT, yT, signal) {
|
|
168
171
|
try {
|
|
169
|
-
const data = await csv(`${this.url}${xT}/${yT}.csv
|
|
172
|
+
const data = await csv(`${this.url}${xT}/${yT}.csv`, { signal })
|
|
170
173
|
|
|
171
|
-
// Preprocess or filter the data if needed
|
|
172
174
|
const cells = this.preprocess ? data.filter((cell) => this.preprocess(cell) !== false) : data
|
|
173
175
|
|
|
174
176
|
if (!this.info) throw new Error('Tile info unknown')
|
|
175
177
|
|
|
176
178
|
return getGridTile(cells, xT, yT, this.info)
|
|
177
179
|
} catch (error) {
|
|
178
|
-
|
|
180
|
+
if (error.name === 'AbortError') {
|
|
181
|
+
console.warn(`Tile request for ${xT}, ${yT} was aborted.`)
|
|
182
|
+
}
|
|
179
183
|
throw error
|
|
180
184
|
}
|
|
181
185
|
}
|
|
182
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Cancel ongoing data requests when zoom level changes.
|
|
189
|
+
*/
|
|
190
|
+
cancelCurrentRequests() {
|
|
191
|
+
if (this.abortController) {
|
|
192
|
+
this.abortController.abort()
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
183
196
|
checkAndRedraw(tile, isLastTile) {
|
|
184
197
|
// Check if any visible layer depends on this dataset
|
|
185
|
-
//check if redraw is really needed, that is if:
|
|
198
|
+
// check if redraw is really needed, that is if:
|
|
186
199
|
// 1. the dataset belongs to a layer which is visible at the current zoom level
|
|
187
200
|
let needsRedraw = false
|
|
188
201
|
//go through the layers
|
package/src/utils/ternary.js
CHANGED
|
@@ -15,7 +15,7 @@ export const ternaryClassifier = (properties, totalFunction, opts = {}) => {
|
|
|
15
15
|
//parameter to decide wether to use mixed classes m0, m1, m2.
|
|
16
16
|
const withMixedClasses = opts.withMixedClasses != undefined ? opts.withMixedClasses : true
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
//parameter to decide wether to use a central class, and the size of this central class.
|
|
19
19
|
//set to 0 or undefined for not showing any central class. Set to 1 for a central class that contains the mix classes
|
|
20
20
|
const cc = opts.centerCoefficient ? 1 - opts.centerCoefficient : undefined
|
|
21
21
|
|
|
@@ -130,6 +130,11 @@ export const ternaryColorClassifier = (properties, totalFunction, colors, opts =
|
|
|
130
130
|
return fun
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// OLD VERSIONS
|
|
137
|
+
|
|
133
138
|
/*
|
|
134
139
|
const orderedIndexesDec = arr => [...arr.keys()].sort((a, b) => arr[b] - arr[a]);
|
|
135
140
|
const orderedIndexesInc = arr => [...arr.keys()].sort((a, b) => arr[a] - arr[b]);
|