LCOV - code coverage report
Current view: top level - alg - viewshed.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 366 449 81.5 %
Date: 2024-05-04 12:52:34 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Viewshed Generation
       4             :  * Purpose:  Core algorithm implementation for viewshed generation.
       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 <algorithm>
      29             : #include <array>
      30             : #include <limits>
      31             : 
      32             : #include "gdal_alg.h"
      33             : #include "gdal_priv.h"
      34             : #include "gdal_priv_templates.hpp"
      35             : 
      36             : #include "viewshed.h"
      37             : 
      38             : /************************************************************************/
      39             : /*                        GDALViewshedGenerate()                        */
      40             : /************************************************************************/
      41             : 
      42             : /**
      43             :  * Create viewshed from raster DEM.
      44             :  *
      45             :  * This algorithm will generate a viewshed raster from an input DEM raster
      46             :  * by using a modified algorithm of "Generating Viewsheds without Using
      47             :  * Sightlines" published at
      48             :  * https://www.asprs.org/wp-content/uploads/pers/2000journal/january/2000_jan_87-90.pdf
      49             :  * This appoach provides a relatively fast calculation, since the output raster
      50             :  * is generated in a single scan. The gdal/apps/gdal_viewshed.cpp mainline can
      51             :  * be used as an example of how to use this function. The output raster will be
      52             :  * of type Byte or Float64.
      53             :  *
      54             :  * \note The algorithm as implemented currently will only output meaningful
      55             :  * results if the georeferencing is in a projected coordinate reference system.
      56             :  *
      57             :  * @param hBand The band to read the DEM data from. Only the part of the raster
      58             :  * within the specified maxdistance around the observer point is processed.
      59             :  *
      60             :  * @param pszDriverName Driver name (GTiff if set to NULL)
      61             :  *
      62             :  * @param pszTargetRasterName The name of the target raster to be generated.
      63             :  * Must not be NULL
      64             :  *
      65             :  * @param papszCreationOptions creation options.
      66             :  *
      67             :  * @param dfObserverX observer X value (in SRS units)
      68             :  *
      69             :  * @param dfObserverY observer Y value (in SRS units)
      70             :  *
      71             :  * @param dfObserverHeight The height of the observer above the DEM surface.
      72             :  *
      73             :  * @param dfTargetHeight The height of the target above the DEM surface.
      74             :  * (default 0)
      75             :  *
      76             :  * @param dfVisibleVal pixel value for visibility (default 255)
      77             :  *
      78             :  * @param dfInvisibleVal pixel value for invisibility (default 0)
      79             :  *
      80             :  * @param dfOutOfRangeVal The value to be set for the cells that fall outside of
      81             :  * the range specified by dfMaxDistance.
      82             :  *
      83             :  * @param dfNoDataVal The value to be set for the cells that have no data.
      84             :  *                    If set to a negative value, nodata is not set.
      85             :  *                    Note: currently, no special processing of input cells at a
      86             :  * nodata value is done (which may result in erroneous results).
      87             :  *
      88             :  * @param dfCurvCoeff Coefficient to consider the effect of the curvature and
      89             :  * refraction. The height of the DEM is corrected according to the following
      90             :  * formula: [Height] -= dfCurvCoeff * [Target Distance]^2 / [Earth Diameter] For
      91             :  * the effect of the atmospheric refraction we can use 0.85714.
      92             :  *
      93             :  * @param eMode The mode of the viewshed calculation.
      94             :  * Possible values GVM_Diagonal = 1, GVM_Edge = 2 (default), GVM_Max = 3,
      95             :  * GVM_Min = 4.
      96             :  *
      97             :  * @param dfMaxDistance maximum distance range to compute viewshed.
      98             :  *                      It is also used to clamp the extent of the output
      99             :  * raster. If set to 0, then unlimited range is assumed, that is to say the
     100             :  *                      computation is performed on the extent of the whole
     101             :  * raster.
     102             :  *
     103             :  * @param pfnProgress A GDALProgressFunc that may be used to report progress
     104             :  * to the user, or to interrupt the algorithm.  May be NULL if not required.
     105             :  *
     106             :  * @param pProgressArg The callback data for the pfnProgress function.
     107             :  *
     108             :  * @param heightMode Type of information contained in output raster. Possible
     109             :  * values GVOT_NORMAL = 1 (default), GVOT_MIN_TARGET_HEIGHT_FROM_DEM = 2,
     110             :  *                   GVOT_MIN_TARGET_HEIGHT_FROM_GROUND = 3
     111             :  *
     112             :  *                   GVOT_NORMAL returns a raster of type Byte containing
     113             :  * visible locations.
     114             :  *
     115             :  *                   GVOT_MIN_TARGET_HEIGHT_FROM_DEM and
     116             :  * GVOT_MIN_TARGET_HEIGHT_FROM_GROUND will return a raster of type Float64
     117             :  * containing the minimum target height for target to be visible from the DEM
     118             :  * surface or ground level respectively. Parameters dfTargetHeight, dfVisibleVal
     119             :  * and dfInvisibleVal will be ignored.
     120             :  *
     121             :  *
     122             :  * @param papszExtraOptions Future extra options. Must be set to NULL currently.
     123             :  *
     124             :  * @return not NULL output dataset on success (to be closed with GDALClose()) or
     125             :  * NULL if an error occurs.
     126             :  *
     127             :  * @since GDAL 3.1
     128             :  */
     129           1 : GDALDatasetH GDALViewshedGenerate(
     130             :     GDALRasterBandH hBand, const char *pszDriverName,
     131             :     const char *pszTargetRasterName, CSLConstList papszCreationOptions,
     132             :     double dfObserverX, double dfObserverY, double dfObserverHeight,
     133             :     double dfTargetHeight, double dfVisibleVal, double dfInvisibleVal,
     134             :     double dfOutOfRangeVal, double dfNoDataVal, double dfCurvCoeff,
     135             :     GDALViewshedMode eMode, double dfMaxDistance, GDALProgressFunc pfnProgress,
     136             :     void *pProgressArg, GDALViewshedOutputType heightMode,
     137             :     [[maybe_unused]] CSLConstList papszExtraOptions)
     138             : {
     139             :     using namespace gdal;
     140             : 
     141           2 :     Viewshed::Options oOpts;
     142           1 :     oOpts.outputFormat = pszDriverName;
     143           1 :     oOpts.outputFilename = pszTargetRasterName;
     144           1 :     oOpts.creationOpts = papszCreationOptions;
     145           1 :     oOpts.observer.x = dfObserverX;
     146           1 :     oOpts.observer.y = dfObserverY;
     147           1 :     oOpts.observer.z = dfObserverHeight;
     148           1 :     oOpts.targetHeight = dfTargetHeight;
     149           1 :     oOpts.curveCoeff = dfCurvCoeff;
     150           1 :     oOpts.maxDistance = dfMaxDistance;
     151           1 :     oOpts.nodataVal = dfNoDataVal;
     152             : 
     153           1 :     switch (eMode)
     154             :     {
     155           1 :         case GVM_Edge:
     156           1 :             oOpts.cellMode = Viewshed::CellMode::Edge;
     157           1 :             break;
     158           0 :         case GVM_Diagonal:
     159           0 :             oOpts.cellMode = Viewshed::CellMode::Diagonal;
     160           0 :             break;
     161           0 :         case GVM_Min:
     162           0 :             oOpts.cellMode = Viewshed::CellMode::Min;
     163           0 :             break;
     164           0 :         case GVM_Max:
     165           0 :             oOpts.cellMode = Viewshed::CellMode::Max;
     166           0 :             break;
     167             :     }
     168             : 
     169           1 :     switch (heightMode)
     170             :     {
     171           0 :         case GVOT_MIN_TARGET_HEIGHT_FROM_DEM:
     172           0 :             oOpts.outputMode = Viewshed::OutputMode::DEM;
     173           0 :             break;
     174           1 :         case GVOT_MIN_TARGET_HEIGHT_FROM_GROUND:
     175           1 :             oOpts.outputMode = Viewshed::OutputMode::Ground;
     176           1 :             break;
     177           0 :         case GVOT_NORMAL:
     178           0 :             oOpts.outputMode = Viewshed::OutputMode::Normal;
     179           0 :             break;
     180             :     }
     181             : 
     182           1 :     if (!GDALIsValueInRange<uint8_t>(dfVisibleVal))
     183             :     {
     184           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     185             :                  "dfVisibleVal out of range. Must be [0, 255].");
     186           0 :         return nullptr;
     187             :     }
     188           1 :     if (!GDALIsValueInRange<uint8_t>(dfInvisibleVal))
     189             :     {
     190           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     191             :                  "dfInvisibleVal out of range. Must be [0, 255].");
     192           0 :         return nullptr;
     193             :     }
     194           1 :     if (!GDALIsValueInRange<uint8_t>(dfOutOfRangeVal))
     195             :     {
     196           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     197             :                  "dfOutOfRangeVal out of range. Must be [0, 255].");
     198           0 :         return nullptr;
     199             :     }
     200           1 :     oOpts.visibleVal = static_cast<uint8_t>(dfVisibleVal);
     201           1 :     oOpts.invisibleVal = static_cast<uint8_t>(dfInvisibleVal);
     202           1 :     oOpts.outOfRangeVal = static_cast<uint8_t>(dfOutOfRangeVal);
     203             : 
     204           1 :     gdal::Viewshed v(oOpts);
     205             : 
     206             :     //ABELL - Make a function for progress that captures the progress argument.
     207           1 :     v.run(hBand, pfnProgress, pProgressArg);
     208             : 
     209           1 :     return GDALDataset::FromHandle(v.output().release());
     210             : }
     211             : 
     212             : namespace gdal
     213             : {
     214             : 
     215             : namespace
     216             : {
     217             : 
     218       88480 : void SetVisibility(int iPixel, double dfZ, double dfZTarget, double *padfZVal,
     219             :                    std::vector<GByte> &vResult, GByte byVisibleVal,
     220             :                    GByte byInvisibleVal)
     221             : {
     222       88480 :     if (padfZVal[iPixel] + dfZTarget < dfZ)
     223       57873 :         vResult[iPixel] = byInvisibleVal;
     224             :     else
     225       30607 :         vResult[iPixel] = byVisibleVal;
     226             : 
     227       88480 :     if (padfZVal[iPixel] < dfZ)
     228       57968 :         padfZVal[iPixel] = dfZ;
     229       88480 : }
     230             : 
     231       88602 : bool AdjustHeightInRange(const double *adfGeoTransform, int iPixel, int iLine,
     232             :                          double &dfHeight, double dfDistance2,
     233             :                          double dfCurvCoeff, double dfSphereDiameter)
     234             : {
     235       88602 :     if (dfDistance2 <= 0 && dfCurvCoeff == 0)
     236           0 :         return true;
     237             : 
     238       88602 :     double dfX = adfGeoTransform[1] * iPixel + adfGeoTransform[2] * iLine;
     239       88602 :     double dfY = adfGeoTransform[4] * iPixel + adfGeoTransform[5] * iLine;
     240       88602 :     double dfR2 = dfX * dfX + dfY * dfY;
     241             : 
     242             :     /* calc adjustment */
     243      175184 :     if (dfCurvCoeff != 0 &&
     244       86582 :         dfSphereDiameter != std::numeric_limits<double>::infinity())
     245       86514 :         dfHeight -= dfCurvCoeff * dfR2 / dfSphereDiameter;
     246             : 
     247       88602 :     if (dfDistance2 > 0 && dfR2 > dfDistance2)
     248         104 :         return false;
     249             : 
     250       88498 :     return true;
     251             : }
     252             : 
     253        2852 : double CalcHeightLine(int i, double Za, double Zo)
     254             : {
     255        2852 :     if (i == 1)
     256          54 :         return Za;
     257             :     else
     258        2798 :         return (Za - Zo) / (i - 1) + Za;
     259             : }
     260             : 
     261           0 : double CalcHeightDiagonal(int i, int j, double Za, double Zb, double Zo)
     262             : {
     263           0 :     return ((Za - Zo) * i + (Zb - Zo) * j) / (i + j - 1) + Zo;
     264             : }
     265             : 
     266       86936 : double CalcHeightEdge(int i, int j, double Za, double Zb, double Zo)
     267             : {
     268       86936 :     if (i == j)
     269        1308 :         return CalcHeightLine(i, Za, Zo);
     270             :     else
     271       85628 :         return ((Za - Zo) * i + (Zb - Zo) * (j - i)) / (j - 1) + Zo;
     272             : }
     273             : 
     274       86936 : double CalcHeight(double dfZ, double dfZ2, Viewshed::CellMode eMode)
     275             : {
     276       86936 :     double dfHeight = dfZ;
     277             : 
     278       86936 :     switch (eMode)
     279             :     {
     280       86936 :         case Viewshed::CellMode::Edge:
     281       86936 :             dfHeight = dfZ2;
     282       86936 :             break;
     283           0 :         case Viewshed::CellMode::Max:
     284           0 :             dfHeight = std::max(dfZ, dfZ2);
     285           0 :             break;
     286           0 :         case Viewshed::CellMode::Min:
     287           0 :             dfHeight = std::min(dfZ, dfZ2);
     288           0 :             break;
     289           0 :         case Viewshed::CellMode::Diagonal:
     290           0 :             dfHeight = dfZ;
     291           0 :             break;
     292             :     }
     293       86936 :     return dfHeight;
     294             : }
     295             : 
     296             : }  // unnamed namespace
     297             : 
     298          12 : bool Viewshed::run(GDALRasterBandH hBand, GDALProgressFunc pfnProgress,
     299             :                    void *pProgressArg)
     300             : {
     301          12 :     if (!pfnProgress)
     302           1 :         pfnProgress = GDALDummyProgress;
     303             : 
     304          12 :     if (!pfnProgress(0.0, "", pProgressArg))
     305             :     {
     306           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     307           0 :         return false;
     308             :     }
     309             : 
     310             :     /* set up geotransformation */
     311          12 :     std::array<double, 6> adfGeoTransform{{0.0, 1.0, 0.0, 0.0, 0.0, 1.0}};
     312          12 :     GDALDatasetH hSrcDS = GDALGetBandDataset(hBand);
     313          12 :     if (hSrcDS != nullptr)
     314          12 :         GDALGetGeoTransform(hSrcDS, adfGeoTransform.data());
     315             : 
     316             :     double adfInvGeoTransform[6];
     317          12 :     if (!GDALInvGeoTransform(adfGeoTransform.data(), adfInvGeoTransform))
     318             :     {
     319           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
     320           0 :         return false;
     321             :     }
     322             : 
     323             :     /* calculate observer position */
     324             :     double dfX, dfY;
     325          12 :     GDALApplyGeoTransform(adfInvGeoTransform, oOpts.observer.x,
     326             :                           oOpts.observer.y, &dfX, &dfY);
     327          12 :     int nX = static_cast<int>(dfX);
     328          12 :     int nY = static_cast<int>(dfY);
     329             : 
     330          12 :     int nXSize = GDALGetRasterBandXSize(hBand);
     331          12 :     int nYSize = GDALGetRasterBandYSize(hBand);
     332             : 
     333          12 :     if (nX < 0 || nX > nXSize || nY < 0 || nY > nYSize)
     334             :     {
     335           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     336             :                  "The observer location falls outside of the DEM area");
     337           1 :         return false;
     338             :     }
     339             : 
     340             :     /* calculate the area of interest */
     341          11 :     constexpr double EPSILON = 1e-8;
     342             : 
     343          11 :     int nXStart = 0;
     344          11 :     int nYStart = 0;
     345          11 :     int nXStop = nXSize;
     346          11 :     int nYStop = nYSize;
     347          11 :     if (oOpts.maxDistance > 0)
     348             :     {
     349           1 :         nXStart = static_cast<int>(std::floor(
     350           1 :             nX - adfInvGeoTransform[1] * oOpts.maxDistance + EPSILON));
     351           1 :         nXStop = static_cast<int>(
     352           1 :             std::ceil(nX + adfInvGeoTransform[1] * oOpts.maxDistance -
     353           1 :                       EPSILON) +
     354             :             1);
     355           1 :         nYStart =
     356           1 :             static_cast<int>(std::floor(
     357           1 :                 nY - std::fabs(adfInvGeoTransform[5]) * oOpts.maxDistance +
     358           1 :                 EPSILON)) -
     359           1 :             (adfInvGeoTransform[5] > 0 ? 1 : 0);
     360           1 :         nYStop = static_cast<int>(
     361           1 :             std::ceil(nY +
     362           1 :                       std::fabs(adfInvGeoTransform[5]) * oOpts.maxDistance -
     363           1 :                       EPSILON) +
     364           1 :             (adfInvGeoTransform[5] < 0 ? 1 : 0));
     365             :     }
     366          11 :     nXStart = std::max(nXStart, 0);
     367          11 :     nYStart = std::max(nYStart, 0);
     368          11 :     nXStop = std::min(nXStop, nXSize);
     369          11 :     nYStop = std::min(nYStop, nYSize);
     370             : 
     371             :     /* normalize horizontal index (0 - nXSize) */
     372          11 :     nXSize = nXStop - nXStart;
     373          11 :     nX -= nXStart;
     374             : 
     375          11 :     nYSize = nYStop - nYStart;
     376             : 
     377          11 :     if (nXSize == 0 || nYSize == 0)
     378             :     {
     379           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid target raster size");
     380           0 :         return false;
     381             :     }
     382             : 
     383          22 :     std::vector<double> vFirstLineVal;
     384          22 :     std::vector<double> vLastLineVal;
     385          22 :     std::vector<double> vThisLineVal;
     386          22 :     std::vector<GByte> vResult;
     387          22 :     std::vector<double> vHeightResult;
     388             : 
     389             :     try
     390             :     {
     391          11 :         vFirstLineVal.resize(nXSize);
     392          11 :         vLastLineVal.resize(nXSize);
     393          11 :         vThisLineVal.resize(nXSize);
     394          11 :         vResult.resize(nXSize);
     395             : 
     396          11 :         if (oOpts.outputMode != OutputMode::Normal)
     397           3 :             vHeightResult.resize(nXSize);
     398             :     }
     399           0 :     catch (...)
     400             :     {
     401           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     402             :                  "Cannot allocate vectors for viewshed");
     403           0 :         return false;
     404             :     }
     405             : 
     406          11 :     double *padfFirstLineVal = vFirstLineVal.data();
     407          11 :     double *padfLastLineVal = vLastLineVal.data();
     408          11 :     double *padfThisLineVal = vThisLineVal.data();
     409          11 :     GByte *pabyResult = vResult.data();
     410          11 :     double *dfHeightResult = vHeightResult.data();
     411             : 
     412          11 :     GDALDriverManager *hMgr = GetGDALDriverManager();
     413          11 :     GDALDriver *hDriver = hMgr->GetDriverByName(oOpts.outputFormat.c_str());
     414          11 :     if (!hDriver)
     415             :     {
     416           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver");
     417           1 :         return false;
     418             :     }
     419             : 
     420             :     /* create output raster */
     421          10 :     poDstDS.reset(hDriver->Create(
     422             :         oOpts.outputFilename.c_str(), nXSize, nYSize, 1,
     423          10 :         oOpts.outputMode == OutputMode::Normal ? GDT_Byte : GDT_Float64,
     424          10 :         const_cast<char **>(oOpts.creationOpts.List())));
     425          10 :     if (!poDstDS)
     426             :     {
     427           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create dataset for %s",
     428             :                  oOpts.outputFilename.c_str());
     429           1 :         return false;
     430             :     }
     431             :     /* copy srs */
     432           9 :     if (hSrcDS)
     433          18 :         poDstDS->SetSpatialRef(
     434           9 :             GDALDataset::FromHandle(hSrcDS)->GetSpatialRef());
     435             : 
     436             :     std::array<double, 6> adfDstGeoTransform;
     437           9 :     adfDstGeoTransform[0] = adfGeoTransform[0] + adfGeoTransform[1] * nXStart +
     438           9 :                             adfGeoTransform[2] * nYStart;
     439           9 :     adfDstGeoTransform[1] = adfGeoTransform[1];
     440           9 :     adfDstGeoTransform[2] = adfGeoTransform[2];
     441           9 :     adfDstGeoTransform[3] = adfGeoTransform[3] + adfGeoTransform[4] * nXStart +
     442           9 :                             adfGeoTransform[5] * nYStart;
     443           9 :     adfDstGeoTransform[4] = adfGeoTransform[4];
     444           9 :     adfDstGeoTransform[5] = adfGeoTransform[5];
     445           9 :     poDstDS->SetGeoTransform(adfDstGeoTransform.data());
     446             : 
     447           9 :     auto hTargetBand = poDstDS->GetRasterBand(1);
     448           9 :     if (hTargetBand == nullptr)
     449             :     {
     450           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot get band for %s",
     451             :                  oOpts.outputFilename.c_str());
     452           0 :         return false;
     453             :     }
     454             : 
     455           9 :     if (oOpts.nodataVal >= 0)
     456           1 :         GDALSetRasterNoDataValue(hTargetBand, oOpts.nodataVal);
     457             : 
     458             :     /* process first line */
     459           9 :     if (GDALRasterIO(hBand, GF_Read, nXStart, nY, nXSize, 1, padfFirstLineVal,
     460           9 :                      nXSize, 1, GDT_Float64, 0, 0))
     461             :     {
     462           0 :         CPLError(
     463             :             CE_Failure, CPLE_AppDefined,
     464             :             "RasterIO error when reading DEM at position(%d, %d), size(%d, %d)",
     465             :             nXStart, nY, nXSize, 1);
     466           0 :         return false;
     467             :     }
     468             : 
     469           9 :     const double dfZObserver = oOpts.observer.z + padfFirstLineVal[nX];
     470           9 :     double dfZ = 0.0;
     471           9 :     const double dfDistance2 = oOpts.maxDistance * oOpts.maxDistance;
     472             : 
     473             :     /* If we can't get a SemiMajor axis from the SRS, it will be
     474             :      * SRS_WGS84_SEMIMAJOR
     475             :      */
     476           9 :     double dfSphereDiameter(std::numeric_limits<double>::infinity());
     477           9 :     const OGRSpatialReference *poDstSRS = poDstDS->GetSpatialRef();
     478           9 :     if (poDstSRS)
     479             :     {
     480             :         OGRErr eSRSerr;
     481           7 :         double dfSemiMajor = poDstSRS->GetSemiMajor(&eSRSerr);
     482             : 
     483             :         /* If we fetched the axis from the SRS, use it */
     484           7 :         if (eSRSerr != OGRERR_FAILURE)
     485           7 :             dfSphereDiameter = dfSemiMajor * 2.0;
     486             :         else
     487           0 :             CPLDebug("GDALViewshedGenerate",
     488             :                      "Unable to fetch SemiMajor axis from spatial reference");
     489             :     }
     490             : 
     491             :     /* mark the observer point as visible */
     492           9 :     double dfGroundLevel = 0;
     493           9 :     if (oOpts.outputMode == OutputMode::DEM)
     494           1 :         dfGroundLevel = padfFirstLineVal[nX];
     495             : 
     496           9 :     pabyResult[nX] = oOpts.visibleVal;
     497             : 
     498             :     //ABELL - Do we care about this conditional?
     499           9 :     if (oOpts.outputMode != OutputMode::Normal)
     500           3 :         dfHeightResult[nX] = dfGroundLevel;
     501             : 
     502           9 :     dfGroundLevel = 0;
     503           9 :     if (nX > 0)
     504             :     {
     505           9 :         if (oOpts.outputMode == OutputMode::DEM)
     506           1 :             dfGroundLevel = padfFirstLineVal[nX - 1];
     507           9 :         CPL_IGNORE_RET_VAL(AdjustHeightInRange(
     508           9 :             adfGeoTransform.data(), 1, 0, padfFirstLineVal[nX - 1], dfDistance2,
     509             :             oOpts.curveCoeff, dfSphereDiameter));
     510           9 :         pabyResult[nX - 1] = oOpts.visibleVal;
     511           9 :         if (oOpts.outputMode != OutputMode::Normal)
     512           3 :             dfHeightResult[nX - 1] = dfGroundLevel;
     513             :     }
     514           9 :     if (nX < nXSize - 1)
     515             :     {
     516           9 :         if (oOpts.outputMode == OutputMode::DEM)
     517           1 :             dfGroundLevel = padfFirstLineVal[nX + 1];
     518           9 :         CPL_IGNORE_RET_VAL(AdjustHeightInRange(
     519           9 :             adfGeoTransform.data(), 1, 0, padfFirstLineVal[nX + 1], dfDistance2,
     520             :             oOpts.curveCoeff, dfSphereDiameter));
     521           9 :         pabyResult[nX + 1] = oOpts.visibleVal;
     522           9 :         if (oOpts.outputMode != OutputMode::Normal)
     523           3 :             dfHeightResult[nX + 1] = dfGroundLevel;
     524             :     }
     525             : 
     526             :     /* process left direction */
     527         337 :     for (int iPixel = nX - 2; iPixel >= 0; iPixel--)
     528             :     {
     529         328 :         dfGroundLevel = 0;
     530         328 :         if (oOpts.outputMode == OutputMode::DEM)
     531          50 :             dfGroundLevel = padfFirstLineVal[iPixel];
     532             : 
     533         328 :         if (AdjustHeightInRange(adfGeoTransform.data(), nX - iPixel, 0,
     534         328 :                                 padfFirstLineVal[iPixel], dfDistance2,
     535             :                                 oOpts.curveCoeff, dfSphereDiameter))
     536             :         {
     537         327 :             dfZ = CalcHeightLine(nX - iPixel, padfFirstLineVal[iPixel + 1],
     538             :                                  dfZObserver);
     539             : 
     540         327 :             if (oOpts.outputMode != OutputMode::Normal)
     541         150 :                 dfHeightResult[iPixel] = std::max(
     542         150 :                     0.0, (dfZ - padfFirstLineVal[iPixel] + dfGroundLevel));
     543             : 
     544         327 :             SetVisibility(iPixel, dfZ, oOpts.targetHeight, padfFirstLineVal,
     545         327 :                           vResult, oOpts.visibleVal, oOpts.invisibleVal);
     546             :         }
     547             :         else
     548             :         {
     549           2 :             for (; iPixel >= 0; iPixel--)
     550             :             {
     551           1 :                 pabyResult[iPixel] = oOpts.outOfRangeVal;
     552           1 :                 if (oOpts.outputMode != OutputMode::Normal)
     553           0 :                     dfHeightResult[iPixel] = oOpts.outOfRangeVal;
     554             :             }
     555             :         }
     556             :     }
     557             :     /* process right direction */
     558         337 :     for (int iPixel = nX + 2; iPixel < nXSize; iPixel++)
     559             :     {
     560         328 :         dfGroundLevel = 0;
     561         328 :         if (oOpts.outputMode == OutputMode::DEM)
     562          50 :             dfGroundLevel = padfFirstLineVal[iPixel];
     563         328 :         if (AdjustHeightInRange(adfGeoTransform.data(), iPixel - nX, 0,
     564         328 :                                 padfFirstLineVal[iPixel], dfDistance2,
     565             :                                 oOpts.curveCoeff, dfSphereDiameter))
     566             :         {
     567         327 :             dfZ = CalcHeightLine(iPixel - nX, padfFirstLineVal[iPixel - 1],
     568             :                                  dfZObserver);
     569             : 
     570         327 :             if (oOpts.outputMode != OutputMode::Normal)
     571         150 :                 dfHeightResult[iPixel] = std::max(
     572         150 :                     0.0, (dfZ - padfFirstLineVal[iPixel] + dfGroundLevel));
     573             : 
     574         327 :             SetVisibility(iPixel, dfZ, oOpts.targetHeight, padfFirstLineVal,
     575         327 :                           vResult, oOpts.visibleVal, oOpts.invisibleVal);
     576             :         }
     577             :         else
     578             :         {
     579           2 :             for (; iPixel < nXSize; iPixel++)
     580             :             {
     581           1 :                 pabyResult[iPixel] = oOpts.outOfRangeVal;
     582           1 :                 if (oOpts.outputMode != OutputMode::Normal)
     583           0 :                     dfHeightResult[iPixel] = oOpts.outOfRangeVal;
     584             :             }
     585             :         }
     586             :     }
     587             :     /* write result line */
     588             : 
     589             :     void *data;
     590             :     GDALDataType dataType;
     591           9 :     if (oOpts.outputMode == OutputMode::Normal)
     592             :     {
     593           6 :         data = static_cast<void *>(pabyResult);
     594           6 :         dataType = GDT_Byte;
     595             :     }
     596             :     else
     597             :     {
     598           3 :         data = static_cast<void *>(dfHeightResult);
     599           3 :         dataType = GDT_Float64;
     600             :     }
     601           9 :     if (GDALRasterIO(hTargetBand, GF_Write, 0, nY - nYStart, nXSize, 1, data,
     602           9 :                      nXSize, 1, dataType, 0, 0))
     603             :     {
     604           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     605             :                  "RasterIO error when writing target raster at position "
     606             :                  "(%d,%d), size (%d,%d)",
     607             :                  0, nY - nYStart, nXSize, 1);
     608           0 :         return false;
     609             :     }
     610             : 
     611             :     /* scan upwards */
     612           9 :     std::copy(vFirstLineVal.begin(), vFirstLineVal.end(), vLastLineVal.begin());
     613         458 :     for (int iLine = nY - 1; iLine >= nYStart; iLine--)
     614             :     {
     615         449 :         if (GDALRasterIO(hBand, GF_Read, nXStart, iLine, nXSize, 1,
     616         449 :                          padfThisLineVal, nXSize, 1, GDT_Float64, 0, 0))
     617             :         {
     618           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     619             :                      "RasterIO error when reading DEM at position (%d,%d), "
     620             :                      "size (%d,%d)",
     621             :                      nXStart, iLine, nXSize, 1);
     622           0 :             return false;
     623             :         }
     624             : 
     625             :         /* set up initial point on the scanline */
     626         449 :         dfGroundLevel = 0;
     627         449 :         if (oOpts.outputMode == OutputMode::DEM)
     628          70 :             dfGroundLevel = padfThisLineVal[nX];
     629         449 :         if (AdjustHeightInRange(adfGeoTransform.data(), 0, nY - iLine,
     630         449 :                                 padfThisLineVal[nX], dfDistance2,
     631             :                                 oOpts.curveCoeff, dfSphereDiameter))
     632             :         {
     633         448 :             dfZ = CalcHeightLine(nY - iLine, padfLastLineVal[nX], dfZObserver);
     634             : 
     635         448 :             if (oOpts.outputMode != OutputMode::Normal)
     636         210 :                 dfHeightResult[nX] =
     637         210 :                     std::max(0.0, (dfZ - padfThisLineVal[nX] + dfGroundLevel));
     638             : 
     639         448 :             SetVisibility(nX, dfZ, oOpts.targetHeight, padfThisLineVal, vResult,
     640         448 :                           oOpts.visibleVal, oOpts.invisibleVal);
     641             :         }
     642             :         else
     643             :         {
     644           1 :             pabyResult[nX] = oOpts.outOfRangeVal;
     645           1 :             if (oOpts.outputMode != OutputMode::Normal)
     646           0 :                 dfHeightResult[nX] = oOpts.outOfRangeVal;
     647             :         }
     648             : 
     649             :         /* process left direction */
     650       22361 :         for (int iPixel = nX - 1; iPixel >= 0; iPixel--)
     651             :         {
     652       21912 :             dfGroundLevel = 0;
     653       21912 :             if (oOpts.outputMode == OutputMode::DEM)
     654        3570 :                 dfGroundLevel = padfThisLineVal[iPixel];
     655       21912 :             if (AdjustHeightInRange(adfGeoTransform.data(), nX - iPixel,
     656       21912 :                                     nY - iLine, padfThisLineVal[iPixel],
     657             :                                     dfDistance2, oOpts.curveCoeff,
     658             :                                     dfSphereDiameter))
     659             :             {
     660       21887 :                 if (oOpts.cellMode != CellMode::Edge)
     661           0 :                     dfZ = CalcHeightDiagonal(
     662           0 :                         nX - iPixel, nY - iLine, padfThisLineVal[iPixel + 1],
     663           0 :                         padfLastLineVal[iPixel], dfZObserver);
     664             : 
     665       21887 :                 if (oOpts.cellMode != CellMode::Diagonal)
     666             :                 {
     667             :                     double dfZ2 =
     668       21887 :                         nX - iPixel >= nY - iLine
     669       21887 :                             ? CalcHeightEdge(nY - iLine, nX - iPixel,
     670        8202 :                                              padfLastLineVal[iPixel + 1],
     671        8202 :                                              padfThisLineVal[iPixel + 1],
     672             :                                              dfZObserver)
     673       13685 :                             : CalcHeightEdge(nX - iPixel, nY - iLine,
     674       13685 :                                              padfLastLineVal[iPixel + 1],
     675       13685 :                                              padfLastLineVal[iPixel],
     676       21887 :                                              dfZObserver);
     677       21887 :                     dfZ = CalcHeight(dfZ, dfZ2, oOpts.cellMode);
     678             :                 }
     679             : 
     680       21887 :                 if (oOpts.outputMode != OutputMode::Normal)
     681       10710 :                     dfHeightResult[iPixel] = std::max(
     682       10710 :                         0.0, (dfZ - padfThisLineVal[iPixel] + dfGroundLevel));
     683             : 
     684       21887 :                 SetVisibility(iPixel, dfZ, oOpts.targetHeight, padfThisLineVal,
     685       21887 :                               vResult, oOpts.visibleVal, oOpts.invisibleVal);
     686             :             }
     687             :             else
     688             :             {
     689         195 :                 for (; iPixel >= 0; iPixel--)
     690             :                 {
     691         170 :                     pabyResult[iPixel] = oOpts.outOfRangeVal;
     692         170 :                     if (oOpts.outputMode != OutputMode::Normal)
     693           0 :                         dfHeightResult[iPixel] = oOpts.outOfRangeVal;
     694             :                 }
     695             :             }
     696             :         }
     697             :         /* process right direction */
     698       22361 :         for (int iPixel = nX + 1; iPixel < nXSize; iPixel++)
     699             :         {
     700       21912 :             dfGroundLevel = 0;
     701       21912 :             if (oOpts.outputMode == OutputMode::DEM)
     702        3570 :                 dfGroundLevel = padfThisLineVal[iPixel];
     703             : 
     704       21912 :             if (AdjustHeightInRange(adfGeoTransform.data(), iPixel - nX,
     705       21912 :                                     nY - iLine, padfThisLineVal[iPixel],
     706             :                                     dfDistance2, oOpts.curveCoeff,
     707             :                                     dfSphereDiameter))
     708             :             {
     709       21887 :                 if (oOpts.cellMode != CellMode::Edge)
     710           0 :                     dfZ = CalcHeightDiagonal(
     711           0 :                         iPixel - nX, nY - iLine, padfThisLineVal[iPixel - 1],
     712           0 :                         padfLastLineVal[iPixel], dfZObserver);
     713       21887 :                 if (oOpts.cellMode != CellMode::Diagonal)
     714             :                 {
     715             :                     double dfZ2 =
     716       21887 :                         iPixel - nX >= nY - iLine
     717       21887 :                             ? CalcHeightEdge(nY - iLine, iPixel - nX,
     718        8202 :                                              padfLastLineVal[iPixel - 1],
     719        8202 :                                              padfThisLineVal[iPixel - 1],
     720             :                                              dfZObserver)
     721       13685 :                             : CalcHeightEdge(iPixel - nX, nY - iLine,
     722       13685 :                                              padfLastLineVal[iPixel - 1],
     723       13685 :                                              padfLastLineVal[iPixel],
     724       21887 :                                              dfZObserver);
     725       21887 :                     dfZ = CalcHeight(dfZ, dfZ2, oOpts.cellMode);
     726             :                 }
     727             : 
     728       21887 :                 if (oOpts.outputMode != OutputMode::Normal)
     729       10710 :                     dfHeightResult[iPixel] = std::max(
     730       10710 :                         0.0, (dfZ - padfThisLineVal[iPixel] + dfGroundLevel));
     731             : 
     732       21887 :                 SetVisibility(iPixel, dfZ, oOpts.targetHeight, padfThisLineVal,
     733       21887 :                               vResult, oOpts.visibleVal, oOpts.invisibleVal);
     734             :             }
     735             :             else
     736             :             {
     737         195 :                 for (; iPixel < nXSize; iPixel++)
     738             :                 {
     739         170 :                     pabyResult[iPixel] = oOpts.outOfRangeVal;
     740         170 :                     if (oOpts.outputMode != OutputMode::Normal)
     741           0 :                         dfHeightResult[iPixel] = oOpts.outOfRangeVal;
     742             :                 }
     743             :             }
     744             :         }
     745             : 
     746             :         /* write result line */
     747         449 :         if (GDALRasterIO(hTargetBand, GF_Write, 0, iLine - nYStart, nXSize, 1,
     748         449 :                          data, nXSize, 1, dataType, 0, 0))
     749             :         {
     750           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     751             :                      "RasterIO error when writing target raster at position "
     752             :                      "(%d,%d), size (%d,%d)",
     753             :                      0, iLine - nYStart, nXSize, 1);
     754           0 :             return false;
     755             :         }
     756             : 
     757         449 :         std::swap(padfLastLineVal, padfThisLineVal);
     758             : 
     759         449 :         if (!pfnProgress((nY - iLine) / static_cast<double>(nYSize), "",
     760             :                          pProgressArg))
     761             :         {
     762           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     763           0 :             return false;
     764             :         }
     765             :     }
     766             : 
     767             :     /* scan downwards */
     768           9 :     memcpy(padfLastLineVal, padfFirstLineVal, nXSize * sizeof(double));
     769         452 :     for (int iLine = nY + 1; iLine < nYStop; iLine++)
     770             :     {
     771         443 :         if (GDALRasterIO(hBand, GF_Read, nXStart, iLine, nXSize, 1,
     772         443 :                          padfThisLineVal, nXSize, 1, GDT_Float64, 0, 0))
     773             :         {
     774           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     775             :                      "RasterIO error when reading DEM at position (%d,%d), "
     776             :                      "size (%d,%d)",
     777             :                      nXStart, iLine, nXStop - nXStart, 1);
     778           0 :             return false;
     779             :         }
     780             : 
     781             :         /* set up initial point on the scanline */
     782         443 :         dfGroundLevel = 0;
     783         443 :         if (oOpts.outputMode == OutputMode::DEM)
     784          69 :             dfGroundLevel = padfThisLineVal[nX];
     785             : 
     786         443 :         if (AdjustHeightInRange(adfGeoTransform.data(), 0, iLine - nY,
     787         443 :                                 padfThisLineVal[nX], dfDistance2,
     788             :                                 oOpts.curveCoeff, dfSphereDiameter))
     789             :         {
     790         442 :             dfZ = CalcHeightLine(iLine - nY, padfLastLineVal[nX], dfZObserver);
     791             : 
     792         442 :             if (oOpts.outputMode != OutputMode::Normal)
     793         207 :                 dfHeightResult[nX] =
     794         207 :                     std::max(0.0, (dfZ - padfThisLineVal[nX] + dfGroundLevel));
     795             : 
     796         442 :             SetVisibility(nX, dfZ, oOpts.targetHeight, padfThisLineVal, vResult,
     797         442 :                           oOpts.visibleVal, oOpts.invisibleVal);
     798             :         }
     799             :         else
     800             :         {
     801           1 :             pabyResult[nX] = oOpts.outOfRangeVal;
     802           1 :             if (oOpts.outputMode != OutputMode::Normal)
     803           0 :                 dfHeightResult[nX] = oOpts.outOfRangeVal;
     804             :         }
     805             : 
     806             :         /* process left direction */
     807       22049 :         for (int iPixel = nX - 1; iPixel >= 0; iPixel--)
     808             :         {
     809       21606 :             dfGroundLevel = 0;
     810       21606 :             if (oOpts.outputMode == OutputMode::DEM)
     811        3519 :                 dfGroundLevel = padfThisLineVal[iPixel];
     812             : 
     813       21606 :             if (AdjustHeightInRange(adfGeoTransform.data(), nX - iPixel,
     814       21606 :                                     iLine - nY, padfThisLineVal[iPixel],
     815             :                                     dfDistance2, oOpts.curveCoeff,
     816             :                                     dfSphereDiameter))
     817             :             {
     818       21581 :                 if (oOpts.cellMode != CellMode::Edge)
     819           0 :                     dfZ = CalcHeightDiagonal(
     820           0 :                         nX - iPixel, iLine - nY, padfThisLineVal[iPixel + 1],
     821           0 :                         padfLastLineVal[iPixel], dfZObserver);
     822             : 
     823       21581 :                 if (oOpts.cellMode != CellMode::Diagonal)
     824             :                 {
     825             :                     double dfZ2 =
     826       21581 :                         nX - iPixel >= iLine - nY
     827       21581 :                             ? CalcHeightEdge(iLine - nY, nX - iPixel,
     828        8202 :                                              padfLastLineVal[iPixel + 1],
     829        8202 :                                              padfThisLineVal[iPixel + 1],
     830             :                                              dfZObserver)
     831       13379 :                             : CalcHeightEdge(nX - iPixel, iLine - nY,
     832       13379 :                                              padfLastLineVal[iPixel + 1],
     833       13379 :                                              padfLastLineVal[iPixel],
     834       21581 :                                              dfZObserver);
     835       21581 :                     dfZ = CalcHeight(dfZ, dfZ2, oOpts.cellMode);
     836             :                 }
     837             : 
     838       21581 :                 if (oOpts.outputMode != OutputMode::Normal)
     839       10557 :                     dfHeightResult[iPixel] = std::max(
     840       10557 :                         0.0, (dfZ - padfThisLineVal[iPixel] + dfGroundLevel));
     841             : 
     842       21581 :                 SetVisibility(iPixel, dfZ, oOpts.targetHeight, padfThisLineVal,
     843       21581 :                               vResult, oOpts.visibleVal, oOpts.invisibleVal);
     844             :             }
     845             :             else
     846             :             {
     847         195 :                 for (; iPixel >= 0; iPixel--)
     848             :                 {
     849         170 :                     pabyResult[iPixel] = oOpts.outOfRangeVal;
     850         170 :                     if (oOpts.outputMode != OutputMode::Normal)
     851           0 :                         dfHeightResult[iPixel] = oOpts.outOfRangeVal;
     852             :                 }
     853             :             }
     854             :         }
     855             :         /* process right direction */
     856       22049 :         for (int iPixel = nX + 1; iPixel < nXSize; iPixel++)
     857             :         {
     858       21606 :             dfGroundLevel = 0;
     859       21606 :             if (oOpts.outputMode == OutputMode::DEM)
     860        3519 :                 dfGroundLevel = padfThisLineVal[iPixel];
     861             : 
     862       21606 :             if (AdjustHeightInRange(adfGeoTransform.data(), iPixel - nX,
     863       21606 :                                     iLine - nY, padfThisLineVal[iPixel],
     864             :                                     dfDistance2, oOpts.curveCoeff,
     865             :                                     dfSphereDiameter))
     866             :             {
     867       21581 :                 if (oOpts.cellMode != CellMode::Edge)
     868           0 :                     dfZ = CalcHeightDiagonal(
     869           0 :                         iPixel - nX, iLine - nY, padfThisLineVal[iPixel - 1],
     870           0 :                         padfLastLineVal[iPixel], dfZObserver);
     871             : 
     872       21581 :                 if (oOpts.cellMode != CellMode::Diagonal)
     873             :                 {
     874             :                     double dfZ2 =
     875       21581 :                         iPixel - nX >= iLine - nY
     876       21581 :                             ? CalcHeightEdge(iLine - nY, iPixel - nX,
     877        8202 :                                              padfLastLineVal[iPixel - 1],
     878        8202 :                                              padfThisLineVal[iPixel - 1],
     879             :                                              dfZObserver)
     880       13379 :                             : CalcHeightEdge(iPixel - nX, iLine - nY,
     881       13379 :                                              padfLastLineVal[iPixel - 1],
     882       13379 :                                              padfLastLineVal[iPixel],
     883       21581 :                                              dfZObserver);
     884       21581 :                     dfZ = CalcHeight(dfZ, dfZ2, oOpts.cellMode);
     885             :                 }
     886             : 
     887       21581 :                 if (oOpts.outputMode != OutputMode::Normal)
     888       10557 :                     dfHeightResult[iPixel] = std::max(
     889       10557 :                         0.0, (dfZ - padfThisLineVal[iPixel] + dfGroundLevel));
     890             : 
     891       21581 :                 SetVisibility(iPixel, dfZ, oOpts.targetHeight, padfThisLineVal,
     892       21581 :                               vResult, oOpts.visibleVal, oOpts.invisibleVal);
     893             :             }
     894             :             else
     895             :             {
     896         195 :                 for (; iPixel < nXSize; iPixel++)
     897             :                 {
     898         170 :                     pabyResult[iPixel] = oOpts.outOfRangeVal;
     899         170 :                     if (oOpts.outputMode != OutputMode::Normal)
     900           0 :                         dfHeightResult[iPixel] = oOpts.outOfRangeVal;
     901             :                 }
     902             :             }
     903             :         }
     904             : 
     905             :         /* write result line */
     906         443 :         if (GDALRasterIO(hTargetBand, GF_Write, 0, iLine - nYStart, nXSize, 1,
     907         443 :                          data, nXSize, 1, dataType, 0, 0))
     908             :         {
     909           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     910             :                      "RasterIO error when writing target raster at position "
     911             :                      "(%d,%d), size (%d,%d)",
     912             :                      0, iLine - nYStart, nXSize, 1);
     913           0 :             return false;
     914             :         }
     915             : 
     916         443 :         std::swap(padfLastLineVal, padfThisLineVal);
     917             : 
     918         443 :         if (!pfnProgress((iLine - nYStart) / static_cast<double>(nYSize), "",
     919             :                          pProgressArg))
     920             :         {
     921           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     922           0 :             return false;
     923             :         }
     924             :     }
     925             : 
     926           9 :     if (!pfnProgress(1.0, "", pProgressArg))
     927             :     {
     928           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     929           0 :         return false;
     930             :     }
     931             : 
     932           9 :     return true;
     933             : }
     934             : 
     935             : }  // namespace gdal

Generated by: LCOV version 1.14