@@ -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)),
0 commit comments