LCOV - code coverage report
Current view: top level - apps - gdal_viewshed.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 132 139 95.0 %
Date: 2024-05-03 15:49:35 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Viewshed Generator
       4             :  * Purpose:  Viewshed Generator mainline.
       5             :  * Author:   Tamas Szekeres <szekerest@gmail.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  *
       9             :  * Permission is hereby granted, free of charge, to any person obtaining a
      10             :  * copy of this software and associated documentation files (the "Software"),
      11             :  * to deal in the Software without restriction, including without limitation
      12             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      13             :  * and/or sell copies of the Software, and to permit persons to whom the
      14             :  * Software is furnished to do so, subject to the following conditions:
      15             :  *
      16             :  * The above copyright notice and this permission notice shall be included
      17             :  * in all copies or substantial portions of the Software.
      18             :  *
      19             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      20             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      22             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25             :  * DEALINGS IN THE SOFTWARE.
      26             :  ****************************************************************************/
      27             : 
      28             : #include <limits>
      29             : 
      30             : #include "commonutils.h"
      31             : #include "gdal.h"
      32             : #include "gdalargumentparser.h"
      33             : 
      34             : #include "viewshed.h"
      35             : 
      36             : /************************************************************************/
      37             : /*                                main()                                */
      38             : /************************************************************************/
      39             : 
      40          18 : MAIN_START(argc, argv)
      41             : {
      42             :     using namespace gdal;
      43             : 
      44          18 :     EarlySetConfigOptions(argc, argv);
      45             : 
      46          18 :     GDALAllRegister();
      47             : 
      48          18 :     argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
      49          29 :     CPLStringList aosArgv;
      50          18 :     aosArgv.Assign(argv, /* bTakeOwnership= */ true);
      51          18 :     if (argc < 1)
      52           0 :         std::exit(-argc);
      53             : 
      54          47 :     GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true);
      55             : 
      56             :     argParser.add_description(
      57          18 :         _("Calculates a viewshed raster from an input raster DEM."));
      58             : 
      59             :     argParser.add_epilog(_("For more details, consult "
      60          18 :                            "https://gdal.org/programs/gdal_viewshed.html"));
      61             : 
      62          29 :     Viewshed::Options opts;
      63             : 
      64          18 :     argParser.add_output_format_argument(opts.outputFormat);
      65          18 :     argParser.add_argument("-ox")
      66          18 :         .store_into(opts.observer.x)
      67          18 :         .required()
      68          36 :         .metavar("<value>")
      69          18 :         .help(_("The X position of the observer (in SRS units)."));
      70             : 
      71          18 :     argParser.add_argument("-oy")
      72          18 :         .store_into(opts.observer.y)
      73          18 :         .required()
      74          36 :         .metavar("<value>")
      75          18 :         .help(_("The Y position of the observer (in SRS units)."));
      76             : 
      77          18 :     argParser.add_argument("-oz")
      78          18 :         .default_value(2)
      79          18 :         .store_into(opts.observer.z)
      80          36 :         .metavar("<value>")
      81          18 :         .nargs(1)
      82             :         .help(_("The height of the observer above the DEM surface in the "
      83          18 :                 "height unit of the DEM."));
      84             : 
      85          18 :     argParser.add_argument("-vv")
      86          18 :         .default_value(255)
      87          18 :         .store_into(opts.visibleVal)
      88          36 :         .metavar("<value>")
      89          18 :         .nargs(1)
      90          18 :         .help(_("Pixel value to set for visible areas."));
      91             : 
      92          18 :     argParser.add_argument("-iv")
      93          18 :         .default_value(0)
      94          18 :         .store_into(opts.invisibleVal)
      95          36 :         .metavar("<value>")
      96          18 :         .nargs(1)
      97          18 :         .help(_("Pixel value to set for invisible areas."));
      98             : 
      99          18 :     argParser.add_argument("-ov")
     100          18 :         .default_value(0)
     101          18 :         .store_into(opts.outOfRangeVal)
     102          36 :         .metavar("<value>")
     103          18 :         .nargs(1)
     104             :         .help(
     105             :             _("Pixel value to set for the cells that fall outside of the range "
     106          18 :               "specified by the observer location and the maximum distance."));
     107             : 
     108          18 :     argParser.add_creation_options_argument(opts.creationOpts);
     109             : 
     110          18 :     argParser.add_argument("-a_nodata")
     111          18 :         .default_value(-1.0)
     112          18 :         .store_into(opts.nodataVal)
     113          36 :         .metavar("<value>")
     114          18 :         .nargs(1)
     115             :         .help(_("The value to be set for the cells in the output raster that "
     116          18 :                 "have no data."));
     117             : 
     118          18 :     argParser.add_argument("-tz")
     119          18 :         .default_value(0.0)
     120          18 :         .store_into(opts.targetHeight)
     121          36 :         .metavar("<value>")
     122          18 :         .nargs(1)
     123             :         .help(_("The height of the target above the DEM surface in the height "
     124          18 :                 "unit of the DEM."));
     125             : 
     126          18 :     argParser.add_argument("-md")
     127          18 :         .default_value(0)
     128          18 :         .store_into(opts.maxDistance)
     129          36 :         .metavar("<value>")
     130          18 :         .nargs(1)
     131          18 :         .help(_("Maximum distance from observer to compute visibility."));
     132             : 
     133             :     // Value for standard atmospheric refraction. See
     134             :     // doc/source/programs/gdal_viewshed.rst
     135          18 :     argParser.add_argument("-cc")
     136          18 :         .default_value(0.85714)
     137          18 :         .store_into(opts.curveCoeff)
     138          36 :         .metavar("<value>")
     139          18 :         .nargs(1)
     140             :         .help(_("Coefficient to consider the effect of the curvature and "
     141          18 :                 "refraction."));
     142             : 
     143          18 :     int nBandIn = 1;
     144          18 :     argParser.add_argument("-b")
     145          18 :         .default_value(nBandIn)
     146          18 :         .store_into(nBandIn)
     147          36 :         .metavar("<value>")
     148          18 :         .nargs(1)
     149          18 :         .help(_("Select an input band band containing the DEM data."));
     150             : 
     151          18 :     argParser.add_argument("-om")
     152          18 :         .choices("NORMAL", "DEM", "GROUND")
     153          36 :         .metavar("NORMAL|DEM|GROUND")
     154             :         .action(
     155           6 :             [&into = opts.outputMode](const std::string &value)
     156             :             {
     157           3 :                 if (EQUAL(value.c_str(), "DEM"))
     158           1 :                     into = Viewshed::OutputMode::DEM;
     159           2 :                 else if (EQUAL(value.c_str(), "GROUND"))
     160           1 :                     into = Viewshed::OutputMode::Ground;
     161             :                 else
     162           1 :                     into = Viewshed::OutputMode::Normal;
     163          18 :             })
     164          18 :         .nargs(1)
     165          18 :         .help(_("Sets what information the output contains."));
     166             : 
     167          18 :     bool bQuiet = false;
     168          18 :     argParser.add_quiet_argument(&bQuiet);
     169             : 
     170          29 :     std::string osSrcFilename;
     171          18 :     argParser.add_argument("src_filename")
     172          18 :         .store_into(osSrcFilename)
     173          18 :         .metavar("<src_filename>");
     174             : 
     175          18 :     argParser.add_argument("dst_filename")
     176          18 :         .store_into(opts.outputFilename)
     177          18 :         .metavar("<dst_filename>");
     178             : 
     179             :     try
     180             :     {
     181          18 :         argParser.parse_args(aosArgv);
     182             :     }
     183           4 :     catch (const std::exception &err)
     184             :     {
     185           4 :         argParser.display_error_and_usage(err);
     186           4 :         std::exit(1);
     187             :     }
     188             : 
     189          13 :     if (opts.maxDistance < 0)
     190             :     {
     191           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     192             :                  "Max distance must be non-negative.");
     193           0 :         exit(2);
     194             :     }
     195             : 
     196          13 :     if (opts.outputFormat.empty())
     197             :     {
     198             :         opts.outputFormat =
     199          11 :             GetOutputDriverForRaster(opts.outputFilename.c_str());
     200          11 :         if (opts.outputFormat.empty())
     201             :         {
     202           0 :             exit(2);
     203             :         }
     204             :     }
     205             : 
     206             :     // For double values that are out of range for byte raster output,
     207             :     // set to zero.  Values less than zero are sentinel as NULL nodata.
     208          24 :     if (opts.outputMode == Viewshed::OutputMode::Normal &&
     209          11 :         opts.nodataVal > std::numeric_limits<uint8_t>::max())
     210           0 :         opts.nodataVal = 0;
     211             : 
     212             :     /* -------------------------------------------------------------------- */
     213             :     /*      Open source raster file.                                        */
     214             :     /* -------------------------------------------------------------------- */
     215          13 :     GDALDatasetH hSrcDS = GDALOpen(osSrcFilename.c_str(), GA_ReadOnly);
     216          13 :     if (hSrcDS == nullptr)
     217           1 :         exit(2);
     218             : 
     219          12 :     GDALRasterBandH hBand = GDALGetRasterBand(hSrcDS, nBandIn);
     220          12 :     if (hBand == nullptr)
     221             :     {
     222           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     223             :                  "Band %d does not exist on dataset.", nBandIn);
     224           1 :         exit(2);
     225             :     }
     226             : 
     227          11 :     if (!argParser.is_used("-cc"))
     228             :     {
     229             :         const OGRSpatialReference *poSRS =
     230           9 :             GDALDataset::FromHandle(hSrcDS)->GetSpatialRef();
     231           9 :         if (poSRS)
     232             :         {
     233             :             OGRErr eSRSerr;
     234           7 :             const double dfSemiMajor = poSRS->GetSemiMajor(&eSRSerr);
     235           7 :             if (eSRSerr != OGRERR_FAILURE &&
     236           7 :                 fabs(dfSemiMajor - SRS_WGS84_SEMIMAJOR) >
     237             :                     0.05 * SRS_WGS84_SEMIMAJOR)
     238             :             {
     239           1 :                 opts.curveCoeff = 1.0;
     240           1 :                 CPLDebug("gdal_viewshed",
     241             :                          "Using -cc=1.0 as a non-Earth CRS has been detected");
     242             :             }
     243             :         }
     244             :     }
     245             : 
     246             :     /* -------------------------------------------------------------------- */
     247             :     /*      Invoke.                                                         */
     248             :     /* -------------------------------------------------------------------- */
     249          11 :     Viewshed oViewshed(opts);
     250             : 
     251             :     bool bSuccess =
     252          11 :         oViewshed.run(hBand, bQuiet ? GDALDummyProgress : GDALTermProgress);
     253             : 
     254          11 :     GDALDatasetH hDstDS = GDALDataset::FromHandle(oViewshed.output().release());
     255             : 
     256          11 :     GDALClose(hSrcDS);
     257          11 :     if (GDALClose(hDstDS) != CE_None)
     258           0 :         bSuccess = false;
     259             : 
     260          11 :     GDALDestroyDriverManager();
     261          11 :     OGRCleanupAll();
     262             : 
     263          11 :     return bSuccess ? 0 : 1;
     264             : }
     265             : 
     266           0 : MAIN_END

Generated by: LCOV version 1.14