@@ -54,28 +54,63 @@ def _generate_primary_rays(rays, H, W):
54
54
return 0
55
55
56
56
57
+ @nb .cuda .jit (device = True )
58
+ def _get_vertical_ang (diff_elev , distance_to_viewpoint ):
59
+ # Find the vertical angle in degrees between the vp
60
+ # and the point represented by the StatusNode
61
+
62
+ # 0 above, 180 below
63
+ if diff_elev == 0.0 :
64
+ return 90
65
+ elif diff_elev > 0 :
66
+ return math .atan (distance_to_viewpoint / diff_elev ) * 180 / math .pi
67
+
68
+ return math .atan (- diff_elev / distance_to_viewpoint ) * 180 / math .pi + 90
69
+
70
+
57
71
@nb .cuda .jit
58
- def _calc_viewshed_kernel (hits , visibility_grid , H , W ):
72
+ def _calc_viewshed_kernel (hits , visibility_grid , H , W , hmap , v , oe , te , ew_range , ns_range ):
59
73
i , j = nb .cuda .grid (2 )
60
74
if i >= 0 and i < H and j >= 0 and j < W :
61
75
dist = hits [i , j , 0 ]
62
76
# We traced the viewshed rays and now hits contains the intersection
63
77
# data. If dist > 0, then we were able to hit something along the
64
78
# length of the ray which means that the pixel we targeted is not
65
79
# directly visible from the view point.
80
+ t = (i , j ) # t for target, v for viewer
66
81
if dist >= 0 :
67
- visibility_grid [i , j ] = INVISIBLE
82
+ visibility_grid [t ] = INVISIBLE
83
+ else :
84
+ if t == v :
85
+ visibility_grid [t ] = 180
86
+ else :
87
+ diff_elev = (hmap [v ]+ oe ) - (hmap [t ]+ te )
88
+ dy = (v [0 ]- t [0 ])* ns_range
89
+ dx = (v [1 ]- t [1 ])* ew_range
90
+ distance_to_viewpoint = math .sqrt (dx * dx + dy * dy )
91
+ visibility_grid [t ] = _get_vertical_ang (diff_elev , distance_to_viewpoint )
68
92
69
93
70
- def _calc_viewshed (hits , visibility_grid , H , W ):
94
+ def _calc_viewshed (hits , visibility_grid , H , W , hmap , vp , oe , te , ew_range , ns_range ):
71
95
griddim , blockdim = calc_cuda_dims ((H , W ))
72
- _calc_viewshed_kernel [griddim , blockdim ](hits , visibility_grid , H , W )
96
+ _calc_viewshed_kernel [griddim , blockdim ](
97
+ hits ,
98
+ visibility_grid ,
99
+ H ,
100
+ W ,
101
+ hmap ,
102
+ vp ,
103
+ oe ,
104
+ te ,
105
+ ew_range ,
106
+ ns_range
107
+ )
73
108
return 0
74
109
75
110
76
111
@nb .cuda .jit
77
112
def _generate_viewshed_rays_kernel (
78
- camera_rays , hits , vsrays , visibility_grid , H , W , vp
113
+ camera_rays , hits , vsrays , H , W , vp
79
114
):
80
115
i , j = nb .cuda .grid (2 )
81
116
if i >= 0 and i < H and j >= 0 and j < W :
@@ -124,11 +159,6 @@ def _generate_viewshed_rays_kernel(
124
159
# normalize the direction (vector v)
125
160
new_dir = mul (new_dir , 1 / length )
126
161
127
- # cosine of the angle between n and v
128
- cosine = dot (norm , new_dir )
129
- theta = math .acos (cosine ) # Cosine angle in radians
130
- theta = (180 * theta )/ math .pi # Cosine angle in degrees
131
-
132
162
# prepare a viewshed ray to cast to determine visibility
133
163
vsray = vsrays [i , j ]
134
164
vsray [0 ] = new_origin [0 ]
@@ -140,13 +170,11 @@ def _generate_viewshed_rays_kernel(
140
170
vsray [6 ] = new_dir [2 ]
141
171
vsray [7 ] = length
142
172
143
- visibility_grid [i , j ] = theta
144
-
145
173
146
- def _generate_viewshed_rays (rays , hits , vsrays , visibility_grid , H , W , vp ):
174
+ def _generate_viewshed_rays (rays , hits , vsrays , H , W , vp ):
147
175
griddim , blockdim = calc_cuda_dims ((H , W ))
148
176
_generate_viewshed_rays_kernel [griddim , blockdim ](
149
- rays , hits , vsrays , visibility_grid , H , W , vp )
177
+ rays , hits , vsrays , H , W , vp )
150
178
return 0
151
179
152
180
@@ -157,6 +185,7 @@ def _viewshed_rt(
157
185
y : Union [int , float ],
158
186
observer_elev : float ,
159
187
target_elev : float ,
188
+ scale : float ,
160
189
) -> xr .DataArray :
161
190
162
191
H , W = raster .shape
@@ -183,6 +212,12 @@ def _viewshed_rt(
183
212
y_view = np .where (y_coords == y )[0 ][0 ]
184
213
x_view = np .where (x_coords == x )[0 ][0 ]
185
214
215
+ y_range = (y_coords [0 ], y_coords [- 1 ])
216
+ x_range = (x_coords [0 ], x_coords [- 1 ])
217
+
218
+ ew_res = (x_range [1 ] - x_range [0 ]) / (W - 1 )
219
+ ns_res = (y_range [1 ] - y_range [0 ]) / (H - 1 )
220
+
186
221
# Device buffers
187
222
d_rays = cupy .empty ((H , W , 8 ), np .float32 )
188
223
d_hits = cupy .empty ((H , W , 4 ), np .float32 )
@@ -196,14 +231,25 @@ def _viewshed_rt(
196
231
if res :
197
232
raise RuntimeError (f"Failed trace 1, error code: { res } " )
198
233
199
- _generate_viewshed_rays (d_rays , d_hits , d_vsrays , d_visgrid , H , W ,
200
- (x_view , y_view , observer_elev , target_elev ))
234
+ _generate_viewshed_rays (d_rays , d_hits , d_vsrays , H , W ,
235
+ (x_view , y_view , observer_elev * scale , target_elev * scale ))
201
236
device .synchronize ()
202
237
res = optix .trace (d_vsrays , d_hits , W * H )
203
238
if res :
204
239
raise RuntimeError (f"Failed trace 2, error code: { res } " )
205
240
206
- _calc_viewshed (d_hits , d_visgrid , H , W )
241
+ _calc_viewshed (
242
+ d_hits ,
243
+ d_visgrid ,
244
+ H ,
245
+ W ,
246
+ raster .data ,
247
+ (y_view , x_view ),
248
+ observer_elev ,
249
+ target_elev ,
250
+ ew_res ,
251
+ ns_res
252
+ )
207
253
208
254
if isinstance (raster .data , np .ndarray ):
209
255
visgrid = cupy .asnumpy (d_visgrid )
@@ -233,4 +279,4 @@ def viewshed_gpu(
233
279
optix = RTX ()
234
280
scale = create_triangulation (raster , optix )
235
281
236
- return _viewshed_rt (raster , optix , x , y , observer_elev * scale , target_elev * scale )
282
+ return _viewshed_rt (raster , optix , x , y , observer_elev , target_elev , scale )
0 commit comments