LCOV - code coverage report
Current view: top level - autotest/cpp - test_viewshed.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 477 485 98.4 %
Date: 2026-02-12 23:49:34 Functions: 65 65 100.0 %

          Line data    Source code
       1             : ///////////////////////////////////////////////////////////////////////////////
       2             : //
       3             : // Project:  C++ Test Suite for GDAL/OGR
       4             : // Purpose:  Test viewshed algorithm
       5             : // Author:   Andrew Bell
       6             : //
       7             : ///////////////////////////////////////////////////////////////////////////////
       8             : /*
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include <algorithm>
      13             : #include <array>
      14             : #include <utility>
      15             : 
      16             : #include <iomanip>
      17             : 
      18             : #include "gdal_unit_test.h"
      19             : 
      20             : #include "gtest_include.h"
      21             : 
      22             : #include "viewshed/viewshed.h"
      23             : 
      24             : namespace gdal
      25             : {
      26             : namespace viewshed
      27             : {
      28             : 
      29             : namespace
      30             : {
      31             : using Coord = std::pair<int, int>;
      32             : using DatasetPtr = std::unique_ptr<GDALDataset>;
      33             : using Transform = std::array<double, 6>;
      34             : Transform identity{0, 1, 0, 0, 0, 1};
      35             : 
      36          30 : Options stdOptions(int x, int y)
      37             : {
      38          30 :     Options opts;
      39          30 :     opts.observer.x = x;
      40          30 :     opts.observer.y = y;
      41          30 :     opts.outputFilename = "none";
      42          30 :     opts.outputFormat = "mem";
      43          30 :     opts.curveCoeff = 0;
      44             : 
      45          30 :     return opts;
      46             : }
      47             : 
      48           5 : Options stdOptions(const Coord &observer)
      49             : {
      50           5 :     return stdOptions(observer.first, observer.second);
      51             : }
      52             : 
      53          27 : DatasetPtr runViewshed(const int8_t *in, int xlen, int ylen,
      54             :                        const Options &opts)
      55             : {
      56          54 :     Viewshed v(opts);
      57             : 
      58          27 :     GDALDriver *driver = (GDALDriver *)GDALGetDriverByName("MEM");
      59          27 :     GDALDataset *dataset = driver->Create("", xlen, ylen, 1, GDT_Int8, nullptr);
      60          27 :     EXPECT_TRUE(dataset);
      61          27 :     dataset->SetGeoTransform(identity.data());
      62          27 :     GDALRasterBand *band = dataset->GetRasterBand(1);
      63          27 :     EXPECT_TRUE(band);
      64          27 :     CPLErr err = band->RasterIO(GF_Write, 0, 0, xlen, ylen, (int8_t *)in, xlen,
      65          27 :                                 ylen, GDT_Int8, 0, 0, nullptr);
      66          27 :     EXPECT_EQ(err, CE_None);
      67             : 
      68          27 :     EXPECT_TRUE(v.run(band));
      69          54 :     return v.output();
      70             : }
      71             : 
      72           8 : DatasetPtr runViewshed(const double *in, const double *sd, int xlen, int ylen,
      73             :                        const Options &opts)
      74             : {
      75          16 :     Viewshed v(opts);
      76             : 
      77           8 :     GDALDriver *driver = (GDALDriver *)GDALGetDriverByName("MEM");
      78             :     GDALDataset *dataset =
      79           8 :         driver->Create("", xlen, ylen, 2, GDT_Float32, nullptr);
      80           8 :     EXPECT_TRUE(dataset);
      81           8 :     dataset->SetGeoTransform(identity.data());
      82           8 :     GDALRasterBand *band = dataset->GetRasterBand(1);
      83           8 :     EXPECT_TRUE(band);
      84           8 :     CPLErr err = band->RasterIO(GF_Write, 0, 0, xlen, ylen, (void *)in, xlen,
      85           8 :                                 ylen, GDT_Float64, 0, 0, nullptr);
      86           8 :     EXPECT_EQ(err, CE_None);
      87           8 :     GDALRasterBand *sdBand = dataset->GetRasterBand(2);
      88           8 :     EXPECT_TRUE(sdBand);
      89           8 :     err = sdBand->RasterIO(GF_Write, 0, 0, xlen, ylen, (void *)sd, xlen, ylen,
      90             :                            GDT_Float64, 0, 0, nullptr);
      91           8 :     EXPECT_EQ(err, CE_None);
      92             : 
      93           8 :     EXPECT_TRUE(v.run(band, sdBand));
      94          16 :     return v.output();
      95             : }
      96             : 
      97             : }  // namespace
      98             : 
      99           4 : TEST(Viewshed, min_max_mask)
     100             : {
     101           1 :     const int xlen = 15;
     102           1 :     const int ylen = 15;
     103             :     std::array<int8_t, xlen * ylen> in;
     104           1 :     in.fill(0);
     105             : 
     106           2 :     SCOPED_TRACE("min_max_mask");
     107           2 :     Options opts(stdOptions(7, 7));
     108           1 :     opts.minDistance = 2;
     109           1 :     opts.maxDistance = 6;
     110             : 
     111           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     112             : 
     113             :     std::array<int8_t, xlen * ylen> out;
     114           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     115             : 
     116           1 :     int xOutLen = band->GetXSize();
     117           1 :     int yOutLen = band->GetYSize();
     118           1 :     EXPECT_EQ(xOutLen, 13);
     119           1 :     EXPECT_EQ(yOutLen, 13);
     120             : 
     121           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     122           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     123           1 :     EXPECT_EQ(err, CE_None);
     124             : 
     125             :     //clang-format off
     126           1 :     std::array<int8_t, 13 * 13> expected{
     127             :         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
     128             :         0,   0,   0,   0,   0,   0,   127, 0,   0,   0,   0,   0,   0,
     129             :         0,   0,   0,   127, 127, 127, 127, 127, 127, 127, 0,   0,   0,
     130             :         0,   0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,
     131             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     132             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     133             :         0,   127, 127, 127, 127, 0,   0,   0,   127, 127, 127, 127, 0,
     134             :         127, 127, 127, 127, 127, 0,   0,   0,   127, 127, 127, 127, 127,
     135             :         0,   127, 127, 127, 127, 0,   0,   0,   127, 127, 127, 127, 0,
     136             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     137             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     138             :         0,   0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,
     139             :         0,   0,   0,   127, 127, 127, 127, 127, 127, 127, 0,   0,   0};
     140             :     //clang-format on
     141             : 
     142           1 :     int8_t *o = out.data();
     143           1 :     int8_t *e = expected.data();
     144         170 :     for (size_t i = 0; i < 13 * 13; ++i)
     145         169 :         EXPECT_EQ(*e++, *o++);
     146             : 
     147             :     /**
     148             :     int8_t *p = out.data();
     149             :     for (int y = 0; y < yOutLen; ++y)
     150             :     {
     151             :         for (int x = 0; x < xOutLen; ++x)
     152             :         {
     153             :             char c;
     154             :             if (*p == 0)
     155             :                 c = '*';
     156             :             else if (*p == 127)
     157             :                 c = '.';
     158             :             else
     159             :                 c = '?';
     160             :             std::cerr << c;
     161             :             p++;
     162             :         }
     163             :         std::cerr << "\n";
     164             :     }
     165             :     std::cerr << "\n";
     166             :     **/
     167           1 : }
     168             : 
     169           4 : TEST(Viewshed, angle)
     170             : {
     171           1 :     const int xlen = 17;
     172           1 :     const int ylen = 17;
     173             :     std::array<int8_t, xlen * ylen> in;
     174           1 :     in.fill(0);
     175             : 
     176           2 :     SCOPED_TRACE("min_max_mask");
     177           2 :     Options opts(stdOptions(8, 8));
     178           1 :     opts.startAngle = 0;
     179           1 :     opts.endAngle = 30;
     180             : 
     181           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     182             : 
     183             :     std::array<int8_t, xlen * ylen> out;
     184           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     185             : 
     186           1 :     int xOutLen = band->GetXSize();
     187           1 :     int yOutLen = band->GetYSize();
     188           1 :     EXPECT_EQ(xOutLen, 6);
     189           1 :     EXPECT_EQ(yOutLen, 9);
     190           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     191           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     192           1 :     EXPECT_EQ(err, CE_None);
     193             : 
     194             :     // clang-format off
     195           1 :     std::array<int8_t, 6 * 9> expected{
     196             :         127, 127, 127, 127, 127, 127,
     197             :         127, 127, 127, 127, 127, 0,
     198             :         127, 127, 127, 127, 0,   0,
     199             :         127, 127, 127, 127, 0,   0,
     200             :         127, 127, 127, 0,   0,   0,
     201             :         127, 127, 127, 0,   0,   0,
     202             :         127, 127, 0,   0,   0,   0,
     203             :         127, 127, 0,   0,   0,   0,
     204             :         127, 0,   0,   0,   0,   0};
     205             :     // clang-format on
     206             : 
     207           1 :     int8_t *o = out.data();
     208           1 :     int8_t *e = expected.data();
     209          55 :     for (size_t i = 0; i < 6 * 9; ++i)
     210          54 :         EXPECT_EQ(*e++, *o++);
     211             : 
     212             :     /**
     213             :     int8_t *p = out.data();
     214             :     for (int y = 0; y < yOutLen; ++y)
     215             :     {
     216             :         for (int x = 0; x < xOutLen; ++x)
     217             :         {
     218             :             char c;
     219             :             if (*p == 0)
     220             :                 c = '*';
     221             :             else if (*p == 127)
     222             :                 c = '.';
     223             :             else
     224             :                 c = '?';
     225             :             std::cerr << c;
     226             :             p++;
     227             :         }
     228             :         std::cerr << "\n";
     229             :     }
     230             :     std::cerr << "\n";
     231             :     **/
     232           1 : }
     233             : 
     234           4 : TEST(Viewshed, angle2)
     235             : {
     236           1 :     const int xlen = 11;
     237           1 :     const int ylen = 11;
     238             :     std::array<int8_t, xlen * ylen> in;
     239           1 :     in.fill(0);
     240             : 
     241           2 :     SCOPED_TRACE("min_max_mask");
     242           2 :     Options opts(stdOptions(5, 5));
     243           1 :     opts.startAngle = 0;
     244           1 :     opts.endAngle = 300;
     245             : 
     246           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     247             : 
     248             :     std::array<int8_t, xlen * ylen> out;
     249           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     250             : 
     251           1 :     int xOutLen = band->GetXSize();
     252           1 :     int yOutLen = band->GetYSize();
     253           1 :     EXPECT_EQ(xOutLen, 11);
     254           1 :     EXPECT_EQ(yOutLen, 11);
     255           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     256           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     257           1 :     EXPECT_EQ(err, CE_None);
     258             : 
     259             :     // clang-format off
     260           1 :     std::array<int8_t, 11 * 11> expected{
     261             :         0,   0,   0,   0,   0,   127, 127, 127, 127, 127, 127,
     262             :         0,   0,   0,    0,  0,   127, 127, 127, 127, 127, 127,
     263             :         127, 0,   0,   0,   0,   127, 127, 127, 127, 127, 127,
     264             :         127, 127, 127, 0,   0,   127, 127, 127, 127, 127, 127,
     265             :         127, 127, 127, 127, 0,   127, 127, 127, 127, 127, 127,
     266             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     267             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     268             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     269             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     270             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     271             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
     272             :     };
     273             :     // clang-format on
     274             : 
     275           1 :     int8_t *o = out.data();
     276           1 :     int8_t *e = expected.data();
     277         122 :     for (size_t i = 0; i < 11 * 11; ++i)
     278         121 :         EXPECT_EQ(*e++, *o++);
     279           1 : }
     280             : 
     281           4 : TEST(Viewshed, high_mask)
     282             : {
     283           1 :     const int xlen = 15;
     284           1 :     const int ylen = 15;
     285             :     std::array<int8_t, xlen * ylen> in;
     286           1 :     in.fill(0);
     287           1 :     in[15 * 7 + 5] = 1;
     288           1 :     in[15 * 7 + 6] = 3;
     289           1 :     in[15 * 7 + 7] = 5;
     290           1 :     in[15 * 7 + 8] = 7;
     291           1 :     in[15 * 7 + 9] = 7;
     292           1 :     in[15 * 7 + 10] = 7;
     293           1 :     in[15 * 7 + 11] = 7;
     294           1 :     in[15 * 7 + 12] = 12;
     295           1 :     in[15 * 7 + 13] = 6;
     296           1 :     in[15 * 7 + 14] = 15;
     297             : 
     298           2 :     SCOPED_TRACE("high_mask");
     299           2 :     Options opts(stdOptions(3, 7));
     300             : 
     301           1 :     opts.highPitch = 45;
     302           1 :     opts.outOfRangeVal = 2;
     303           1 :     opts.visibleVal = 1;
     304           1 :     opts.invisibleVal = 0;
     305             : 
     306           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     307             : 
     308             :     std::array<int8_t, xlen * ylen> out;
     309           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     310             : 
     311           1 :     int xOutLen = band->GetXSize();
     312           1 :     int yOutLen = band->GetYSize();
     313           1 :     EXPECT_EQ(xOutLen, 15);
     314           1 :     EXPECT_EQ(yOutLen, 15);
     315           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     316           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     317           1 :     EXPECT_EQ(err, CE_None);
     318             : 
     319             :     // clang-format off
     320           1 :     std::array<int8_t, 15 * 15> expected{
     321             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
     322             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
     323             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
     324             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
     325             :         1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
     326             :         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
     327             :         1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     328             :         1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 2, 0, 2,
     329             :         1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     330             :         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
     331             :         1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
     332             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
     333             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
     334             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
     335             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
     336             :     };
     337             :     // clang-format on
     338             : 
     339           1 :     int8_t *o = out.data();
     340           1 :     int8_t *e = expected.data();
     341          16 :     for (int y = 0; y < 15; ++y)
     342             :     {
     343         240 :         for (int x = 0; x < 15; ++x)
     344             :         {
     345         225 :             EXPECT_EQ(*e, *o) << "X/Y exp/act = " << x << "/" << y << "/"
     346           0 :                               << (int)*e << "/" << (int)*o << "!\n";
     347         225 :             e++;
     348         225 :             o++;
     349             :         }
     350             :     }
     351           1 : }
     352             : 
     353           4 : TEST(Viewshed, low_mask)
     354             : {
     355           1 :     const int xlen = 5;
     356           1 :     const int ylen = 5;
     357             :     std::array<int8_t, xlen * ylen> in;
     358           1 :     in.fill(0);
     359           1 :     in[12] = 5;
     360             : 
     361           2 :     SCOPED_TRACE("low_mask");
     362           2 :     Options opts(stdOptions(2, 2));
     363             : 
     364           1 :     opts.lowPitch = -45;
     365           1 :     opts.outputMode = OutputMode::DEM;
     366             : 
     367           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     368             : 
     369             :     std::array<double, xlen * ylen> out;
     370           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     371             : 
     372           1 :     int xOutLen = band->GetXSize();
     373           1 :     int yOutLen = band->GetYSize();
     374           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     375           1 :                                 xOutLen, yOutLen, GDT_Float64, 0, 0, nullptr);
     376           1 :     EXPECT_EQ(err, CE_None);
     377             : 
     378           1 :     std::array<double, 5 * 5> expected{2.17157, 2.76393, 3, 2.76393, 2.17157,
     379             :                                        2.76393, 3.58579, 4, 3.58579, 2.76393,
     380             :                                        3,       4,       5, 4,       3,
     381             :                                        2.76393, 3.58579, 4, 3.58579, 2.76393,
     382             :                                        2.17157, 2.76393, 3, 2.76393, 2.17157};
     383             : 
     384           1 :     const double *o = out.data();
     385           1 :     const double *e = expected.data();
     386          26 :     for (size_t i = 0; i < expected.size(); ++i)
     387             :     {
     388          25 :         EXPECT_NEAR(*o, *e, .00001);
     389          25 :         o++;
     390          25 :         e++;
     391             :     }
     392           1 : }
     393             : 
     394           4 : TEST(Viewshed, all_visible)
     395             : {
     396             :     // clang-format off
     397           1 :     const int xlen = 3;
     398           1 :     const int ylen = 3;
     399           1 :     std::array<int8_t, xlen * ylen> in
     400             :     {
     401             :         1, 2, 3,
     402             :         4, 5, 6,
     403             :         3, 2, 1
     404             :     };
     405             :     // clang-format on
     406             : 
     407           2 :     SCOPED_TRACE("all_visible");
     408           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, stdOptions(1, 1));
     409             : 
     410             :     std::array<uint8_t, xlen * ylen> out;
     411           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     412           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     413           1 :                                 ylen, GDT_Int8, 0, 0, nullptr);
     414           1 :     EXPECT_EQ(err, CE_None);
     415             : 
     416          10 :     for (uint8_t o : out)
     417           9 :         EXPECT_EQ(o, 127);
     418           1 : }
     419             : 
     420           4 : TEST(Viewshed, simple_height)
     421             : {
     422             :     // clang-format off
     423           1 :     const int xlen = 5;
     424           1 :     const int ylen = 5;
     425           1 :     std::array<int8_t, xlen * ylen> in
     426             :     {
     427             :         -1, 0, 1, 0, -1,
     428             :         -1, 2, 0, 4, -1,
     429             :         -1, 1, 0, -1, -1,
     430             :          0, 3, 0, 2, 0,
     431             :         -1, 0, 0, 3, -1
     432             :     };
     433             : 
     434           1 :     std::array<double, xlen * ylen> observable
     435             :     {
     436             :         4, 2, 1, 4, 8,
     437             :         3, 2, 0, 4, 3,
     438             :         2, 1, 0, -1, -1,
     439             :         4, 3, 0, 2, 1,
     440             :         6, 3, 0, 3, 4
     441             :     };
     442             :     // clang-format on
     443             : 
     444             :     {
     445           2 :         SCOPED_TRACE("simple_height:normal");
     446             : 
     447             :         DatasetPtr output =
     448           2 :             runViewshed(in.data(), xlen, ylen, stdOptions(2, 2));
     449             : 
     450             :         std::array<int8_t, xlen * ylen> out;
     451           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     452           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     453           1 :                                     ylen, GDT_Int8, 0, 0, nullptr);
     454           1 :         EXPECT_EQ(err, CE_None);
     455             : 
     456             :         // We expect the cell to be observable when the input is higher than the observable
     457             :         // height.
     458             :         std::array<int8_t, xlen * ylen> expected;
     459          26 :         for (size_t i = 0; i < in.size(); ++i)
     460          25 :             expected[i] = (in[i] >= observable[i] ? 127 : 0);
     461             : 
     462           1 :         EXPECT_EQ(expected, out);
     463             :     }
     464             : 
     465             :     {
     466             :         std::array<double, xlen * ylen> dem;
     467           2 :         SCOPED_TRACE("simple_height:dem");
     468           2 :         Options opts = stdOptions(2, 2);
     469           1 :         opts.outputMode = OutputMode::DEM;
     470             : 
     471           2 :         DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     472             : 
     473           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     474           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, dem.data(), xlen,
     475           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     476           1 :         EXPECT_EQ(err, CE_None);
     477             : 
     478           1 :         std::array<double, xlen * ylen> expected = observable;
     479             :         // Double equality is fine here as all the values are small integers.
     480           1 :         EXPECT_EQ(dem, expected);
     481             :     }
     482             : 
     483             :     {
     484             :         std::array<double, xlen * ylen> ground;
     485           2 :         SCOPED_TRACE("simple_height:ground");
     486           2 :         Options opts = stdOptions(2, 2);
     487           1 :         opts.outputMode = OutputMode::Ground;
     488           2 :         DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     489             : 
     490           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     491           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, ground.data(),
     492           1 :                                     xlen, ylen, GDT_Float64, 0, 0, nullptr);
     493           1 :         EXPECT_EQ(err, CE_None);
     494             : 
     495             :         std::array<double, xlen * ylen> expected;
     496          26 :         for (size_t i = 0; i < expected.size(); ++i)
     497          25 :             expected[i] = std::max(0.0, observable[i] - in[i]);
     498             : 
     499             :         // Double equality is fine here as all the values are small integers.
     500           1 :         EXPECT_EQ(expected, ground);
     501             :     }
     502           1 : }
     503             : 
     504             : // Addresses cases in #9501
     505           4 : TEST(Viewshed, dem_vs_ground)
     506             : {
     507             :     // Run gdal_viewshed on the input 8 x 1 array in both ground and dem mode and
     508             :     // verify the results are what are expected.
     509           5 :     auto run = [](const std::array<int8_t, 8> &in, Coord observer,
     510             :                   const std::array<double, 8> &ground,
     511             :                   const std::array<double, 8> &dem)
     512             :     {
     513           5 :         const int xlen = 8;
     514           5 :         const int ylen = 1;
     515             : 
     516             :         std::array<double, xlen * ylen> out;
     517          10 :         Options opts = stdOptions(observer);
     518           5 :         opts.outputMode = OutputMode::Ground;
     519             : 
     520             :         // Verify ground mode.
     521          10 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     522           5 :         GDALRasterBand *band = ds->GetRasterBand(1);
     523           5 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     524           5 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     525           5 :         EXPECT_EQ(err, CE_None);
     526          45 :         for (size_t i = 0; i < ground.size(); ++i)
     527          40 :             EXPECT_DOUBLE_EQ(out[i], ground[i]);
     528             : 
     529             :         // Verify DEM mode.
     530           5 :         opts.outputMode = OutputMode::DEM;
     531           5 :         ds = runViewshed(in.data(), xlen, ylen, opts);
     532           5 :         band = ds->GetRasterBand(1);
     533           5 :         err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen, ylen,
     534             :                              GDT_Float64, 0, 0, nullptr);
     535           5 :         EXPECT_EQ(err, CE_None);
     536          45 :         for (size_t i = 0; i < dem.size(); ++i)
     537          40 :             EXPECT_DOUBLE_EQ(out[i], dem[i]);
     538           5 :     };
     539             : 
     540             :     // Input / Observer / Minimum expected above ground / Minimum expected  above zero (DEM)
     541           1 :     run({0, 0, 0, 1, 0, 0, 0, 0}, {2, 0}, {0, 0, 0, 0, 2, 3, 4, 5},
     542             :         {0, 0, 0, 1, 2, 3, 4, 5});
     543           1 :     run({1, 1, 0, 1, 0, 1, 2, 2}, {3, 0}, {0, 0, 0, 0, 0, 0, 0, 1 / 3.0},
     544             :         {1, 1, 0, 1, 0, 1, 2, 7 / 3.0});
     545           1 :     run({0, 0, 0, 1, 1, 0, 0, 0}, {0, 0},
     546             :         {0, 0, 0, 0, 1 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0},
     547             :         {0, 0, 0, 1, 4 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0});
     548           1 :     run({0, 0, 1, 2, 3, 4, 5, 6}, {0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
     549             :         {0, 0, 1, 2, 3, 4, 5, 6});
     550           1 :     run({0, 0, 1, 1, 3, 4, 5, 4}, {0, 0}, {0, 0, 0, .5, 0, 0, 0, 11 / 6.0},
     551             :         {0, 0, 1, 1.5, 3, 4, 5, 35 / 6.0});
     552           1 : }
     553             : 
     554             : // Test an observer to the right of the raster.
     555           4 : TEST(Viewshed, oor_right)
     556             : {
     557             :     // clang-format off
     558           1 :     const int xlen = 5;
     559           1 :     const int ylen = 3;
     560           1 :     std::array<int8_t, xlen * ylen> in
     561             :     {
     562             :         1, 2, 0, 4, 1,
     563             :         0, 0, 2, 1, 0,
     564             :         1, 0, 0, 3, 3
     565             :     };
     566             :     // clang-format on
     567             : 
     568             :     {
     569           2 :         Options opts = stdOptions(6, 1);
     570           1 :         opts.outputMode = OutputMode::DEM;
     571           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     572           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     573             :         std::array<double, xlen * ylen> out;
     574           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     575           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     576           1 :         EXPECT_EQ(err, CE_None);
     577             : 
     578             :         // clang-format off
     579           1 :         std::array<double, xlen * ylen> expected
     580             :         {
     581             :             16 / 3.0, 29 / 6.0, 13 / 3.0, 4, 1,
     582             :             3,        2.5,      2,  1, 0,
     583             :             13 / 3.0, 23 / 6.0, 10 / 3.0, 3, 3
     584             :         };
     585             :         // clang-format on
     586             : 
     587          16 :         for (size_t i = 0; i < out.size(); ++i)
     588          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     589             :     }
     590             : 
     591             :     {
     592           2 :         Options opts = stdOptions(6, 2);
     593           1 :         opts.outputMode = OutputMode::DEM;
     594           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     595           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     596             :         std::array<double, xlen * ylen> out;
     597           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     598           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     599           1 :         EXPECT_EQ(err, CE_None);
     600             : 
     601             :         // clang-format off
     602           1 :         std::array<double, xlen * ylen> expected
     603             :         {
     604             :             26 / 5.0, 17 / 4.0, 11 / 3.0, 4,  1,
     605             :             6,        4.5,      3,        1.5, 0,
     606             :             9,        7.5,      6,        4.5, 3
     607             :         };
     608             :         // clang-format on
     609             : 
     610          16 :         for (size_t i = 0; i < out.size(); ++i)
     611          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     612             :     }
     613           1 : }
     614             : 
     615             : // Test an observer to the left of the raster.
     616           4 : TEST(Viewshed, oor_left)
     617             : {
     618             :     // clang-format off
     619           1 :     const int xlen = 5;
     620           1 :     const int ylen = 3;
     621           1 :     std::array<int8_t, xlen * ylen> in
     622             :     {
     623             :         1, 2, 0, 4, 1,
     624             :         0, 0, 2, 1, 0,
     625             :         1, 0, 0, 3, 3
     626             :     };
     627             :     // clang-format on
     628             : 
     629             :     {
     630           2 :         Options opts = stdOptions(-2, 1);
     631           1 :         opts.outputMode = OutputMode::DEM;
     632           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     633           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     634             :         std::array<double, xlen * ylen> out;
     635           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     636           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     637           1 :         EXPECT_EQ(err, CE_None);
     638             : 
     639             :         // clang-format off
     640           1 :         std::array<double, xlen * ylen> expected
     641             :         {
     642             :             1, 2, 2, 4, 4.5,
     643             :             0, 0, 2, 2.5, 3,
     644             :             1, 1, 1, 3, 3.5
     645             :         };
     646             :         // clang-format on
     647             : 
     648          16 :         for (size_t i = 0; i < out.size(); ++i)
     649          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     650             :     }
     651             : 
     652             :     {
     653           2 :         Options opts = stdOptions(-2, 2);
     654           1 :         opts.outputMode = OutputMode::DEM;
     655           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     656           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     657             :         std::array<double, xlen * ylen> out;
     658           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     659           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     660           1 :         EXPECT_EQ(err, CE_None);
     661             : 
     662             :         // clang-format off
     663           1 :         std::array<double, xlen * ylen> expected
     664             :         {
     665             :             1, 2,   5 / 3.0, 4,   4.2,
     666             :             0, .5,  2,       2.5, 3.1,
     667             :             1, 1.5, 2,       3,   3.6
     668             :         };
     669             :         // clang-format on
     670             : 
     671          16 :         for (size_t i = 0; i < out.size(); ++i)
     672          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     673             :     }
     674           1 : }
     675             : 
     676             : // Test an observer above the raster
     677           4 : TEST(Viewshed, oor_above)
     678             : {
     679             :     // clang-format off
     680           1 :     const int xlen = 5;
     681           1 :     const int ylen = 3;
     682           1 :     std::array<int8_t, xlen * ylen> in
     683             :     {
     684             :         1, 2, 0, 4, 1,
     685             :         0, 0, 2, 1, 0,
     686             :         1, 0, 0, 3, 3
     687             :     };
     688             :     // clang-format on
     689             : 
     690             :     {
     691           2 :         Options opts = stdOptions(2, -2);
     692           1 :         opts.outputMode = OutputMode::DEM;
     693           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     694           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     695             :         std::array<double, xlen * ylen> out;
     696           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     697           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     698             : 
     699           1 :         EXPECT_EQ(err, CE_None);
     700             : 
     701             :         // clang-format off
     702           1 :         std::array<double, xlen * ylen> expected
     703             :         {
     704             :             1,   2,       0,       4,        1,
     705             :             2.5, 2,       2,       4,        4.5,
     706             :             3,   8 / 3.0, 8 / 3.0, 14 / 3.0, 17 / 3.0
     707             :         };
     708             :         // clang-format on
     709             : 
     710          16 :         for (size_t i = 0; i < out.size(); ++i)
     711          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     712             :     }
     713             : 
     714             :     {
     715           2 :         Options opts = stdOptions(-2, -2);
     716           1 :         opts.outputMode = OutputMode::DEM;
     717           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     718           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     719             :         std::array<double, xlen * ylen> out;
     720           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     721           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     722           1 :         EXPECT_EQ(err, CE_None);
     723             : 
     724             :         // clang-format off
     725           1 :         std::array<double, xlen * ylen> expected
     726             :         {
     727             :             1, 2,   0,   4,    1,
     728             :             0, 1.5, 2.5, 1.25, 3.15,
     729             :             1, 0.5, 2,   3,    3
     730             :         };
     731             :         // clang-format on
     732             : 
     733          16 :         for (size_t i = 0; i < out.size(); ++i)
     734          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     735             :     }
     736           1 : }
     737             : 
     738             : // Test an observer below the raster
     739           4 : TEST(Viewshed, oor_below)
     740             : {
     741             :     // clang-format off
     742           1 :     const int xlen = 5;
     743           1 :     const int ylen = 3;
     744           1 :     std::array<int8_t, xlen * ylen> in
     745             :     {
     746             :         1, 2, 0, 4, 1,
     747             :         0, 0, 2, 1, 0,
     748             :         1, 0, 0, 3, 3
     749             :     };
     750             :     // clang-format on
     751             : 
     752             :     {
     753           2 :         Options opts = stdOptions(2, 4);
     754           1 :         opts.outputMode = OutputMode::DEM;
     755           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     756           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     757             :         std::array<double, xlen * ylen> out;
     758           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     759           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     760             : 
     761           1 :         EXPECT_EQ(err, CE_None);
     762             : 
     763             :         // clang-format off
     764           1 :         std::array<double, xlen * ylen> expected
     765             :         {
     766             :             1,    2,  8 / 3.0,  4,  5,
     767             :             0.5,  0,  2,        3,  4.5,
     768             :             1,    0,  0,        3,  3
     769             :         };
     770             :         // clang-format on
     771             : 
     772          16 :         for (size_t i = 0; i < out.size(); ++i)
     773          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     774             :     }
     775             : 
     776             :     {
     777           2 :         Options opts = stdOptions(6, 4);
     778           1 :         opts.outputMode = OutputMode::DEM;
     779           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     780           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     781             :         std::array<double, xlen * ylen> out;
     782           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     783           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     784           1 :         EXPECT_EQ(err, CE_None);
     785             : 
     786             :         // clang-format off
     787           1 :         std::array<double, xlen * ylen> expected
     788             :         {
     789             :             4.2,  6,    6,   4,   1,
     790             :             1.35, 2.25, 4.5, 4.5, 0,
     791             :             1,    0,    0,   3,   3
     792             :         };
     793             :         // clang-format on
     794             : 
     795          16 :         for (size_t i = 0; i < out.size(); ++i)
     796          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     797             :     }
     798           1 : }
     799             : 
     800             : // Test a handling of SD raster right and left.
     801           4 : TEST(Viewshed, sd)
     802             : {
     803             :     {
     804             :         // clang-format off
     805           1 :         const int xlen = 8;
     806           1 :         const int ylen = 1;
     807           1 :         std::array<double, xlen * ylen> in
     808             :         {
     809             :             0, 1, 1, 3.1, 1.5, 2.7, 3.7, 7.5
     810             :         };
     811             : 
     812           1 :         std::array<double, xlen * ylen> sd
     813             :         {
     814             :             1, 100, .1, 100, .1, .1, 100, .1
     815             :         };
     816             :         // clang-format on
     817             : 
     818             :         {
     819           2 :             Options opts = stdOptions(0, 0);
     820           1 :             opts.outputMode = OutputMode::Normal;
     821           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
     822           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
     823             :             std::array<double, xlen * ylen> out;
     824           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
     825           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
     826             : 
     827           1 :             EXPECT_EQ(err, CE_None);
     828             : 
     829             :             // clang-format off
     830           1 :             std::array<double, xlen * ylen> expected
     831             :             {
     832             :                 255, 255, 2, 255, 0, 2, 2, 255
     833             :             };
     834             :             // clang-format on
     835             : 
     836           9 :             for (size_t i = 0; i < out.size(); ++i)
     837           8 :                 EXPECT_DOUBLE_EQ(out[i], expected[i])
     838           0 :                     << "Error at position " << i << ".";
     839             :         }
     840             :     }
     841             :     {
     842             :         // clang-format off
     843           1 :         const int xlen = 8;
     844           1 :         const int ylen = 1;
     845           1 :         std::array<double, xlen * ylen> in
     846             :         {
     847             :             7.5, 3.7, 2.7, 1.5, 3.1, 1, 1, 0
     848             :         };
     849             : 
     850           1 :         std::array<double, xlen * ylen> sd
     851             :         {
     852             :             .1, 100, .1, .1, 100, .1, 100, 1
     853             :         };
     854             :         // clang-format on
     855             : 
     856             :         {
     857           2 :             Options opts = stdOptions(7, 0);
     858           1 :             opts.outputMode = OutputMode::Normal;
     859           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
     860           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
     861             :             std::array<double, xlen * ylen> out;
     862           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
     863           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
     864             : 
     865           1 :             EXPECT_EQ(err, CE_None);
     866             : 
     867             :             // clang-format off
     868           1 :             std::array<double, xlen * ylen> expected
     869             :             {
     870             :                 255, 2, 2, 0, 255, 2, 255, 255
     871             :             };
     872             :             // clang-format on
     873             : 
     874           9 :             for (size_t i = 0; i < out.size(); ++i)
     875           8 :                 EXPECT_DOUBLE_EQ(out[i], expected[i])
     876           0 :                     << "Error at position " << i << ".";
     877             :         }
     878             :     }
     879           1 : }
     880             : 
     881             : // Test a handling of SD raster up and down.
     882           4 : TEST(Viewshed, sd_up_down)
     883             : {
     884             :     // Up.
     885             :     {
     886             :         // clang-format off
     887           1 :         const int xlen = 1;
     888           1 :         const int ylen = 8;
     889           1 :         std::array<double, xlen * ylen> in
     890             :         {
     891             :             0, 1, 1, 3.1, 1.5, 2.7, 3.7, 7.5
     892             :         };
     893             : 
     894           1 :         std::array<double, xlen * ylen> sd
     895             :         {
     896             :             1, 100, .1, 100, .1, .1, 100, .1
     897             :         };
     898             :         // clang-format on
     899             : 
     900             :         {
     901           2 :             Options opts = stdOptions(0, 0);
     902           1 :             opts.outputMode = OutputMode::Normal;
     903           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
     904           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
     905             :             std::array<double, xlen * ylen> out;
     906           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
     907           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
     908             : 
     909           1 :             EXPECT_EQ(err, CE_None);
     910             : 
     911             :             // clang-format off
     912           1 :             std::array<double, xlen * ylen> expected
     913             :             {
     914             :                 255, 255, 2, 255, 0, 2, 2, 255
     915             :             };
     916             :             // clang-format on
     917             : 
     918           9 :             for (size_t i = 0; i < out.size(); ++i)
     919           8 :                 EXPECT_DOUBLE_EQ(out[i], expected[i])
     920           0 :                     << "Error at position " << i << ".";
     921             :         }
     922             :     }
     923             :     // Down.
     924             :     {
     925             :         // clang-format off
     926           1 :         const int xlen = 1;
     927           1 :         const int ylen = 8;
     928           1 :         std::array<double, xlen * ylen> in
     929             :         {
     930             :             7.5, 3.7, 2.7, 1.5, 3.1, 1, 1, 0
     931             :         };
     932             : 
     933           1 :         std::array<double, xlen * ylen> sd
     934             :         {
     935             :             .1, 100, .1, .1, 100, .1, 100, 1
     936             :         };
     937             :         // clang-format on
     938             : 
     939             :         {
     940           2 :             Options opts = stdOptions(0, 7);
     941           1 :             opts.outputMode = OutputMode::Normal;
     942           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
     943           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
     944             :             std::array<double, xlen * ylen> out;
     945           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
     946           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
     947             : 
     948           1 :             EXPECT_EQ(err, CE_None);
     949             : 
     950             :             // clang-format off
     951           1 :             std::array<double, xlen * ylen> expected
     952             :             {
     953             :                 255, 2, 2, 0, 255, 2, 255, 255
     954             :             };
     955             :             // clang-format on
     956             : 
     957           9 :             for (size_t i = 0; i < out.size(); ++i)
     958           8 :                 EXPECT_DOUBLE_EQ(out[i], expected[i]);
     959             :         }
     960             :     }
     961           1 : }
     962             : 
     963             : // Test SD raster
     964           4 : TEST(Viewshed, sd_2)
     965             : {
     966             :     // Right, down
     967             :     {
     968             :         // clang-format off
     969           1 :         const int xlen = 8;
     970           1 :         const int ylen = 2;
     971           1 :         std::array<double, xlen * ylen> in
     972             :         {
     973             :             0, 1,   1,   3.1, 1.5, 2.7, 3.7, 7.5,  // Row 0
     974             :             0, 1.1, 1.4, 3.1, 1.5, 2.7, 3.7, 7.5   // Row 1
     975             :         };
     976             : 
     977           1 :         std::array<double, xlen * ylen> sd
     978             :         {
     979             :             1, 100, .1, 100, .1, .1, 100, .1,  // Row 0
     980             :             1, 100, .1, 100, .1, .1, 100, .1   // Row 1
     981             :         };
     982             :         // clang-format on
     983             : 
     984             :         {
     985           2 :             Options opts = stdOptions(0, 0);
     986           1 :             opts.outputMode = OutputMode::Normal;
     987           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
     988           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
     989             :             std::array<double, xlen * ylen> out;
     990           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
     991           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
     992             : 
     993           1 :             EXPECT_EQ(err, CE_None);
     994             : 
     995             :             // clang-format off
     996           1 :             std::array<double, xlen * ylen> expected
     997             :             {
     998             :                 255, 255, 2, 255, 0, 2, 2, 255,
     999             :                 255, 255, 2, 2,   0, 0, 2, 255
    1000             :             };
    1001             :             // clang-format on
    1002             : 
    1003           1 :             size_t i = 0;
    1004           3 :             for (int y = 0; y < ylen; y++)
    1005          18 :                 for (int x = 0; x < xlen; x++)
    1006             :                 {
    1007          16 :                     EXPECT_DOUBLE_EQ(out[i], expected[i])
    1008           0 :                         << "Mismatch at (" << x << ", " << y << ")";
    1009          16 :                     i++;
    1010             :                 }
    1011             :         }
    1012             :     }
    1013             :     // Right, up
    1014             :     {
    1015             :         // clang-format off
    1016           1 :         const int xlen = 8;
    1017           1 :         const int ylen = 2;
    1018           1 :         std::array<double, xlen * ylen> in
    1019             :         {
    1020             :             0, 1.1, 1.4, 3.1, 1.5, 2.7, 3.7, 7.5,  // Row 0
    1021             :             0, 1,   1,   3.1, 1.5, 2.7, 3.7, 7.5   // Row 1
    1022             :         };
    1023             : 
    1024           1 :         std::array<double, xlen * ylen> sd
    1025             :         {
    1026             :             1, 100, .1, 100, .1, .1, 100, .1,  // Row 0
    1027             :             1, 100, .1, 100, .1, .1, 100, .1   // Row 1
    1028             :         };
    1029             :         // clang-format on
    1030             : 
    1031             :         {
    1032           2 :             Options opts = stdOptions(0, 1);
    1033           1 :             opts.outputMode = OutputMode::Normal;
    1034           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
    1035           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
    1036             :             std::array<double, xlen * ylen> out;
    1037           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
    1038           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
    1039             : 
    1040           1 :             EXPECT_EQ(err, CE_None);
    1041             : 
    1042             :             // clang-format off
    1043           1 :             std::array<double, xlen * ylen> expected
    1044             :             {
    1045             :                 255, 255, 2, 2,   0, 0, 2, 255,
    1046             :                 255, 255, 2, 255, 0, 2, 2, 255
    1047             :             };
    1048             :             // clang-format on
    1049             : 
    1050           1 :             size_t i = 0;
    1051           3 :             for (int y = 0; y < ylen; y++)
    1052          18 :                 for (int x = 0; x < xlen; x++)
    1053             :                 {
    1054          16 :                     EXPECT_DOUBLE_EQ(out[i], expected[i])
    1055           0 :                         << "Mismatch at (" << x << ", " << y << ")";
    1056          16 :                     i++;
    1057             :                 }
    1058             :         }
    1059             :     }
    1060             : 
    1061             :     // Left, down
    1062             :     {
    1063             :         // clang-format off
    1064           1 :         const int xlen = 8;
    1065           1 :         const int ylen = 2;
    1066           1 :         std::array<double, xlen * ylen> in
    1067             :         {
    1068             :             7.5, 3.7, 2.7, 1.5, 3.1, 1,   1,   0, // Row 0
    1069             :             7.5, 3.7, 2.7, 1.5, 3.1, 1.4, 1.1, 0  // Row 1
    1070             :         };
    1071             : 
    1072           1 :         std::array<double, xlen * ylen> sd
    1073             :         {
    1074             :             .1, 100, .1, .1, 100, .1, 100, 1,  // Row 0
    1075             :             .1, 100, .1, .1, 100, .1, 100, 1   // Row 1
    1076             :         };
    1077             :         // clang-format on
    1078             : 
    1079             :         {
    1080           2 :             Options opts = stdOptions(7, 0);
    1081           1 :             opts.outputMode = OutputMode::Normal;
    1082           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
    1083           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
    1084             :             std::array<double, xlen * ylen> out;
    1085           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
    1086           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
    1087             : 
    1088           1 :             EXPECT_EQ(err, CE_None);
    1089             : 
    1090             :             // clang-format off
    1091           1 :             std::array<double, xlen * ylen> expected
    1092             :             {
    1093             :                 255, 2, 2, 0, 255, 2, 255, 255,  // Row 0
    1094             :                 255, 2, 0, 0, 2,   2, 255, 255   // Row 1
    1095             :             };
    1096             :             // clang-format on
    1097             : 
    1098           1 :             size_t i = 0;
    1099           3 :             for (int y = 0; y < ylen; y++)
    1100          18 :                 for (int x = 0; x < xlen; x++)
    1101             :                 {
    1102          16 :                     EXPECT_DOUBLE_EQ(out[i], expected[i])
    1103           0 :                         << "Mismatch at (" << x << ", " << y << ")";
    1104          16 :                     i++;
    1105             :                 }
    1106             :         }
    1107             :     }
    1108             : 
    1109             :     // Left, up
    1110             :     {
    1111             :         // clang-format off
    1112           1 :         const int xlen = 8;
    1113           1 :         const int ylen = 2;
    1114           1 :         std::array<double, xlen * ylen> in
    1115             :         {
    1116             :             7.5, 3.7, 2.7, 1.5, 3.1, 1.4, 1.1, 0, // Row 0
    1117             :             7.5, 3.7, 2.7, 1.5, 3.1, 1,   1,   0  // Row 1
    1118             :         };
    1119             : 
    1120           1 :         std::array<double, xlen * ylen> sd
    1121             :         {
    1122             :             .1, 100, .1, .1, 100, .1, 100, 1,  // Row 0
    1123             :             .1, 100, .1, .1, 100, .1, 100, 1   // Row 1
    1124             :         };
    1125             :         // clang-format on
    1126             : 
    1127             :         {
    1128           2 :             Options opts = stdOptions(7, 1);
    1129           1 :             opts.outputMode = OutputMode::Normal;
    1130           2 :             DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
    1131           1 :             GDALRasterBand *band = ds->GetRasterBand(1);
    1132             :             std::array<double, xlen * ylen> out;
    1133           1 :             CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
    1134           1 :                                         xlen, ylen, GDT_Float64, 0, 0, nullptr);
    1135             : 
    1136           1 :             EXPECT_EQ(err, CE_None);
    1137             : 
    1138             :             // clang-format off
    1139           1 :             std::array<double, xlen * ylen> expected
    1140             :             {
    1141             :                 255, 2, 0, 0, 2,   2, 255, 255,  // Row 0
    1142             :                 255, 2, 2, 0, 255, 2, 255, 255   // Row 1
    1143             :             };
    1144             :             // clang-format on
    1145             : 
    1146           1 :             size_t i = 0;
    1147           3 :             for (int y = 0; y < ylen; y++)
    1148          18 :                 for (int x = 0; x < xlen; x++)
    1149             :                 {
    1150          16 :                     EXPECT_DOUBLE_EQ(out[i], expected[i])
    1151           0 :                         << "Mismatch at (" << x << ", " << y << ")";
    1152          16 :                     i++;
    1153             :                 }
    1154             :         }
    1155             :     }
    1156           1 : }
    1157             : 
    1158             : }  // namespace viewshed
    1159             : }  // namespace gdal

Generated by: LCOV version 1.14