gridviz 3.0.32 → 3.0.34
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 +1929 -1052
- package/dist/gridviz.min.js +1 -1
- package/package.json +1 -1
- package/src/core/Drawable.js +2 -2
- package/src/core/Map.js +8 -4
- package/src/index.js +3 -1
- package/src/layer/GeoJSONLayer.js +132 -80
- package/src/layer/GridLayer.js +21 -4
- package/src/legend/TernaryLegend.js +1 -1
- package/src/style/Interpolator.js +258 -0
- package/src/style/JoyPlotStyle.js +63 -10
- package/src/style/ShadingRayStyle.js +280 -0
- package/src/style/SquareColorCategoryWebGLStyle.js +2 -2
- package/src/utils/utils.js +67 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
4
|
import { Style } from '../core/Style.js'
|
|
5
|
+
import { cellsToGrid } from '../utils/utils.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @module style
|
|
@@ -47,15 +48,70 @@ export class JoyPlotStyle extends Style {
|
|
|
47
48
|
|
|
48
49
|
//index cells by y and x
|
|
49
50
|
/** @type {object} */
|
|
50
|
-
const ind =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
const ind = cellsToGrid(cells, cell => this.height(cell, resolution, z, viewScale))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
//make grid
|
|
55
|
+
/** @type {object} */
|
|
56
|
+
/*const grid = cellsToGrid2(cells, resolution, cell => this.height(cell, resolution, z, viewScale))
|
|
57
|
+
if (!grid.maxI || !grid.maxJ) return
|
|
58
|
+
|
|
59
|
+
//TODO
|
|
60
|
+
const ys = { min: grid.y0 + resolution * grid.minI, max: grid.y0 + resolution * grid.maxI }
|
|
61
|
+
|
|
62
|
+
//draw lines, row by row, stating from the top
|
|
63
|
+
ctx.lineJoin = 'round'
|
|
64
|
+
for (let i = grid.minI; i <= grid.maxI; i++) {
|
|
65
|
+
|
|
66
|
+
//Get row data
|
|
67
|
+
const row = grid[i]
|
|
68
|
+
if (!row) continue
|
|
69
|
+
|
|
70
|
+
// y of the row
|
|
71
|
+
const y = grid.y0 - i * resolution
|
|
72
|
+
|
|
73
|
+
//store the previous height
|
|
74
|
+
let hG_
|
|
75
|
+
|
|
76
|
+
for (let j = grid.minJ; j <= grid.maxJ; j++) {
|
|
77
|
+
//get column value
|
|
78
|
+
let hG = row[j]
|
|
79
|
+
if (!hG) hG = 0
|
|
80
|
+
|
|
81
|
+
// x of the cell
|
|
82
|
+
const x = grid.x0 + j * resolution
|
|
83
|
+
|
|
84
|
+
if (hG || hG_) {
|
|
85
|
+
//draw line only when at least one of both values is non-null
|
|
86
|
+
//TODO test bezierCurveTo
|
|
87
|
+
ctx.lineTo(x + resolution / 2, y + hG)
|
|
88
|
+
} else {
|
|
89
|
+
//else move the point
|
|
90
|
+
ctx.moveTo(x + resolution / 2, y)
|
|
91
|
+
}
|
|
92
|
+
//store the previous value
|
|
93
|
+
hG_ = hG
|
|
94
|
+
}
|
|
95
|
+
//last point
|
|
96
|
+
if (hG_) ctx.lineTo(grid.x0 + resolution * grid.maxJ + resolution / 2, y)
|
|
97
|
+
|
|
98
|
+
//draw fill
|
|
99
|
+
const fc = this.fillColor(y, ys, resolution, z)
|
|
100
|
+
if (fc && fc != 'none') {
|
|
101
|
+
ctx.fillStyle = fc
|
|
102
|
+
ctx.fill()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//draw line
|
|
106
|
+
const lc = this.lineColor(y, ys, resolution, z)
|
|
107
|
+
const lw = this.lineWidth(y, ys, resolution, z)
|
|
108
|
+
if (lc && lc != 'none' && lw > 0) {
|
|
109
|
+
ctx.strokeStyle = lc
|
|
110
|
+
ctx.lineWidth = lw
|
|
111
|
+
ctx.stroke()
|
|
56
112
|
}
|
|
57
|
-
row[cell.x] = this.height(cell, resolution, z, viewScale)
|
|
58
113
|
}
|
|
114
|
+
*/
|
|
59
115
|
|
|
60
116
|
//compute extent
|
|
61
117
|
const e = geoCanvas.extGeo
|
|
@@ -65,7 +121,6 @@ export class JoyPlotStyle extends Style {
|
|
|
65
121
|
const yMin = Math.floor(e.yMin / resolution) * resolution
|
|
66
122
|
const yMax = Math.floor(e.yMax / resolution) * resolution
|
|
67
123
|
|
|
68
|
-
/** @type {{min:number, max:number}} */
|
|
69
124
|
const ys = { min: yMin, max: yMax }
|
|
70
125
|
|
|
71
126
|
//draw lines, row by row, stating from the top
|
|
@@ -82,13 +137,11 @@ export class JoyPlotStyle extends Style {
|
|
|
82
137
|
ctx.moveTo(xMin - resolution / 2, y)
|
|
83
138
|
|
|
84
139
|
//store the previous height
|
|
85
|
-
/** @type {number|undefined} */
|
|
86
140
|
let hG_
|
|
87
141
|
|
|
88
142
|
//go through the line cells
|
|
89
143
|
for (let x = xMin; x <= xMax; x += resolution) {
|
|
90
144
|
//get column value
|
|
91
|
-
/** @type {number} */
|
|
92
145
|
let hG = row[x]
|
|
93
146
|
if (!hG) hG = 0
|
|
94
147
|
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Style } from '../core/Style.js'
|
|
5
|
+
import { cellsToGrid, cellsToMatrix } from '../utils/utils.js'
|
|
6
|
+
import { SquareColorCategoryWebGLStyle } from './SquareColorCategoryWebGLStyle.js'
|
|
7
|
+
//import { SquareColorWebGLStyle } from './SquareColorWebGLStyle.js'
|
|
8
|
+
import { extent } from 'd3-array'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
//TODO:
|
|
12
|
+
// make faster algo by setting shades by sun ray
|
|
13
|
+
// diffusion effect ? based on distance to light (altitude - sun ray height)
|
|
14
|
+
// style with SquareColorWebGLStyle, improved version !
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @module style
|
|
18
|
+
* @author Julien Gaffuri
|
|
19
|
+
*/
|
|
20
|
+
export class ShadingRayStyle extends Style {
|
|
21
|
+
/** @param {object} opts */
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
super(opts)
|
|
24
|
+
opts = opts || {}
|
|
25
|
+
|
|
26
|
+
this.elevation = opts.elevation //(c) => elevation
|
|
27
|
+
|
|
28
|
+
this.sunAzimuth = opts.sunAzimuth || (() => 2.356) //(r,z,vs)=>
|
|
29
|
+
this.sunAltitude = opts.sunAltitude || (() => 0.2) //(r,z,vs)=>
|
|
30
|
+
|
|
31
|
+
//this.alpha = opts.alpha || (() => 0.33) //(z,vs)
|
|
32
|
+
this.color = opts.color || (() => 'black') //(r,z,vs) => {}
|
|
33
|
+
|
|
34
|
+
this.undefinedValue = opts.undefinedValue || 0
|
|
35
|
+
|
|
36
|
+
//this.version = opts.version || 1
|
|
37
|
+
// used only for v2
|
|
38
|
+
//this.shadowProperty = opts.shadowProperty || "shadow"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param {Array.<import("../core/Dataset.js").Cell>} cells
|
|
44
|
+
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
|
|
45
|
+
* @param {number} resolution
|
|
46
|
+
*/
|
|
47
|
+
draw(cells, geoCanvas, resolution) {
|
|
48
|
+
//filter
|
|
49
|
+
if (this.filter) cells = cells.filter(this.filter)
|
|
50
|
+
if (cells.length == 0) return;
|
|
51
|
+
|
|
52
|
+
//
|
|
53
|
+
const z = geoCanvas.view.z
|
|
54
|
+
|
|
55
|
+
//get view scale
|
|
56
|
+
const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
|
|
57
|
+
|
|
58
|
+
//if (this.version == "1") {
|
|
59
|
+
//index cells by y and x
|
|
60
|
+
let m = cellsToMatrix(cells, resolution, c => this.elevation(c))
|
|
61
|
+
const x0 = m.x0, y0 = m.y0
|
|
62
|
+
|
|
63
|
+
// compute ray casting shadow
|
|
64
|
+
m = referenceShadow(
|
|
65
|
+
m,
|
|
66
|
+
resolution,
|
|
67
|
+
this.sunAzimuth(resolution, z, viewScale),
|
|
68
|
+
this.sunAltitude(resolution, z, viewScale),
|
|
69
|
+
this.undefinedValue);
|
|
70
|
+
|
|
71
|
+
// make cells
|
|
72
|
+
cells = []
|
|
73
|
+
for (let i = 0; i < m.length; i++) {
|
|
74
|
+
const row = m[i]
|
|
75
|
+
const y = y0 - i * resolution
|
|
76
|
+
for (let j = 0; j < row.length; j++) {
|
|
77
|
+
const sh = row[j]
|
|
78
|
+
if (!sh) continue
|
|
79
|
+
const c = { x: x0 + j * resolution, y: y }
|
|
80
|
+
//c[this.shadowProperty] = sh
|
|
81
|
+
cells.push(c)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//} else {
|
|
85
|
+
//clean previsouly computed shadows
|
|
86
|
+
/*for (let c of cells) delete c[this.shadowProperty]
|
|
87
|
+
|
|
88
|
+
//compute shading
|
|
89
|
+
referenceShadowV2(
|
|
90
|
+
cells,
|
|
91
|
+
resolution,
|
|
92
|
+
(c) => this.elevation(c),
|
|
93
|
+
this.shadowProperty,
|
|
94
|
+
this.sunAzimuth(resolution, z, viewScale),
|
|
95
|
+
this.sunAltitude(resolution, z, viewScale));
|
|
96
|
+
|
|
97
|
+
//keep only those with shadow
|
|
98
|
+
cells = cells.filter(c => c.shadow)*/
|
|
99
|
+
//}
|
|
100
|
+
|
|
101
|
+
//set style alpha and blend mode
|
|
102
|
+
//TODO: multiply by layer alpha ?
|
|
103
|
+
//geoCanvas.ctx.globalAlpha = s.alpha ? s.alpha(z) : 1.0
|
|
104
|
+
//if (s.blendOperation) geoCanvas.ctx.globalCompositeOperation = s.blendOperation(z)
|
|
105
|
+
|
|
106
|
+
//set affin transform to draw with geographical coordinates
|
|
107
|
+
//geoCanvas.setCanvasTransform()
|
|
108
|
+
|
|
109
|
+
//draw shadowed cells with webgl style
|
|
110
|
+
new SquareColorCategoryWebGLStyle({
|
|
111
|
+
code: () => "a",
|
|
112
|
+
color: { 'a': this.color(resolution, z, viewScale) },
|
|
113
|
+
//alpha: () => this.alpha(resolution, z, viewScale),
|
|
114
|
+
}).draw(cells, geoCanvas, resolution)
|
|
115
|
+
|
|
116
|
+
/*new SquareColorWebGLStyle({
|
|
117
|
+
filter: (c => c.shadow),
|
|
118
|
+
tFun: (c, r) => c.shadow, //1-Math.min(1, c.shadow / 10000),//c.shadow, //,
|
|
119
|
+
color: (t => "rgba(0,0,0," + t + ")"),
|
|
120
|
+
alpha: () => this.alpha(resolution, z, viewScale),
|
|
121
|
+
}).draw(cells, geoCanvas, resolution)*/
|
|
122
|
+
|
|
123
|
+
//draw style filter
|
|
124
|
+
//if (s.filterColor) s.drawFilter(geoCanvas)
|
|
125
|
+
|
|
126
|
+
//update legends
|
|
127
|
+
this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Ground-truth terrain shadow algorithm (ray-based)
|
|
136
|
+
* Trig-free with undefined handling
|
|
137
|
+
*
|
|
138
|
+
* @param {number[][]} elevation - DEM [row][col], may contain undefined
|
|
139
|
+
* @param {number} resolution
|
|
140
|
+
* @param {number} sunAzimuth - radians, clockwise from north (+Y)
|
|
141
|
+
* @param {number} sunAltitude - radians - solar elevation angle above the local horizontal plane
|
|
142
|
+
* @param {number|undefined} undefinedValue - The value to set for cells with no elevation value
|
|
143
|
+
* @returns {(number|undefined)[][]} shade. Height above ground where ray light can be reached.
|
|
144
|
+
*/
|
|
145
|
+
function referenceShadow(
|
|
146
|
+
elevation,
|
|
147
|
+
resolution = 1000,
|
|
148
|
+
sunAzimuth = 2.356, // 2PI/3
|
|
149
|
+
sunAltitude = 0.15,
|
|
150
|
+
undefinedValue = undefined,
|
|
151
|
+
) {
|
|
152
|
+
const rows = elevation.length;
|
|
153
|
+
const cols = elevation[0].length;
|
|
154
|
+
|
|
155
|
+
// Sun direction TOWARD the sun
|
|
156
|
+
const ux = Math.sin(sunAzimuth);
|
|
157
|
+
const uy = -Math.cos(sunAzimuth);
|
|
158
|
+
const tanAlt = Math.tan(sunAltitude);
|
|
159
|
+
|
|
160
|
+
//const diffAngle = 40 * Math.PI / 180 // 0.5 deg
|
|
161
|
+
//const tanTop = Math.tan(sunAltitude + diffAngle)
|
|
162
|
+
//const tanBottom = Math.tan(Math.max(sunAltitude - diffAngle, 0))
|
|
163
|
+
//const tanBottom = Math.tan(sunAltitude - diffAngle)
|
|
164
|
+
|
|
165
|
+
const shade = Array.from({ length: rows }, () => new Array(cols).fill(undefined));
|
|
166
|
+
|
|
167
|
+
for (let y0 = 0; y0 < rows; y0++) {
|
|
168
|
+
for (let x0 = 0; x0 < cols; x0++) {
|
|
169
|
+
|
|
170
|
+
const z0raw = elevation[y0][x0] || undefinedValue;
|
|
171
|
+
if (z0raw === undefined) continue;
|
|
172
|
+
|
|
173
|
+
// cast ray
|
|
174
|
+
let t = resolution;
|
|
175
|
+
while (true) {
|
|
176
|
+
const x = x0 - ux * t / resolution;
|
|
177
|
+
const y = y0 - uy * t / resolution;
|
|
178
|
+
|
|
179
|
+
const ix = Math.round(x);
|
|
180
|
+
const iy = Math.round(y);
|
|
181
|
+
|
|
182
|
+
if (ix < 0 || iy < 0 || ix >= cols || iy >= rows) break;
|
|
183
|
+
|
|
184
|
+
const zqraw = elevation[iy][ix] || undefinedValue;
|
|
185
|
+
if (zqraw === undefined) break; // transparent gap
|
|
186
|
+
|
|
187
|
+
//const deltaBottom = zqraw - z0raw - tanBottom * t;
|
|
188
|
+
//if (deltaBottom > 0) {
|
|
189
|
+
const delta = zqraw - z0raw - tanAlt * t;
|
|
190
|
+
if (delta > 0) {
|
|
191
|
+
shade[y0][x0] = 1 //Math.min(3000 / t, 1)
|
|
192
|
+
break;
|
|
193
|
+
/*}
|
|
194
|
+
//const deltaTop = zqraw - z0raw - tanTop * t;
|
|
195
|
+
//if (deltaTop > 0) {
|
|
196
|
+
// shade[y0][x0] = 0
|
|
197
|
+
// break;
|
|
198
|
+
//} else {
|
|
199
|
+
let r = 1 + delta / deltaBottom
|
|
200
|
+
if (r < 0) r = 0
|
|
201
|
+
if (r < 0 || r > 1) console.log(r)
|
|
202
|
+
shade[y0][x0] = shade[y0][x0] ? Math.max(shade[y0][x0], r) : r
|
|
203
|
+
//}*/
|
|
204
|
+
}
|
|
205
|
+
t += resolution;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return shade;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Ground-truth terrain shadow algorithm (ray-based)
|
|
217
|
+
* Trig-free with undefined handling
|
|
218
|
+
*
|
|
219
|
+
* @param {Array.<Cell>} cells - DEM [row][col], may contain undefined
|
|
220
|
+
* @param {number} resolution
|
|
221
|
+
* @param {Function} elevationFun
|
|
222
|
+
* @param {string} shadowProperty
|
|
223
|
+
* @param {number} sunAzimuth - radians, clockwise from north (+Y)
|
|
224
|
+
* @param {number} sunAltitude - radians - solar elevation angle above the local horizontal plane
|
|
225
|
+
*/
|
|
226
|
+
function referenceShadowV2(
|
|
227
|
+
cells,
|
|
228
|
+
resolution = 1000,
|
|
229
|
+
elevationFun,
|
|
230
|
+
shadowProperty = "shadow",
|
|
231
|
+
sunAzimuth = 2.356, // 2PI/3
|
|
232
|
+
sunAltitude = 0.15
|
|
233
|
+
) {
|
|
234
|
+
|
|
235
|
+
//get geo extent
|
|
236
|
+
const [minx, maxx] = extent(cells, c => c.x)
|
|
237
|
+
const [miny, maxy] = extent(cells, c => c.y)
|
|
238
|
+
|
|
239
|
+
//index cells by y and x
|
|
240
|
+
const ind = cellsToGrid(cells)
|
|
241
|
+
|
|
242
|
+
// Sun direction TOWARD the sun
|
|
243
|
+
const ux = Math.sin(sunAzimuth);
|
|
244
|
+
const uy = Math.cos(sunAzimuth);
|
|
245
|
+
const tanAlt = Math.tan(sunAltitude);
|
|
246
|
+
|
|
247
|
+
for (let y0 = miny; y0 <= maxy; y0 += resolution) {
|
|
248
|
+
const row = ind[y0]
|
|
249
|
+
if (!row) continue
|
|
250
|
+
for (let x0 = minx; x0 <= maxx; x0 += resolution) {
|
|
251
|
+
const cell0 = row[x0]
|
|
252
|
+
if (!cell0) continue
|
|
253
|
+
const z0raw = elevationFun(cell0);
|
|
254
|
+
if (z0raw === undefined) continue;
|
|
255
|
+
|
|
256
|
+
// ray
|
|
257
|
+
let t = resolution;
|
|
258
|
+
while (true) {
|
|
259
|
+
const y = y0 - Math.round(uy * t / resolution) * resolution;
|
|
260
|
+
if (!ind[y]) break; // transparent gap
|
|
261
|
+
|
|
262
|
+
const x = x0 + Math.round(ux * t / resolution) * resolution;
|
|
263
|
+
const cellq = ind[y][x];
|
|
264
|
+
if (!cellq) break; // transparent gap
|
|
265
|
+
|
|
266
|
+
const zqraw = elevationFun(cellq);
|
|
267
|
+
if (zqraw === undefined) break; // transparent gap
|
|
268
|
+
|
|
269
|
+
// the height above the ground where light ray can be reached
|
|
270
|
+
const delta = zqraw - z0raw - tanAlt * t;
|
|
271
|
+
if (delta > 0) {
|
|
272
|
+
cell0[shadowProperty] = delta
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
t += resolution;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
@@ -141,7 +141,7 @@ export class SquareColorCategoryWebGLStyle extends Style {
|
|
|
141
141
|
c = cells[i]
|
|
142
142
|
const cat = this.code(c, resolution, z, viewScale)
|
|
143
143
|
if (cat == undefined) {
|
|
144
|
-
console.log('Unexpected category: ' + cat)
|
|
144
|
+
//console.log('Unexpected category: ' + cat)
|
|
145
145
|
continue
|
|
146
146
|
}
|
|
147
147
|
const i_ = this.catToI[cat]
|
|
@@ -236,7 +236,7 @@ export class SquareColorCategoryWebGLStyle extends Style {
|
|
|
236
236
|
geoCanvas.offscreenCtx.drawImage(canvas, 0, 0)
|
|
237
237
|
|
|
238
238
|
//update legends
|
|
239
|
-
this.updateLegends({ style: this, resolution: resolution, z: z })
|
|
239
|
+
this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
package/src/utils/utils.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
+
import { min, max, extent } from 'd3-array'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Get the class id from a value and class break values
|
|
6
8
|
*
|
|
@@ -25,6 +27,71 @@ export function nice(v, multiples = [8, 6, 5, 4, 2.5, 2]) {
|
|
|
25
27
|
return v_
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
/**
|
|
31
|
+
* A grid cell.
|
|
32
|
+
* @typedef {{x: number, y: number}} Cell */
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Index cells by y and x
|
|
36
|
+
* @param {Array.<Cell>} cells
|
|
37
|
+
* @param {Function} [fun]
|
|
38
|
+
* @returns {Object}
|
|
39
|
+
*/
|
|
40
|
+
export function cellsToGrid(cells, fun) {
|
|
41
|
+
/** @type {Object} */
|
|
42
|
+
const ind = {}
|
|
43
|
+
for (const cell of cells) {
|
|
44
|
+
let row = ind[cell.y]
|
|
45
|
+
if (!row) {
|
|
46
|
+
row = {}
|
|
47
|
+
ind[cell.y] = row
|
|
48
|
+
}
|
|
49
|
+
row[cell.x] = fun ? fun(cell) : cell
|
|
50
|
+
}
|
|
51
|
+
return ind
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Cells to grid.
|
|
58
|
+
*
|
|
59
|
+
* @param {Array.<Cell>} cells
|
|
60
|
+
* @param {number} resolution
|
|
61
|
+
* @param {Function} [fun]
|
|
62
|
+
* @returns {Object}
|
|
63
|
+
* Grid[i][j] is the value for line i and column j.
|
|
64
|
+
* Numbered from top to bottom, from left to right.
|
|
65
|
+
* Properties x0 and y0 are the geo coordinates of the lower left corner of the top left cell.
|
|
66
|
+
* Properties minI, maxI, minJ, maxJ are the extent of the grid.
|
|
67
|
+
*/
|
|
68
|
+
export function cellsToMatrix(cells, resolution, fun) {
|
|
69
|
+
|
|
70
|
+
// get cells extent
|
|
71
|
+
const [minx, maxx] = extent(cells, c => c.x)
|
|
72
|
+
const [miny, maxy] = extent(cells, c => c.y)
|
|
73
|
+
|
|
74
|
+
// get grid dimension
|
|
75
|
+
const rows = Math.ceil((maxy - miny) / resolution) + 1
|
|
76
|
+
const cols = Math.ceil((maxx - minx) / resolution) + 1
|
|
77
|
+
|
|
78
|
+
// make empty grid
|
|
79
|
+
const grid = Array.from({ length: rows }, () => new Array(cols).fill(undefined));
|
|
80
|
+
grid.x0 = minx
|
|
81
|
+
grid.y0 = maxy
|
|
82
|
+
grid.resolution = resolution
|
|
83
|
+
|
|
84
|
+
for (const c of cells) {
|
|
85
|
+
const i = Math.floor((maxy - c.y) / resolution)
|
|
86
|
+
const j = Math.floor((c.x - minx) / resolution)
|
|
87
|
+
const v = fun ? fun(c) : c
|
|
88
|
+
grid[i][j] = v
|
|
89
|
+
}
|
|
90
|
+
return grid
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
28
95
|
/*
|
|
29
96
|
//no longer used
|
|
30
97
|
export function loadImage(src) {
|