gridviz 2.0.27 → 2.0.29
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/dist/gridviz.js +34315 -0
- package/package.json +4 -2
- package/src/App.js +651 -0
- package/src/BackgroundLayer.js +182 -0
- package/src/BackgroundLayerWMS.js +131 -0
- package/src/Dataset.js +76 -0
- package/src/DatasetComponent.js +103 -0
- package/src/GeoCanvas.js +419 -0
- package/src/LabelLayer.js +190 -0
- package/src/Layer.js +92 -0
- package/src/Legend.js +86 -0
- package/src/LineLayer.js +150 -0
- package/src/Style.js +114 -0
- package/src/Tooltip.js +150 -0
- package/src/dataset/COGeo.js +24 -0
- package/src/dataset/CSVGrid.js +105 -0
- package/src/dataset/GeoTIFF.js +127 -0
- package/src/dataset/GridTile.js +41 -0
- package/src/dataset/ParquetGrid.js +123 -0
- package/src/dataset/TiledGrid.js +294 -0
- package/src/index.js +176 -0
- package/src/legend/ColorCategoryLegend.js +124 -0
- package/src/legend/ColorDiscreteLegend.js +105 -0
- package/src/legend/ColorLegend.js +125 -0
- package/src/legend/SegmentOrientationLegend.js +87 -0
- package/src/legend/SegmentWidthLegend.js +97 -0
- package/src/legend/SizeLegend.js +141 -0
- package/src/style/CompositionStyle.js +294 -0
- package/src/style/ContourStyle.js +54 -0
- package/src/style/DotDensityStyle.js +156 -0
- package/src/style/JoyPlotStyle.js +135 -0
- package/src/style/KernelSmoothingOldStyle.js +221 -0
- package/src/style/KernelSmoothingStyle.js +115 -0
- package/src/style/KernelSmoothingWGLStyle.js +130 -0
- package/src/style/LegoStyle.js +112 -0
- package/src/style/MosaicStyle.js +107 -0
- package/src/style/NinjaStarStyle.js +124 -0
- package/src/style/PillarStyle.js +230 -0
- package/src/style/SegmentStyle.js +142 -0
- package/src/style/ShapeColorSizeStyle.js +126 -0
- package/src/style/SideCatStyle.js +172 -0
- package/src/style/SideStyle.js +218 -0
- package/src/style/SquareColorCatWGLStyle.js +120 -0
- package/src/style/SquareColorWGLStyle.js +146 -0
- package/src/style/StrokeStyle.js +131 -0
- package/src/style/TanakaStyle.js +160 -0
- package/src/style/TextStyle.js +132 -0
- package/src/utils/Utils.js +35 -0
- package/src/utils/WebGLRectangleColoring.js +106 -0
- package/src/utils/WebGLSquareColoring.js +118 -0
- package/src/utils/WebGLSquareColoringAdvanced.js +230 -0
- package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -0
- package/src/utils/WebGLSquareColoringKS.js +153 -0
- package/src/utils/stretching.js +131 -0
- package/src/utils/webGLUtils.js +71 -0
- package/src/utils/zoomUtils.js +142 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { DatasetComponent } from '../DatasetComponent'
|
|
5
|
+
//import { fromUrl } from "geotiff"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @todo
|
|
9
|
+
* A dataset composed of a single GeoTiff file.
|
|
10
|
+
*
|
|
11
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
12
|
+
*/
|
|
13
|
+
export class GeoTIFF extends DatasetComponent {
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} url The URL of the dataset.
|
|
16
|
+
* @param {number} resolution The dataset resolution in geographical unit.
|
|
17
|
+
* @param {{preprocess?:(function(import("../Dataset").Cell):boolean)}} opts
|
|
18
|
+
*/
|
|
19
|
+
constructor(url, resolution, opts = {}) {
|
|
20
|
+
super(url, resolution, opts)
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @private
|
|
24
|
+
* @type {Array.<import("../Dataset").Cell>} */
|
|
25
|
+
this.cells = []
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @type {string}
|
|
29
|
+
* @private */
|
|
30
|
+
this.infoLoadingStatus = 'notLoaded'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Request data within a geographic envelope.
|
|
35
|
+
*
|
|
36
|
+
* @param {import("../Dataset").Envelope|undefined} e
|
|
37
|
+
* @param {function():void} redraw
|
|
38
|
+
*/
|
|
39
|
+
getData(e, redraw) {
|
|
40
|
+
//check if data already loaded
|
|
41
|
+
if (this.infoLoadingStatus != 'notLoaded') return this
|
|
42
|
+
|
|
43
|
+
//load data
|
|
44
|
+
this.infoLoadingStatus = 'loading'
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
|
|
48
|
+
fromUrl(this.url)
|
|
49
|
+
.then(tiff => {
|
|
50
|
+
console.log(tiff);
|
|
51
|
+
|
|
52
|
+
(async function () {
|
|
53
|
+
const image = await tiff.getImage();
|
|
54
|
+
const width = image.getWidth();
|
|
55
|
+
const height = image.getHeight();
|
|
56
|
+
const tileWidth = image.getTileWidth();
|
|
57
|
+
const tileHeight = image.getTileHeight();
|
|
58
|
+
const samplesPerPixel = image.getSamplesPerPixel();
|
|
59
|
+
|
|
60
|
+
//for geotiff
|
|
61
|
+
const origin = image.getOrigin();
|
|
62
|
+
const resolution = image.getResolution();
|
|
63
|
+
const bbox = image.getBoundingBox();
|
|
64
|
+
|
|
65
|
+
console.log(image)
|
|
66
|
+
console.log(width, height)
|
|
67
|
+
console.log(tileWidth, tileHeight)
|
|
68
|
+
console.log(samplesPerPixel)
|
|
69
|
+
console.log(origin)
|
|
70
|
+
console.log(resolution)
|
|
71
|
+
console.log(bbox)
|
|
72
|
+
|
|
73
|
+
console.log("-------")
|
|
74
|
+
|
|
75
|
+
const data = await image.readRasters();
|
|
76
|
+
console.log(data)
|
|
77
|
+
|
|
78
|
+
})()
|
|
79
|
+
|
|
80
|
+
//convert coordinates in numbers
|
|
81
|
+
//for (const c of data) { c.x = +c.x; c.y = +c.y; }
|
|
82
|
+
|
|
83
|
+
//this.cells = data;
|
|
84
|
+
|
|
85
|
+
//execute preprocess, if any
|
|
86
|
+
//if (this.preprocess) for (const c of this.cells) this.preprocess(c);
|
|
87
|
+
|
|
88
|
+
//TODO check if redraw is necessary
|
|
89
|
+
//that is if the dataset belongs to a layer which is visible at the current zoom level
|
|
90
|
+
|
|
91
|
+
//execute the callback, usually a draw function
|
|
92
|
+
if (redraw) redraw()
|
|
93
|
+
|
|
94
|
+
this.infoLoadingStatus = "loaded";
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
})
|
|
98
|
+
.catch(() => {
|
|
99
|
+
//mark as failed
|
|
100
|
+
this.infoLoadingStatus = "failed";
|
|
101
|
+
this.cells = []
|
|
102
|
+
});
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
return this
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Fill the view cache with all cells which are within a geographical envelope.
|
|
110
|
+
*
|
|
111
|
+
* @param {import("../Dataset").Envelope} extGeo
|
|
112
|
+
* @returns {void}
|
|
113
|
+
*/
|
|
114
|
+
updateViewCache(extGeo) {
|
|
115
|
+
//data not loaded yet
|
|
116
|
+
if (!this.cells) return
|
|
117
|
+
|
|
118
|
+
this.cellsViewCache = []
|
|
119
|
+
for (const cell of this.cells) {
|
|
120
|
+
if (+cell.x + this.resolution < extGeo.xMin) continue
|
|
121
|
+
if (+cell.x - this.resolution > extGeo.xMax) continue
|
|
122
|
+
if (+cell.y + this.resolution < extGeo.yMin) continue
|
|
123
|
+
if (+cell.y - this.resolution > extGeo.yMax) continue
|
|
124
|
+
this.cellsViewCache.push(cell)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A grid tile.
|
|
6
|
+
*
|
|
7
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
8
|
+
*/
|
|
9
|
+
export class GridTile {
|
|
10
|
+
/**
|
|
11
|
+
* @param {Array.<import("../Dataset").Cell>} cells The tile cells.
|
|
12
|
+
* @param {number} xT The X position of the tile.
|
|
13
|
+
* @param {number} yT The Y position of the tile.
|
|
14
|
+
* @param {import("./CSVGrid").GridInfo} gridInfo The grid info object.
|
|
15
|
+
*/
|
|
16
|
+
constructor(cells, xT, yT, gridInfo) {
|
|
17
|
+
/** @type {Array.<import("../Dataset").Cell>} */
|
|
18
|
+
this.cells = cells
|
|
19
|
+
/** @type {number} */
|
|
20
|
+
this.x = xT
|
|
21
|
+
/** @type {number} */
|
|
22
|
+
this.y = yT
|
|
23
|
+
|
|
24
|
+
const r = gridInfo.resolutionGeo
|
|
25
|
+
const s = gridInfo.tileSizeCell
|
|
26
|
+
|
|
27
|
+
/** @type {import("../Dataset").Envelope} */
|
|
28
|
+
this.extGeo = {
|
|
29
|
+
xMin: gridInfo.originPoint.x + r * s * this.x,
|
|
30
|
+
xMax: gridInfo.originPoint.x + r * s * (this.x + 1),
|
|
31
|
+
yMin: gridInfo.originPoint.y + r * s * this.y,
|
|
32
|
+
yMax: gridInfo.originPoint.y + r * s * (this.y + 1),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//convert cell coordinates into geographical coordinates
|
|
36
|
+
for (let cell of this.cells) {
|
|
37
|
+
cell.x = this.extGeo.xMin + cell.x * r
|
|
38
|
+
cell.y = this.extGeo.yMin + cell.y * r
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { DatasetComponent } from '../DatasetComponent'
|
|
5
|
+
import { tableFromIPC } from 'apache-arrow'
|
|
6
|
+
//import { readParquet } from "parquet-wasm"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A dataset composed of a single parquet file (not tiled).
|
|
10
|
+
*
|
|
11
|
+
* @author Julien Gaffuri
|
|
12
|
+
*/
|
|
13
|
+
export class ParquetGrid extends DatasetComponent {
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} url The URL of the dataset.
|
|
16
|
+
* @param {number} resolution The dataset resolution in geographical unit.
|
|
17
|
+
* @param {{preprocess?:(function(import("../Dataset").Cell):boolean), readParquetFun?:Function}} opts
|
|
18
|
+
*/
|
|
19
|
+
constructor(url, resolution, opts = {}) {
|
|
20
|
+
super(url, resolution, opts)
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @private
|
|
24
|
+
* @type {Array.<import("../Dataset").Cell>} */
|
|
25
|
+
this.cells = []
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @type {string}
|
|
29
|
+
* @private */
|
|
30
|
+
this.infoLoadingStatus = 'notLoaded'
|
|
31
|
+
|
|
32
|
+
if (!opts.readParquetFun) throw new Error('readParquet function needed for parquet dataset')
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @type {Function}
|
|
36
|
+
* @private */
|
|
37
|
+
this.readParquetFun = opts.readParquetFun
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Request data within a geographic envelope.
|
|
42
|
+
*
|
|
43
|
+
* @param {import("../Dataset").Envelope|undefined} e
|
|
44
|
+
* @param {function():void} redraw
|
|
45
|
+
*/
|
|
46
|
+
getData(e, redraw) {
|
|
47
|
+
//check if data already loaded
|
|
48
|
+
if (this.infoLoadingStatus != 'notLoaded') return this
|
|
49
|
+
|
|
50
|
+
//load data
|
|
51
|
+
this.infoLoadingStatus = 'loading'
|
|
52
|
+
|
|
53
|
+
;(async () => {
|
|
54
|
+
try {
|
|
55
|
+
const resp = await fetch(this.url)
|
|
56
|
+
const parquetUint8Array = new Uint8Array(await resp.arrayBuffer())
|
|
57
|
+
const arrowUint8Array = this.readParquetFun(parquetUint8Array)
|
|
58
|
+
|
|
59
|
+
const t = tableFromIPC(arrowUint8Array)
|
|
60
|
+
//see https://arrow.apache.org/docs/js/
|
|
61
|
+
//https://loaders.gl/arrowjs/docs/developer-guide/tables#record-tojson-and-toarray
|
|
62
|
+
|
|
63
|
+
this.cells = []
|
|
64
|
+
for (const e of t) {
|
|
65
|
+
//get cell
|
|
66
|
+
const c = e.toJSON()
|
|
67
|
+
|
|
68
|
+
//preprocess/filter
|
|
69
|
+
if (this.preprocess) {
|
|
70
|
+
const b = this.preprocess(c)
|
|
71
|
+
if (b == false) continue
|
|
72
|
+
this.cells.push(c)
|
|
73
|
+
} else {
|
|
74
|
+
this.cells.push(c)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//TODO check if redraw is necessary
|
|
79
|
+
//that is if the dataset belongs to a layer which is visible at the current zoom level
|
|
80
|
+
|
|
81
|
+
//execute the callback, usually a draw function
|
|
82
|
+
if (redraw) redraw()
|
|
83
|
+
|
|
84
|
+
this.infoLoadingStatus = 'loaded'
|
|
85
|
+
} catch (error) {
|
|
86
|
+
//mark as failed
|
|
87
|
+
this.infoLoadingStatus = 'failed'
|
|
88
|
+
this.cells = []
|
|
89
|
+
}
|
|
90
|
+
})()
|
|
91
|
+
|
|
92
|
+
return this
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Fill the view cache with all cells which are within a geographical envelope.
|
|
97
|
+
*
|
|
98
|
+
* @param {import("../Dataset").Envelope} extGeo
|
|
99
|
+
* @returns {void}
|
|
100
|
+
*/
|
|
101
|
+
updateViewCache(extGeo) {
|
|
102
|
+
//data not loaded yet
|
|
103
|
+
if (!this.cells) return
|
|
104
|
+
|
|
105
|
+
this.cellsViewCache = []
|
|
106
|
+
for (const cell of this.cells) {
|
|
107
|
+
if (+cell.x + this.resolution < extGeo.xMin) continue
|
|
108
|
+
if (+cell.x - this.resolution > extGeo.xMax) continue
|
|
109
|
+
if (+cell.y + this.resolution < extGeo.yMin) continue
|
|
110
|
+
if (+cell.y - this.resolution > extGeo.yMax) continue
|
|
111
|
+
this.cellsViewCache.push(cell)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Retrieve parquet wasm decoder
|
|
118
|
+
*/
|
|
119
|
+
const getReadParquet = async () => {
|
|
120
|
+
//const parquetModule = await import("https://unpkg.com/parquet-wasm@0.4.0-beta.5/esm/arrow2.js");
|
|
121
|
+
//await parquetModule.default();
|
|
122
|
+
//return parquetModule.readParquet;
|
|
123
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
/** @typedef {{ dims: object, crs: string, tileSizeCell: number, originPoint: {x:number,y:number}, resolutionGeo: number, tilingBounds:import("../Dataset").Envelope, format:import("../DatasetComponent").Format }} GridInfo */
|
|
5
|
+
|
|
6
|
+
import { json, csv } from 'd3-fetch'
|
|
7
|
+
import { GridTile } from './GridTile'
|
|
8
|
+
import { App } from '../App'
|
|
9
|
+
import { DatasetComponent } from '../DatasetComponent'
|
|
10
|
+
import { monitor, monitorDuration } from '../utils/Utils'
|
|
11
|
+
import { tableFromIPC } from 'apache-arrow'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A tiled dataset, composed of CSV (or parquet) tiles.
|
|
15
|
+
*
|
|
16
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
17
|
+
*/
|
|
18
|
+
export class TiledGrid extends DatasetComponent {
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} url The URL of the dataset.
|
|
21
|
+
* @param {App} app The application.
|
|
22
|
+
* @param {{preprocess?:(function(import("../Dataset").Cell):boolean), readParquetFun?:Function }} opts
|
|
23
|
+
*/
|
|
24
|
+
constructor(url, app, opts = {}) {
|
|
25
|
+
super(url, 0, opts)
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The app being used.
|
|
29
|
+
* @type {App}
|
|
30
|
+
*/
|
|
31
|
+
this.app = app
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The grid info object, from the info.json file.
|
|
35
|
+
* @type {GridInfo | undefined}
|
|
36
|
+
* @private
|
|
37
|
+
* */
|
|
38
|
+
this.info = undefined
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @type {string}
|
|
42
|
+
* @private */
|
|
43
|
+
this.infoLoadingStatus = 'notLoaded'
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The cache of the loaded tiles. It is double indexed: by xT and then yT.
|
|
47
|
+
* Example: this.cache[xT][yT] returns the tile at [xT][yT] location.
|
|
48
|
+
*
|
|
49
|
+
* @type {object}
|
|
50
|
+
* */
|
|
51
|
+
this.cache = {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @type {Function|undefined}
|
|
55
|
+
* @private */
|
|
56
|
+
this.readParquetFun = opts.readParquetFun
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Load the info.json from the url.
|
|
61
|
+
*
|
|
62
|
+
* @param {function():void} callback
|
|
63
|
+
* @returns this
|
|
64
|
+
*/
|
|
65
|
+
loadInfo(callback) {
|
|
66
|
+
if (!this.info && this.infoLoadingStatus === 'notLoaded') {
|
|
67
|
+
;(async () => {
|
|
68
|
+
try {
|
|
69
|
+
const data = await json(this.url + 'info.json')
|
|
70
|
+
this.info = data
|
|
71
|
+
this.resolution = data.resolutionGeo
|
|
72
|
+
this.infoLoadingStatus = 'loaded'
|
|
73
|
+
if (callback) callback()
|
|
74
|
+
} catch (error) {
|
|
75
|
+
//mark as failed
|
|
76
|
+
this.infoLoadingStatus = 'failed'
|
|
77
|
+
}
|
|
78
|
+
})()
|
|
79
|
+
} else if (callback && (this.infoLoadingStatus === 'loaded' || this.infoLoadingStatus === 'failed'))
|
|
80
|
+
callback()
|
|
81
|
+
return this
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Compute a tiling envelope from a geographical envelope.
|
|
86
|
+
* This is the function to use to know which tiles to download for a geographical view.
|
|
87
|
+
*
|
|
88
|
+
* @param {import("../Dataset").Envelope} e
|
|
89
|
+
* @returns {import("../Dataset").Envelope|undefined}
|
|
90
|
+
*/
|
|
91
|
+
getTilingEnvelope(e) {
|
|
92
|
+
if (!this.info) {
|
|
93
|
+
this.loadInfo(() => {})
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const po = this.info.originPoint,
|
|
98
|
+
r = this.info.resolutionGeo,
|
|
99
|
+
s = this.info.tileSizeCell
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
xMin: Math.floor((e.xMin - po.x) / (r * s)),
|
|
103
|
+
xMax: Math.floor((e.xMax - po.x) / (r * s)),
|
|
104
|
+
yMin: Math.floor((e.yMin - po.y) / (r * s)),
|
|
105
|
+
yMax: Math.floor((e.yMax - po.y) / (r * s)),
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Request data within a geographic envelope.
|
|
111
|
+
*
|
|
112
|
+
* @param {import("../Dataset").Envelope} extGeo
|
|
113
|
+
* @param {function():void} redrawFun
|
|
114
|
+
* @returns {this}
|
|
115
|
+
*/
|
|
116
|
+
getData(extGeo, redrawFun) {
|
|
117
|
+
//TODO empty cache when it gets too big ?
|
|
118
|
+
|
|
119
|
+
//check if info has been loaded
|
|
120
|
+
if (!this.info) return this
|
|
121
|
+
|
|
122
|
+
//tiles within the scope
|
|
123
|
+
/** @type {import("../Dataset").Envelope|undefined} */
|
|
124
|
+
const tb = this.getTilingEnvelope(extGeo)
|
|
125
|
+
if (!tb) return this
|
|
126
|
+
|
|
127
|
+
//grid bounds
|
|
128
|
+
/** @type {import("../Dataset").Envelope} */
|
|
129
|
+
const gb = this.info.tilingBounds
|
|
130
|
+
|
|
131
|
+
const format = this.info.format
|
|
132
|
+
|
|
133
|
+
for (let xT = Math.max(tb.xMin, gb.xMin); xT <= Math.min(tb.xMax, gb.xMax); xT++) {
|
|
134
|
+
for (let yT = Math.max(tb.yMin, gb.yMin); yT <= Math.min(tb.yMax, gb.yMax); yT++) {
|
|
135
|
+
//prepare cache
|
|
136
|
+
if (!this.cache[xT]) this.cache[xT] = {}
|
|
137
|
+
|
|
138
|
+
//check if tile exists in the cache
|
|
139
|
+
/** @type {GridTile} */
|
|
140
|
+
let tile = this.cache[xT][yT]
|
|
141
|
+
if (tile) continue
|
|
142
|
+
|
|
143
|
+
//mark tile as loading
|
|
144
|
+
this.cache[xT][yT] = 'loading'
|
|
145
|
+
|
|
146
|
+
;(async () => {
|
|
147
|
+
//request tile
|
|
148
|
+
/** @type {Array.<import("../Dataset").Cell>} */
|
|
149
|
+
let cells
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
if (!format || format === 'CSV') {
|
|
153
|
+
/** @type {Array.<import("../Dataset").Cell>} */
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
const data = await csv(this.url + xT + '/' + yT + '.csv')
|
|
156
|
+
|
|
157
|
+
if (monitor) monitorDuration('*** TiledGrid parse start')
|
|
158
|
+
|
|
159
|
+
//preprocess/filter
|
|
160
|
+
if (this.preprocess) {
|
|
161
|
+
cells = []
|
|
162
|
+
for (const c of data) {
|
|
163
|
+
const b = this.preprocess(c)
|
|
164
|
+
if (b == false) continue
|
|
165
|
+
cells.push(c)
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
cells = data
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (monitor) monitorDuration('preprocess / filter')
|
|
172
|
+
} else if (format === 'PARQUET') {
|
|
173
|
+
if (!this.readParquetFun)
|
|
174
|
+
throw new Error('readParquet function needed for parquet dataset')
|
|
175
|
+
|
|
176
|
+
const resp = await fetch(this.url + xT + '/' + yT + '.parquet')
|
|
177
|
+
const parquetUint8Array = new Uint8Array(await resp.arrayBuffer())
|
|
178
|
+
const arrowUint8Array = this.readParquetFun(parquetUint8Array)
|
|
179
|
+
const t = tableFromIPC(arrowUint8Array)
|
|
180
|
+
|
|
181
|
+
cells = []
|
|
182
|
+
for (const e of t) {
|
|
183
|
+
//get cell
|
|
184
|
+
const c = e.toJSON()
|
|
185
|
+
|
|
186
|
+
//preprocess/filter
|
|
187
|
+
if (this.preprocess) {
|
|
188
|
+
const b = this.preprocess(c)
|
|
189
|
+
if (b == false) continue
|
|
190
|
+
cells.push(c)
|
|
191
|
+
} else {
|
|
192
|
+
cells.push(c)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} else throw new Error('Tiled format not supported: ' + format)
|
|
196
|
+
} catch (error) {
|
|
197
|
+
//mark as failed
|
|
198
|
+
this.cache[xT][yT] = 'failed'
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//store tile in cache
|
|
203
|
+
if (!this.info) {
|
|
204
|
+
console.error('Tile info inknown')
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
const tile_ = new GridTile(cells, xT, yT, this.info)
|
|
208
|
+
this.cache[xT][yT] = tile_
|
|
209
|
+
|
|
210
|
+
if (monitor) monitorDuration('storage')
|
|
211
|
+
|
|
212
|
+
//if no redraw is specified, then leave
|
|
213
|
+
if (!redrawFun) return
|
|
214
|
+
|
|
215
|
+
//check if redraw is really needed, that is if:
|
|
216
|
+
|
|
217
|
+
// 1. the dataset belongs to a layer which is visible at the current zoom level
|
|
218
|
+
let redraw = false
|
|
219
|
+
//go through the layers
|
|
220
|
+
const zf = this.app.getZoomFactor()
|
|
221
|
+
for (const lay of this.app.layers) {
|
|
222
|
+
if (!lay.visible) continue
|
|
223
|
+
if (lay.getDatasetComponent(zf) != this) continue
|
|
224
|
+
//found one layer. No need to seek more.
|
|
225
|
+
redraw = true
|
|
226
|
+
break
|
|
227
|
+
}
|
|
228
|
+
if (monitor) monitorDuration('check redraw 1')
|
|
229
|
+
|
|
230
|
+
if (!redraw) return
|
|
231
|
+
|
|
232
|
+
// 2. the tile is within the view, that is its geo envelope intersects the viewer geo envelope.
|
|
233
|
+
const env = this.app.updateExtentGeo()
|
|
234
|
+
const envT = tile_.extGeo
|
|
235
|
+
if (env.xMax <= envT.xMin) return
|
|
236
|
+
if (env.xMin >= envT.xMax) return
|
|
237
|
+
if (env.yMax <= envT.yMin) return
|
|
238
|
+
if (env.yMin >= envT.yMax) return
|
|
239
|
+
|
|
240
|
+
if (monitor) monitorDuration('check redraw 2')
|
|
241
|
+
if (monitor) monitorDuration('*** TiledGrid parse end')
|
|
242
|
+
|
|
243
|
+
//redraw
|
|
244
|
+
redrawFun()
|
|
245
|
+
})()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return this
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Fill the view cache with all cells which are within a geographical envelope.
|
|
253
|
+
* @abstract
|
|
254
|
+
* @param {import("../Dataset").Envelope} extGeo
|
|
255
|
+
* @returns {void}
|
|
256
|
+
*/
|
|
257
|
+
updateViewCache(extGeo) {
|
|
258
|
+
//
|
|
259
|
+
this.cellsViewCache = []
|
|
260
|
+
|
|
261
|
+
//check if info has been loaded
|
|
262
|
+
if (!this.info) return
|
|
263
|
+
|
|
264
|
+
//tiles within the scope
|
|
265
|
+
/** @type {import("../Dataset").Envelope|undefined} */
|
|
266
|
+
const tb = this.getTilingEnvelope(extGeo)
|
|
267
|
+
if (!tb) return
|
|
268
|
+
|
|
269
|
+
//grid bounds
|
|
270
|
+
/** @type {import("../Dataset").Envelope} */
|
|
271
|
+
const gb = this.info.tilingBounds
|
|
272
|
+
|
|
273
|
+
for (let xT = Math.max(tb.xMin, gb.xMin); xT <= Math.min(tb.xMax, gb.xMax); xT++) {
|
|
274
|
+
if (!this.cache[xT]) continue
|
|
275
|
+
for (let yT = Math.max(tb.yMin, gb.yMin); yT <= Math.min(tb.yMax, gb.yMax); yT++) {
|
|
276
|
+
//get tile
|
|
277
|
+
/** @type {GridTile} */
|
|
278
|
+
const tile = this.cache[xT][yT]
|
|
279
|
+
if (!tile || typeof tile === 'string') continue
|
|
280
|
+
|
|
281
|
+
//get cells
|
|
282
|
+
//this.cellsViewCache = this.cellsViewCache.concat(tile.cells)
|
|
283
|
+
|
|
284
|
+
for (const cell of tile.cells) {
|
|
285
|
+
if (+cell.x + this.resolution < extGeo.xMin) continue
|
|
286
|
+
if (+cell.x - this.resolution > extGeo.xMax) continue
|
|
287
|
+
if (+cell.y + this.resolution < extGeo.yMin) continue
|
|
288
|
+
if (+cell.y - this.resolution > extGeo.yMax) continue
|
|
289
|
+
this.cellsViewCache.push(cell)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|