gridviz 3.0.35 → 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.
Files changed (67) hide show
  1. package/README.md +129 -129
  2. package/dist/gridviz.js +17486 -17486
  3. package/dist/gridviz.min.js +2 -2
  4. package/package.json +73 -73
  5. package/src/button/Button.js +67 -67
  6. package/src/button/FullscreenButton.js +121 -121
  7. package/src/button/ZoomButtons.js +103 -103
  8. package/src/core/Dataset.js +154 -154
  9. package/src/core/Drawable.js +78 -78
  10. package/src/core/GeoCanvas.js +443 -443
  11. package/src/core/Layer.js +23 -23
  12. package/src/core/Legend.js +108 -108
  13. package/src/core/Map.js +568 -568
  14. package/src/core/MultiResolutionDataset.js +93 -93
  15. package/src/core/Style.js +98 -98
  16. package/src/core/Tooltip.js +182 -182
  17. package/src/dataset/COGeo.js +25 -25
  18. package/src/dataset/CSVGrid.js +112 -112
  19. package/src/dataset/GeoTIFF.js +122 -122
  20. package/src/dataset/JSGrid.js +55 -55
  21. package/src/dataset/TiledGrid.js +302 -302
  22. package/src/dataset/worker.js +18 -18
  23. package/src/index.js +79 -79
  24. package/src/layer/BackgroundLayer.js +173 -173
  25. package/src/layer/BackgroundLayerImage.js +81 -81
  26. package/src/layer/BackgroundLayerWMS.js +104 -104
  27. package/src/layer/GeoJSONLayer.js +276 -276
  28. package/src/layer/GridLayer.js +166 -166
  29. package/src/layer/LabelLayer.js +194 -194
  30. package/src/legend/ColorCategoryLegend.js +109 -109
  31. package/src/legend/ColorDiscreteLegend.js +100 -100
  32. package/src/legend/ColorLegend.js +115 -115
  33. package/src/legend/OrientationLegend.js +89 -89
  34. package/src/legend/SizeLegend.js +224 -224
  35. package/src/legend/TernaryLegend.js +302 -302
  36. package/src/style/CompositionStyle.js +291 -291
  37. package/src/style/DotDensityStyle.js +133 -133
  38. package/src/style/ImageStyle.js +100 -100
  39. package/src/style/Interpolator.js +258 -258
  40. package/src/style/IsoFenceStyle.js +217 -217
  41. package/src/style/JoyPlotStyle.js +180 -180
  42. package/src/style/LegoStyle.js +132 -132
  43. package/src/style/MosaicStyle.js +99 -99
  44. package/src/style/NinjaStarStyle.js +102 -102
  45. package/src/style/PillarStyle.js +207 -207
  46. package/src/style/SegmentStyle.js +105 -105
  47. package/src/style/ShadingRayStyle.js +280 -280
  48. package/src/style/ShadingStyle.js +72 -72
  49. package/src/style/ShapeColorSizeStyle.js +147 -147
  50. package/src/style/SideCategoryStyle.js +112 -112
  51. package/src/style/SideStyle.js +217 -217
  52. package/src/style/SideTanakaStyle.js +154 -154
  53. package/src/style/SquareColorCategoryWebGLStyle.js +307 -294
  54. package/src/style/SquareColorWebGLStyle.js +126 -126
  55. package/src/style/StrokeStyle.js +135 -135
  56. package/src/style/TextStyle.js +110 -110
  57. package/src/style/TimeSeriesStyle.js +222 -222
  58. package/src/utils/WebGLRectangleColoring.js +106 -106
  59. package/src/utils/WebGLSquareColoring.js +118 -118
  60. package/src/utils/WebGLSquareColoringAdvanced.js +227 -227
  61. package/src/utils/WebGLSquareColoringKS.js +153 -153
  62. package/src/utils/scale.js +255 -255
  63. package/src/utils/stretching.js +120 -120
  64. package/src/utils/ternary.js +208 -208
  65. package/src/utils/utils.js +125 -125
  66. package/src/utils/webGLUtils.js +73 -73
  67. package/src/utils/zoomUtils.js +142 -142
@@ -1,280 +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
-
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
+