Skip to content

Commit 1d1e6f7

Browse files
committed
Checkpoint - working min/max distance and angle masking.
1 parent 1230200 commit 1d1e6f7

File tree

7 files changed

+406
-80
lines changed

7 files changed

+406
-80
lines changed

alg/viewshed/util.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,22 @@ int hIntersect(double angle, int nX, int nY, int y)
5656
{
5757
double x = horizontalIntersect(angle, nX, nY, y);
5858
if (std::isnan(x))
59-
return (std::numeric_limits<int>::max)();
59+
return INVALID_ISECT;
6060
return static_cast<int>(std::round(x));
6161
}
6262

63+
int hIntersect(double angle, int nX, int nY, const Window &win)
64+
{
65+
if (ARE_REAL_EQUAL(angle, M_PI))
66+
return win.xStart;
67+
if (ARE_REAL_EQUAL(angle, 0))
68+
return win.xStop;
69+
double x = horizontalIntersect(angle, nX, nY, win.yStart);
70+
if (std::isnan(x))
71+
x = horizontalIntersect(angle, nX, nY, win.yStop);
72+
return std::clamp(static_cast<int>(std::round(x)), win.xStart, win.xStop);
73+
}
74+
6375
/// Compute the Y intersect position on the line X = x with a ray extending
6476
/// from (nX, nY) along `angle`.
6577
///ABELL doc args
@@ -90,20 +102,32 @@ int vIntersect(double angle, int nX, int nY, int x)
90102
{
91103
double y = verticalIntersect(angle, nX, nY, x);
92104
if (std::isnan(y))
93-
return (std::numeric_limits<int>::max)();
105+
return INVALID_ISECT;
94106
return static_cast<int>(std::round(y));
95107
}
96108

109+
int vIntersect(double angle, int nX, int nY, const Window &win)
110+
{
111+
if (ARE_REAL_EQUAL(angle, M_PI / 2))
112+
return win.yStart;
113+
if (ARE_REAL_EQUAL(angle, 3 * M_PI / 2))
114+
return win.yStop;
115+
double y = verticalIntersect(angle, nX, nY, win.xStart);
116+
if (std::isnan(y))
117+
y = verticalIntersect(angle, nX, nY, win.xStop);
118+
return std::clamp(static_cast<int>(std::round(y)), win.yStart, win.yStop);
119+
}
120+
97121
// Determine if ray is in the slice between two rays starting at `start` and
98-
// going clockwise to `end`.
122+
// going clockwise to `end` (inclusive). [start, end]
99123
bool rayBetween(double start, double end, double test)
100124
{
101125
// Our angles go counterclockwise, so swap start and end
102126
std::swap(start, end);
103127
if (start < end)
104-
return (test > start && test < end);
128+
return (test >= start && test <= end);
105129
else if (start > end)
106-
return (test > start || test < end);
130+
return (test >= start || test <= end);
107131
return false;
108132
}
109133

alg/viewshed/util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ namespace viewshed
1717
double normalizeAngle(double maskAngle);
1818
double horizontalIntersect(double angle, int nX, int nY, int y);
1919
int hIntersect(double angle, int nX, int nY, int y);
20+
int hIntersect(double angle, int nX, int nY, const Window &win);
2021
double verticalIntersect(double angle, int nX, int nY, int x);
2122
int vIntersect(double angle, int nX, int nY, int x);
23+
int vIntersect(double angle, int nX, int nY, const Window &win);
2224
bool rayBetween(double start, double end, double test);
2325
size_t bandSize(GDALRasterBand &band);
2426

alg/viewshed/viewshed.cpp

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
****************************************************************************/
1313

1414
#include <algorithm>
15-
//ABELL
16-
#include <iostream>
17-
#include <iomanip>
1815

1916
#include "gdal_alg.h"
2017
#include "gdal_priv_templates.hpp"
@@ -229,61 +226,45 @@ void shrinkWindowForAngles(Window &oOutExtent, int nX, int nY,
229226

230227
// Set the X boundaries for the angles
231228
//ABELL - Verify for out-of-raster.
232-
int xStart = hIntersect(startAngle, nX, nY, win.yStart);
233-
if (xStart == std::numeric_limits<int>::max())
234-
xStart = hIntersect(startAngle, nX, nY, win.yStop);
235-
236-
int xStop = hIntersect(endAngle, nX, nY, win.yStart);
237-
if (xStop == std::numeric_limits<int>::max())
238-
xStop = hIntersect(endAngle, nX, nY, win.yStop);
229+
int startAngleX = hIntersect(startAngle, nX, nY, win);
230+
int stopAngleX = hIntersect(endAngle, nX, nY, win);
239231

240232
int xmax = nX;
241233
if (!rayBetween(startAngle, endAngle, 0))
242234
{
243-
if (xStart != (std::numeric_limits<int>::max)())
244-
xmax = std::max(xmax, xStart);
245-
if (xStop != (std::numeric_limits<int>::max)())
246-
xmax = std::max(xmax, xStop);
247-
oOutExtent.xStop = std::min(oOutExtent.xStop, xmax);
235+
xmax = std::max(xmax, startAngleX);
236+
xmax = std::max(xmax, stopAngleX);
237+
// Add one to xmax since we want one past the stop. [start, stop)
238+
oOutExtent.xStop = std::min(oOutExtent.xStop, xmax + 1);
248239
}
249240

250241
int xmin = nX;
251242
if (!rayBetween(startAngle, endAngle, M_PI))
252243
{
253-
if (!isnan(xStart))
254-
xmin = std::min(xmin, xStart);
255-
if (!isnan(xStop))
256-
xmin = std::min(xmin, xStop);
244+
xmin = std::min(xmin, startAngleX);
245+
xmin = std::min(xmin, stopAngleX);
257246
oOutExtent.xStart = std::max(oOutExtent.xStart, xmin);
258247
}
259248

260249
// Set the Y boundaries for the angles
261250
//ABELL - Verify for out-of-raster.
262-
int yStart = vIntersect(startAngle, nX, nY, win.xStart);
263-
if (yStart == std::numeric_limits<int>::max())
264-
yStart = vIntersect(startAngle, nX, nY, win.xStop);
265-
266-
int yStop = vIntersect(endAngle, nX, nY, win.xStart);
267-
if (yStop == std::numeric_limits<int>::max())
268-
yStop = vIntersect(endAngle, nX, nY, win.xStop);
251+
int startAngleY = vIntersect(startAngle, nX, nY, win);
252+
int stopAngleY = vIntersect(endAngle, nX, nY, win);
269253

270254
int ymin = nY;
271255
if (!rayBetween(startAngle, endAngle, M_PI / 2))
272256
{
273-
if (yStart != std::numeric_limits<int>::max())
274-
ymin = std::min(ymin, yStart);
275-
if (yStop != std::numeric_limits<int>::max())
276-
ymin = std::min(ymin, yStop);
257+
ymin = std::min(ymin, startAngleY);
258+
ymin = std::min(ymin, stopAngleY);
277259
oOutExtent.yStart = std::max(oOutExtent.yStart, ymin);
278260
}
279261
int ymax = nY;
280262
if (!rayBetween(startAngle, endAngle, 3 * M_PI / 2))
281263
{
282-
if (yStart != std::numeric_limits<int>::max())
283-
ymax = std::max(ymax, yStart);
284-
if (yStop != std::numeric_limits<int>::max())
285-
ymax = std::max(ymax, yStop);
286-
oOutExtent.yStop = std::min(oOutExtent.yStop, ymax);
264+
ymax = std::max(ymax, startAngleY);
265+
ymax = std::max(ymax, stopAngleY);
266+
// Add one to ymax since we want one past the stop. [start, stop)
267+
oOutExtent.yStop = std::min(oOutExtent.yStop, ymax + 1);
287268
}
288269
}
289270

@@ -307,8 +288,17 @@ bool Viewshed::calcExtents(int nX, int nY,
307288
oOutExtent.yStop = GDALGetRasterBandYSize(pSrcBand);
308289

309290
if (!oOutExtent.contains(nX, nY))
291+
{
292+
if (oOpts.startAngle != oOpts.endAngle)
293+
{
294+
CPLError(CE_Failure, CPLE_AppDefined,
295+
"Angle masking is not supported with an out-of-raster "
296+
"observer.");
297+
return false;
298+
}
310299
CPLError(CE_Warning, CPLE_AppDefined,
311300
"NOTE: The observer location falls outside of the DEM area");
301+
}
312302

313303
constexpr double EPSILON = 1e-8;
314304
if (oOpts.maxDistance > 0)
@@ -321,6 +311,10 @@ bool Viewshed::calcExtents(int nX, int nY,
321311
int nXStop = static_cast<int>(
322312
std::ceil(nX + adfInvTransform[1] * oOpts.maxDistance - EPSILON) +
323313
1);
314+
//ABELL - These seem to be wrong. The transform of 1 is no transform, so not
315+
// sure why we're adding one in the first case. Really, the transformed distance
316+
// should add EPSILON. Not sure what the change should be for a negative transform,
317+
// which is what I think is being handled with the 1/0 addition/subtraction.
324318
int nYStart =
325319
static_cast<int>(std::floor(
326320
nY - std::fabs(adfInvTransform[5]) * oOpts.maxDistance +
@@ -399,11 +393,35 @@ bool Viewshed::run(GDALRasterBandH band, GDALProgressFunc pfnProgress,
399393
int nY = static_cast<int>(dfY);
400394

401395
if (oOpts.startAngle < 0 || oOpts.startAngle >= 360)
396+
{
402397
CPLError(CE_Failure, CPLE_AppDefined,
403398
"Start angle out of range. Must be [0, 360).");
399+
return false;
400+
}
404401
if (oOpts.endAngle < 0 || oOpts.endAngle >= 360)
402+
{
405403
CPLError(CE_Failure, CPLE_AppDefined,
406404
"End angle out of range. Must be [0, 360).");
405+
return false;
406+
}
407+
if (oOpts.maxPitch > 90)
408+
{
409+
CPLError(CE_Failure, CPLE_AppDefined,
410+
"Invalid maxPitch. Cannot be greater than 90.");
411+
return false;
412+
}
413+
if (oOpts.minPitch < -90)
414+
{
415+
CPLError(CE_Failure, CPLE_AppDefined,
416+
"Invalid maxPitch. Cannot be less than -90.");
417+
return false;
418+
}
419+
if (oOpts.maxPitch <= oOpts.minPitch)
420+
{
421+
CPLError(CE_Failure, CPLE_AppDefined,
422+
"Invalid pitch. maxPitch must be > minPitch");
423+
return false;
424+
}
407425

408426
// Normalize angle to radians and standard math arrangement.
409427
oOpts.startAngle = normalizeAngle(oOpts.startAngle);

0 commit comments

Comments
 (0)