Skip to content

Commit b3a1378

Browse files
committed
Working with tests.
1 parent 7942c1b commit b3a1378

File tree

7 files changed

+357
-143
lines changed

7 files changed

+357
-143
lines changed

alg/viewshed/viewshed_executor.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <cmath>
1818
#include <limits>
1919

20+
#include <iostream>
21+
2022
#include "viewshed_executor.h"
2123
#include "progress.h"
2224
#include "util.h"
@@ -428,14 +430,15 @@ void ViewshedExecutor::processFirstLineLeft(const LineLimits &ll,
428430
// If the start cell is next to the observer, just mark it visible.
429431
if (iStart + 1 == m_nX || iStart + 1 == oCurExtent.xStop)
430432
{
433+
double dfZ = *pThis;
431434
if (oOpts.outputMode == OutputMode::Normal)
432435
vResult[iStart] = oOpts.visibleVal;
433436
else
434437
{
435-
maskLowPitch(*pThis, 1, 0);
436-
setOutput(vResult[iStart], *pThis, *pThis);
438+
maskLowPitch(dfZ, 1, 0);
439+
setOutput(vResult[iStart], *pThis, dfZ);
437440
}
438-
maskHighPitch(vResult[iStart], *pThis, 1, 0);
441+
maskHighPitch(vResult[iStart], dfZ, 1, 0);
439442
iStart--;
440443
pThis--;
441444
}
@@ -622,14 +625,15 @@ void ViewshedExecutor::processFirstLineRight(const LineLimits &ll,
622625
// If the start cell is next to the observer, just mark it visible.
623626
if (iStart - 1 == m_nX || iStart == oCurExtent.xStart)
624627
{
628+
double dfZ = *pThis;
625629
if (oOpts.outputMode == OutputMode::Normal)
626630
vResult[iStart] = oOpts.visibleVal;
627631
else
628632
{
629-
maskLowPitch(*pThis, 1, 0);
630-
setOutput(vResult[iStart], *pThis, *pThis);
633+
maskLowPitch(dfZ, 1, 0);
634+
setOutput(vResult[iStart], *pThis, dfZ);
631635
}
632-
maskHighPitch(vResult[iStart], *pThis, 1, 0);
636+
maskHighPitch(vResult[iStart], dfZ, 1, 0);
633637
iStart++;
634638
pThis++;
635639
}
@@ -662,7 +666,6 @@ void ViewshedExecutor::processLineLeft(int nYOffset, LineLimits &ll,
662666
int iStart = m_nX - 1;
663667
int iEnd = ll.left - 1;
664668
int nLine = m_nY + nYOffset;
665-
666669
// If start to the left of end, everything is taken care of by processing right.
667670
if (iStart <= iEnd)
668671
return;
@@ -675,6 +678,7 @@ void ViewshedExecutor::processLineLeft(int nYOffset, LineLimits &ll,
675678
// If the observer is to the right of the raster, mark the first cell to the left as
676679
// visible. This may mark an out-of-range cell with a value, but this will be fixed
677680
// with the out of range assignment at the end.
681+
678682
if (iStart == oCurExtent.xStop - 1)
679683
{
680684
if (oOpts.outputMode == OutputMode::Normal)
@@ -851,7 +855,6 @@ bool ViewshedExecutor::processLine(int nLine, std::vector<double> &vLastLineVal)
851855
processLineRight(nYOffset, ll, vResult, vThisLineVal, vLastLineVal);
852856
});
853857
pQueue->WaitCompletion();
854-
855858
// Make the current line the last line.
856859
vLastLineVal = std::move(vThisLineVal);
857860

alg/viewshed/viewshed_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ inline std::ostream &operator<<(std::ostream &out, const Window &w)
188188
/// The right side processing range is [rightMin, right).
189189
struct LineLimits
190190
{
191+
/// Constructor that takes the members in order.
191192
LineLimits(int leftArg, int leftMinArg, int rightMinArg, int rightArg)
192193
: left(leftArg), leftMin(leftMinArg), rightMin(rightMinArg),
193194
right(rightArg)

apps/gdalalg_raster_viewshed.cpp

Lines changed: 127 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ GDALRasterViewshedAlgorithm::GDALRasterViewshedAlgorithm()
4444
AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
4545

4646
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
47-
AddOutputFormatArg(&m_format, /* bStreamAllowed = */ false,
47+
AddOutputFormatArg(&m_opts.outputFormat, /* bStreamAllowed = */ false,
4848
/* bGDALGAllowed = */ false)
4949
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
5050
{GDAL_DCAP_RASTER, GDAL_DCAP_CREATE});
@@ -56,13 +56,14 @@ GDALRasterViewshedAlgorithm::GDALRasterViewshedAlgorithm()
5656
.SetMetaVar("<X,Y> or <X,Y,H>")
5757
.SetMinCount(2)
5858
.SetMaxCount(3)
59-
.SetRequired()
6059
.SetRepeatedArgAllowed(false);
60+
AddArg("height", 'z', _("Observer height"), &m_opts.observer.z);
61+
6162
AddArg("target-height", 0,
6263
_("Height of the target above the DEM surface in the height unit of "
6364
"the DEM."),
64-
&m_targetHeight)
65-
.SetDefault(m_targetHeight);
65+
&m_opts.targetHeight)
66+
.SetDefault(m_opts.targetHeight);
6667
AddArg("mode", 0, _("Sets what information the output contains."),
6768
&m_outputMode)
6869
.SetChoices("normal", "DEM", "ground", "cumulative")
@@ -71,41 +72,81 @@ GDALRasterViewshedAlgorithm::GDALRasterViewshedAlgorithm()
7172
AddArg("max-distance", 0,
7273
_("Maximum distance from observer to compute visibility. It is also "
7374
"used to clamp the extent of the output raster."),
74-
&m_maxDistance)
75+
&m_opts.maxDistance)
76+
.SetMinValueIncluded(0);
77+
AddArg("min-distance", 0,
78+
_("Mask all cells less than this distance from the observer. Must "
79+
"be less "
80+
"than 'max-distance'."),
81+
&m_opts.minDistance)
7582
.SetMinValueIncluded(0);
83+
84+
AddArg("start-angle", 0,
85+
_("Mask all cells outside of the arc ('start-angle', 'end-angle'). "
86+
"Clockwise degrees "
87+
"from north. Also used to clamp the extent of the output raster."),
88+
&m_opts.startAngle)
89+
.SetMinValueIncluded(0)
90+
.SetMaxValueExcluded(360);
91+
AddArg("end-angle", 0,
92+
_("Mask all cells outside of the arc ('start-angle', 'end-angle'). "
93+
"Clockwise degrees "
94+
"from north. Also used to clamp the extent of the output raster."),
95+
&m_opts.endAngle)
96+
.SetMinValueIncluded(0)
97+
.SetMaxValueExcluded(360);
98+
99+
AddArg("high-pitch", 0,
100+
_("Mark all cells out-of-range where the observable height would be "
101+
"higher than the "
102+
"'high-pitch' angle from the observer. Degrees from horizontal - "
103+
"positive is up. "
104+
"Must be greater than 'low-pitch'."),
105+
&m_opts.highPitch)
106+
.SetMaxValueIncluded(90)
107+
.SetMinValueExcluded(-90);
108+
AddArg("low-pitch", 0,
109+
_("Bound observable height to be no lower than the 'low-pitch' "
110+
"angle from the observer. "
111+
"Degrees from horizontal - positive is up. Must be less than "
112+
"'high-pitch'."),
113+
&m_opts.lowPitch)
114+
.SetMaxValueExcluded(90)
115+
.SetMinValueIncluded(-90);
116+
76117
AddArg("curvature-coefficient", 0,
77118
_("Coefficient to consider the effect of the curvature and "
78119
"refraction."),
79-
&m_curveCoefficient)
120+
&m_opts.curveCoeff)
80121
.SetMinValueIncluded(0);
81122

82123
AddBandArg(&m_band).SetDefault(m_band);
83124
AddArg("visible-value", 0, _("Pixel value to set for visible areas"),
84-
&m_visibleVal)
85-
.SetDefault(m_visibleVal)
125+
&m_opts.visibleVal)
126+
.SetDefault(m_opts.visibleVal)
86127
.SetMinValueIncluded(0)
87128
.SetMaxValueIncluded(255);
88129
AddArg("invisible-value", 0, _("Pixel value to set for invisible areas"),
89-
&m_invisibleVal)
90-
.SetDefault(m_invisibleVal)
130+
&m_opts.invisibleVal)
131+
.SetDefault(m_opts.invisibleVal)
91132
.SetMinValueIncluded(0)
92133
.SetMaxValueIncluded(255);
93134
AddArg("out-of-range-value", 0,
94135
_("Pixel value to set for the cells that fall outside of the range "
95136
"specified by the observer location and the maximum distance"),
96-
&m_outOfRangeVal)
97-
.SetDefault(m_outOfRangeVal)
137+
&m_opts.outOfRangeVal)
138+
.SetDefault(m_opts.outOfRangeVal)
98139
.SetMinValueIncluded(0)
99140
.SetMaxValueIncluded(255);
100141
AddArg("dst-nodata", 0,
101142
_("The value to be set for the cells in the output raster that have "
102143
"no data."),
103-
&m_dstNoData)
144+
&m_opts.nodataVal)
104145
.SetMinValueIncluded(0)
105146
.SetMaxValueIncluded(255);
106147
AddArg("observer-spacing", 0, _("Cell Spacing between observers"),
107-
&m_observerSpacing)
108-
.SetDefault(m_observerSpacing)
148+
&m_opts.observerSpacing)
149+
.SetDefault(m_opts.observerSpacing)
109150
.SetMinValueIncluded(1);
110151

111152
m_numThreadsStr = std::to_string(m_numThreads);
@@ -121,62 +162,76 @@ bool GDALRasterViewshedAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
121162
{
122163
CPLAssert(!m_outputDataset.GetDatasetRef());
123164

124-
gdal::viewshed::Options opts;
125-
126-
opts.observer.x = m_observerPos[0];
127-
opts.observer.y = m_observerPos[1];
128-
if (m_observerPos.size() == 3)
129-
opts.observer.z = m_observerPos[2];
130-
else
131-
opts.observer.z = 2;
132-
133-
opts.targetHeight = m_targetHeight;
165+
if (GetArg("height")->IsExplicitlySet())
166+
{
167+
if (m_observerPos.size() == 3)
168+
{
169+
ReportError(CE_Failure, CPLE_AppDefined,
170+
"Height can't be specified in both 'position' and "
171+
"'height' arguments");
172+
return false;
173+
}
174+
}
134175

135-
opts.maxDistance = m_maxDistance;
176+
if (m_observerPos.size())
177+
{
178+
m_opts.observer.x = m_observerPos[0];
179+
m_opts.observer.y = m_observerPos[1];
180+
if (m_observerPos.size() == 3)
181+
m_opts.observer.z = m_observerPos[2];
182+
else
183+
m_opts.observer.z = 2;
184+
}
136185

137-
opts.curveCoeff = m_curveCoefficient;
138186
if (!GetArg("curvature-coefficient")->IsExplicitlySet())
139187
{
140-
opts.curveCoeff = gdal::viewshed::adjustCurveCoeff(
141-
opts.curveCoeff,
188+
m_opts.curveCoeff = gdal::viewshed::adjustCurveCoeff(
189+
m_opts.curveCoeff,
142190
GDALDataset::ToHandle(m_inputDataset.GetDatasetRef()));
143191
}
144192

145-
opts.visibleVal = m_visibleVal;
146-
opts.invisibleVal = m_invisibleVal;
147-
opts.outOfRangeVal = m_outOfRangeVal;
148-
opts.nodataVal = m_dstNoData;
149-
150193
if (m_outputMode == "normal")
151-
opts.outputMode = gdal::viewshed::OutputMode::Normal;
194+
m_opts.outputMode = gdal::viewshed::OutputMode::Normal;
152195
else if (m_outputMode == "DEM")
153-
opts.outputMode = gdal::viewshed::OutputMode::DEM;
196+
m_opts.outputMode = gdal::viewshed::OutputMode::DEM;
154197
else if (m_outputMode == "ground")
155-
opts.outputMode = gdal::viewshed::OutputMode::Ground;
198+
m_opts.outputMode = gdal::viewshed::OutputMode::Ground;
156199
else if (m_outputMode == "cumulative")
157-
opts.outputMode = gdal::viewshed::OutputMode::Cumulative;
200+
m_opts.outputMode = gdal::viewshed::OutputMode::Cumulative;
158201

159-
opts.observerSpacing = m_observerSpacing;
160-
opts.numJobs = static_cast<uint8_t>(std::clamp(m_numThreads, 0, 255));
202+
m_opts.numJobs = static_cast<uint8_t>(std::clamp(m_numThreads, 0, 255));
161203

162-
opts.outputFilename = m_outputDataset.GetName();
163-
opts.outputFormat = m_format;
164-
if (opts.outputFormat.empty())
204+
m_opts.outputFilename = m_outputDataset.GetName();
205+
if (m_opts.outputFormat.empty())
165206
{
166-
opts.outputFormat =
167-
GetOutputDriverForRaster(opts.outputFilename.c_str());
168-
if (opts.outputFormat.empty())
207+
m_opts.outputFormat =
208+
GetOutputDriverForRaster(m_opts.outputFilename.c_str());
209+
if (m_opts.outputFormat.empty())
169210
{
170211
ReportError(CE_Failure, CPLE_AppDefined,
171212
"Cannot guess output driver from output filename");
172213
return false;
173214
}
174215
}
175216

176-
opts.creationOpts = CPLStringList(m_creationOptions);
217+
m_opts.creationOpts = CPLStringList(m_creationOptions);
177218

178-
if (opts.outputMode == gdal::viewshed::OutputMode::Cumulative)
219+
if (m_opts.outputMode == gdal::viewshed::OutputMode::Cumulative)
179220
{
221+
static const std::vector<std::string> badArgs{
222+
"visible-value", "invisible-value", "max-distance",
223+
"min-distance", "start-angle", "end-angle",
224+
"low-pitch", "high-pitch", "position"};
225+
226+
for (const auto &arg : badArgs)
227+
if (GetArg(arg)->IsExplicitlySet())
228+
{
229+
std::string err =
230+
"Option '" + arg + "' can't be used in cumulative mode.";
231+
ReportError(CE_Failure, CPLE_AppDefined, "%s", err.c_str());
232+
return false;
233+
}
234+
180235
auto poSrcDS = m_inputDataset.GetDatasetRef();
181236
auto poSrcDriver = poSrcDS->GetDriver();
182237
if (EQUAL(poSrcDS->GetDescription(), "") || !poSrcDriver ||
@@ -187,29 +242,49 @@ bool GDALRasterViewshedAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
187242
"In cumulative mode, the input dataset must be opened by name");
188243
return false;
189244
}
190-
if (EQUAL(opts.outputFormat.c_str(), "MEM"))
245+
if (EQUAL(m_opts.outputFormat.c_str(), "MEM"))
191246
{
192247
ReportError(CE_Failure, CPLE_AppDefined,
193248
"In cumulative mode, the output dataset cannot be a "
194249
"MEM dataset");
195250
return false;
196251
}
197-
gdal::viewshed::Cumulative oViewshed(opts);
252+
gdal::viewshed::Cumulative oViewshed(m_opts);
198253
const bool bSuccess = oViewshed.run(
199254
m_inputDataset.GetName().c_str(),
200255
pfnProgress ? pfnProgress : GDALDummyProgress, pProgressData);
201256
if (bSuccess)
202257
{
203258
m_outputDataset.Set(std::unique_ptr<GDALDataset>(
204-
GDALDataset::Open(opts.outputFilename.c_str(),
259+
GDALDataset::Open(m_opts.outputFilename.c_str(),
205260
GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
206261
nullptr, nullptr, nullptr)));
207262
}
208263
return bSuccess;
209264
}
210265
else
211266
{
212-
gdal::viewshed::Viewshed oViewshed(opts);
267+
static const std::vector<std::string> badArgs{"observer-spacing",
268+
"num-threads"};
269+
for (const auto &arg : badArgs)
270+
if (GetArg(arg)->IsExplicitlySet())
271+
{
272+
std::string err =
273+
"Option '" + arg + "' can't be used in standard mode.";
274+
ReportError(CE_Failure, CPLE_AppDefined, "%s", err.c_str());
275+
return false;
276+
}
277+
static const std::vector<std::string> goodArgs{"position"};
278+
for (const auto &arg : goodArgs)
279+
if (!GetArg(arg)->IsExplicitlySet())
280+
{
281+
std::string err =
282+
"Option '" + arg + "' must be specified in standard mode.";
283+
ReportError(CE_Failure, CPLE_AppDefined, "%s", err.c_str());
284+
return false;
285+
}
286+
287+
gdal::viewshed::Viewshed oViewshed(m_opts);
213288
const bool bSuccess = oViewshed.run(
214289
GDALRasterBand::ToHandle(
215290
m_inputDataset.GetDatasetRef()->GetRasterBand(m_band)),

apps/gdalalg_raster_viewshed.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define GDALALG_RASTER_VIEWSHED_INCLUDED
1515

1616
#include "gdalalgorithm.h"
17+
#include "viewshed/viewshed_types.h"
1718

1819
//! @cond Doxygen_Suppress
1920

@@ -39,25 +40,16 @@ class GDALRasterViewshedAlgorithm final : public GDALAlgorithm
3940
std::vector<std::string> m_openOptions{};
4041
std::vector<std::string> m_inputFormats{};
4142

42-
std::string m_format{};
4343
GDALArgDatasetValue m_outputDataset{};
4444
std::vector<std::string> m_creationOptions{};
4545
bool m_overwrite = false;
4646

4747
std::vector<double> m_observerPos{};
48-
double m_targetHeight = 0;
48+
gdal::viewshed::Options m_opts;
4949

5050
std::string m_outputMode = "normal";
51-
5251
int m_band = 1;
53-
double m_maxDistance = 0;
54-
double m_curveCoefficient = 0.85714;
55-
int m_observerSpacing = 10;
5652
int m_numThreads = 3;
57-
int m_dstNoData = -1;
58-
int m_visibleVal = 255;
59-
int m_invisibleVal = 0;
60-
int m_outOfRangeVal = 0;
6153

6254
// Work variables
6355
std::string m_numThreadsStr{};

0 commit comments

Comments
 (0)