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.
- package/README.md +129 -129
- package/dist/gridviz.js +17486 -17486
- 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 -568
- 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
|
@@ -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
|
+
|