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