LCOV - code coverage report
Current view: top level - autotest/cpp - test_viewshed.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 330 330 100.0 %
Date: 2025-09-19 00:41:44 Functions: 52 52 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          22 : Options stdOptions(int x, int y)
      37             : {
      38          22 :     Options opts;
      39          22 :     opts.observer.x = x;
      40          22 :     opts.observer.y = y;
      41          22 :     opts.outputFilename = "none";
      42          22 :     opts.outputFormat = "mem";
      43          22 :     opts.curveCoeff = 0;
      44             : 
      45          22 :     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             : }  // namespace
      73             : 
      74           4 : TEST(Viewshed, min_max_mask)
      75             : {
      76           1 :     const int xlen = 15;
      77           1 :     const int ylen = 15;
      78             :     std::array<int8_t, xlen * ylen> in;
      79           1 :     in.fill(0);
      80             : 
      81           2 :     SCOPED_TRACE("min_max_mask");
      82           2 :     Options opts(stdOptions(7, 7));
      83           1 :     opts.minDistance = 2;
      84           1 :     opts.maxDistance = 6;
      85             : 
      86           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
      87             : 
      88             :     std::array<int8_t, xlen * ylen> out;
      89           1 :     GDALRasterBand *band = output->GetRasterBand(1);
      90             : 
      91           1 :     int xOutLen = band->GetXSize();
      92           1 :     int yOutLen = band->GetYSize();
      93           1 :     EXPECT_EQ(xOutLen, 13);
      94           1 :     EXPECT_EQ(yOutLen, 13);
      95             : 
      96           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
      97           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
      98           1 :     EXPECT_EQ(err, CE_None);
      99             : 
     100             :     // clang-format off
     101           1 :     std::array<int8_t, 13 * 13> expected{
     102             :         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
     103             :         0,   0,   0,   0,   0,   0,   127, 0,   0,   0,   0,   0,   0,
     104             :         0,   0,   0,   127, 127, 127, 127, 127, 127, 127, 0,   0,   0,
     105             :         0,   0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,
     106             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     107             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     108             :         0,   127, 127, 127, 127, 0,   0,   0,   127, 127, 127, 127, 0,
     109             :         127, 127, 127, 127, 127, 0,   0,   0,   127, 127, 127, 127, 127,
     110             :         0,   127, 127, 127, 127, 0,   0,   0,   127, 127, 127, 127, 0,
     111             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     112             :         0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
     113             :         0,   0,   127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,
     114             :         0,   0,   0,   127, 127, 127, 127, 127, 127, 127, 0,   0,   0
     115             :     };
     116             :     // clang-format on
     117             : 
     118           1 :     int8_t *o = out.data();
     119           1 :     int8_t *e = expected.data();
     120         170 :     for (size_t i = 0; i < 13 * 13; ++i)
     121         169 :         EXPECT_EQ(*e++, *o++);
     122             : 
     123             :     /**
     124             :     int8_t *p = out.data();
     125             :     for (int y = 0; y < yOutLen; ++y)
     126             :     {
     127             :         for (int x = 0; x < xOutLen; ++x)
     128             :         {
     129             :             char c;
     130             :             if (*p == 0)
     131             :                 c = '*';
     132             :             else if (*p == 127)
     133             :                 c = '.';
     134             :             else
     135             :                 c = '?';
     136             :             std::cerr << c;
     137             :             p++;
     138             :         }
     139             :         std::cerr << "\n";
     140             :     }
     141             :     std::cerr << "\n";
     142             :     **/
     143           1 : }
     144             : 
     145           4 : TEST(Viewshed, angle)
     146             : {
     147           1 :     const int xlen = 17;
     148           1 :     const int ylen = 17;
     149             :     std::array<int8_t, xlen * ylen> in;
     150           1 :     in.fill(0);
     151             : 
     152           2 :     SCOPED_TRACE("min_max_mask");
     153           2 :     Options opts(stdOptions(8, 8));
     154           1 :     opts.startAngle = 0;
     155           1 :     opts.endAngle = 30;
     156             : 
     157           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     158             : 
     159             :     std::array<int8_t, xlen * ylen> out;
     160           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     161             : 
     162           1 :     int xOutLen = band->GetXSize();
     163           1 :     int yOutLen = band->GetYSize();
     164           1 :     EXPECT_EQ(xOutLen, 6);
     165           1 :     EXPECT_EQ(yOutLen, 9);
     166           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     167           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     168           1 :     EXPECT_EQ(err, CE_None);
     169             : 
     170             :     // clang-format off
     171           1 :     std::array<int8_t, 6 * 9> expected{
     172             :         127, 127, 127, 127, 127, 127,
     173             :         127, 127, 127, 127, 127, 0,
     174             :         127, 127, 127, 127, 0,   0,
     175             :         127, 127, 127, 127, 0,   0,
     176             :         127, 127, 127, 0,   0,   0,
     177             :         127, 127, 127, 0,   0,   0,
     178             :         127, 127, 0,   0,   0,   0,
     179             :         127, 127, 0,   0,   0,   0,
     180             :         127, 0,   0,   0,   0,   0
     181             :     };
     182             :     // clang-format on
     183             : 
     184           1 :     int8_t *o = out.data();
     185           1 :     int8_t *e = expected.data();
     186          55 :     for (size_t i = 0; i < 6 * 9; ++i)
     187          54 :         EXPECT_EQ(*e++, *o++);
     188             : 
     189             :     /**
     190             :     int8_t *p = out.data();
     191             :     for (int y = 0; y < yOutLen; ++y)
     192             :     {
     193             :         for (int x = 0; x < xOutLen; ++x)
     194             :         {
     195             :             char c;
     196             :             if (*p == 0)
     197             :                 c = '*';
     198             :             else if (*p == 127)
     199             :                 c = '.';
     200             :             else
     201             :                 c = '?';
     202             :             std::cerr << c;
     203             :             p++;
     204             :         }
     205             :         std::cerr << "\n";
     206             :     }
     207             :     std::cerr << "\n";
     208             :     **/
     209           1 : }
     210             : 
     211           4 : TEST(Viewshed, angle2)
     212             : {
     213           1 :     const int xlen = 11;
     214           1 :     const int ylen = 11;
     215             :     std::array<int8_t, xlen * ylen> in;
     216           1 :     in.fill(0);
     217             : 
     218           2 :     SCOPED_TRACE("min_max_mask");
     219           2 :     Options opts(stdOptions(5, 5));
     220           1 :     opts.startAngle = 0;
     221           1 :     opts.endAngle = 300;
     222             : 
     223           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     224             : 
     225             :     std::array<int8_t, xlen * ylen> out;
     226           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     227             : 
     228           1 :     int xOutLen = band->GetXSize();
     229           1 :     int yOutLen = band->GetYSize();
     230           1 :     EXPECT_EQ(xOutLen, 11);
     231           1 :     EXPECT_EQ(yOutLen, 11);
     232           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     233           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     234           1 :     EXPECT_EQ(err, CE_None);
     235             : 
     236             :     // clang-format off
     237           1 :     std::array<int8_t, 11 * 11> expected{
     238             :         0,   0,   0,   0,   0,   127, 127, 127, 127, 127, 127, 0,   0,   0,
     239             :         0,   0,   127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   127,
     240             :         127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   127, 127, 127, 127,
     241             :         127, 127, 127, 127, 127, 127, 0,   127, 127, 127, 127, 127, 127, 127,
     242             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     243             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     244             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     245             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
     246             :         127, 127, 127, 127, 127, 127, 127, 127, 127,
     247             :     };
     248             :     // clang-format on
     249             : 
     250           1 :     int8_t *o = out.data();
     251           1 :     int8_t *e = expected.data();
     252         122 :     for (size_t i = 0; i < 11 * 11; ++i)
     253         121 :         EXPECT_EQ(*e++, *o++);
     254           1 : }
     255             : 
     256           4 : TEST(Viewshed, high_mask)
     257             : {
     258           1 :     const int xlen = 15;
     259           1 :     const int ylen = 15;
     260             :     std::array<int8_t, xlen * ylen> in;
     261           1 :     in.fill(0);
     262           1 :     in[15 * 7 + 5] = 1;
     263           1 :     in[15 * 7 + 6] = 3;
     264           1 :     in[15 * 7 + 7] = 5;
     265           1 :     in[15 * 7 + 8] = 7;
     266           1 :     in[15 * 7 + 9] = 7;
     267           1 :     in[15 * 7 + 10] = 7;
     268           1 :     in[15 * 7 + 11] = 7;
     269           1 :     in[15 * 7 + 12] = 12;
     270           1 :     in[15 * 7 + 13] = 6;
     271           1 :     in[15 * 7 + 14] = 15;
     272             : 
     273           2 :     SCOPED_TRACE("high_mask");
     274           2 :     Options opts(stdOptions(3, 7));
     275             : 
     276           1 :     opts.highPitch = 45;
     277           1 :     opts.outOfRangeVal = 2;
     278           1 :     opts.visibleVal = 1;
     279           1 :     opts.invisibleVal = 0;
     280             : 
     281           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     282             : 
     283             :     std::array<int8_t, xlen * ylen> out;
     284           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     285             : 
     286           1 :     int xOutLen = band->GetXSize();
     287           1 :     int yOutLen = band->GetYSize();
     288           1 :     EXPECT_EQ(xOutLen, 15);
     289           1 :     EXPECT_EQ(yOutLen, 15);
     290           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     291           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     292           1 :     EXPECT_EQ(err, CE_None);
     293             : 
     294             :     // clang-format off
     295           1 :     std::array<int8_t, 15 * 15> expected{
     296             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
     297             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
     298             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
     299             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
     300             :         1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
     301             :         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
     302             :         1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     303             :         1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 2, 0, 2,
     304             :         1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     305             :         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
     306             :         1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
     307             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
     308             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
     309             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
     310             :         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
     311             :     };
     312             :     // clang-format on
     313             : 
     314           1 :     int8_t *o = out.data();
     315           1 :     int8_t *e = expected.data();
     316          16 :     for (int y = 0; y < 15; ++y)
     317             :     {
     318         240 :         for (int x = 0; x < 15; ++x)
     319             :         {
     320         225 :             EXPECT_EQ(*e, *o);
     321         225 :             e++;
     322         225 :             o++;
     323             :         }
     324             :     }
     325           1 : }
     326             : 
     327           4 : TEST(Viewshed, low_mask)
     328             : {
     329           1 :     const int xlen = 5;
     330           1 :     const int ylen = 5;
     331             :     std::array<int8_t, xlen * ylen> in;
     332           1 :     in.fill(0);
     333           1 :     in[12] = 5;
     334             : 
     335           2 :     SCOPED_TRACE("low_mask");
     336           2 :     Options opts(stdOptions(2, 2));
     337             : 
     338           1 :     opts.lowPitch = -45;
     339           1 :     opts.outputMode = OutputMode::DEM;
     340             : 
     341           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     342             : 
     343             :     std::array<double, xlen * ylen> out;
     344           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     345             : 
     346           1 :     int xOutLen = band->GetXSize();
     347           1 :     int yOutLen = band->GetYSize();
     348           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     349           1 :                                 xOutLen, yOutLen, GDT_Float64, 0, 0, nullptr);
     350           1 :     EXPECT_EQ(err, CE_None);
     351             : 
     352           1 :     std::array<double, 5 * 5> expected{2.17157, 2.76393, 3, 2.76393, 2.17157,
     353             :                                        2.76393, 3.58579, 4, 3.58579, 2.76393,
     354             :                                        3,       4,       5, 4,       3,
     355             :                                        2.76393, 3.58579, 4, 3.58579, 2.76393,
     356             :                                        2.17157, 2.76393, 3, 2.76393, 2.17157};
     357             : 
     358           1 :     const double *o = out.data();
     359           1 :     const double *e = expected.data();
     360          26 :     for (size_t i = 0; i < expected.size(); ++i)
     361             :     {
     362          25 :         EXPECT_NEAR(*o, *e, .00001);
     363          25 :         o++;
     364          25 :         e++;
     365             :     }
     366           1 : }
     367             : 
     368           4 : TEST(Viewshed, all_visible)
     369             : {
     370             :     // clang-format off
     371           1 :     const int xlen = 3;
     372           1 :     const int ylen = 3;
     373           1 :     std::array<int8_t, xlen * ylen> in
     374             :     {
     375             :         1, 2, 3,
     376             :         4, 5, 6,
     377             :         3, 2, 1
     378             :     };
     379             :     // clang-format on
     380             : 
     381           2 :     SCOPED_TRACE("all_visible");
     382           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, stdOptions(1, 1));
     383             : 
     384             :     std::array<uint8_t, xlen * ylen> out;
     385           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     386           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     387           1 :                                 ylen, GDT_Int8, 0, 0, nullptr);
     388           1 :     EXPECT_EQ(err, CE_None);
     389             : 
     390          10 :     for (uint8_t o : out)
     391           9 :         EXPECT_EQ(o, 127);
     392           1 : }
     393             : 
     394           4 : TEST(Viewshed, simple_height)
     395             : {
     396             :     // clang-format off
     397           1 :     const int xlen = 5;
     398           1 :     const int ylen = 5;
     399           1 :     std::array<int8_t, xlen * ylen> in
     400             :     {
     401             :         -1, 0, 1, 0, -1,
     402             :         -1, 2, 0, 4, -1,
     403             :         -1, 1, 0, -1, -1,
     404             :          0, 3, 0, 2, 0,
     405             :         -1, 0, 0, 3, -1
     406             :     };
     407             : 
     408           1 :     std::array<double, xlen * ylen> observable
     409             :     {
     410             :         4, 2, 1, 4, 8,
     411             :         3, 2, 0, 4, 3,
     412             :         2, 1, 0, -1, -1,
     413             :         4, 3, 0, 2, 1,
     414             :         6, 3, 0, 3, 4
     415             :     };
     416             :     // clang-format on
     417             : 
     418             :     {
     419           2 :         SCOPED_TRACE("simple_height:normal");
     420             : 
     421             :         DatasetPtr output =
     422           2 :             runViewshed(in.data(), xlen, ylen, stdOptions(2, 2));
     423             : 
     424             :         std::array<int8_t, xlen * ylen> out;
     425           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     426           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     427           1 :                                     ylen, GDT_Int8, 0, 0, nullptr);
     428           1 :         EXPECT_EQ(err, CE_None);
     429             : 
     430             :         // We expect the cell to be observable when the input is higher than the observable
     431             :         // height.
     432             :         std::array<int8_t, xlen * ylen> expected;
     433          26 :         for (size_t i = 0; i < in.size(); ++i)
     434          25 :             expected[i] = (in[i] >= observable[i] ? 127 : 0);
     435             : 
     436           1 :         EXPECT_EQ(expected, out);
     437             :     }
     438             : 
     439             :     {
     440             :         std::array<double, xlen * ylen> dem;
     441           2 :         SCOPED_TRACE("simple_height:dem");
     442           2 :         Options opts = stdOptions(2, 2);
     443           1 :         opts.outputMode = OutputMode::DEM;
     444             : 
     445           2 :         DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     446             : 
     447           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     448           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, dem.data(), xlen,
     449           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     450           1 :         EXPECT_EQ(err, CE_None);
     451             : 
     452           1 :         std::array<double, xlen *ylen> expected = observable;
     453             :         // Double equality is fine here as all the values are small integers.
     454           1 :         EXPECT_EQ(dem, expected);
     455             :     }
     456             : 
     457             :     {
     458             :         std::array<double, xlen * ylen> ground;
     459           2 :         SCOPED_TRACE("simple_height:ground");
     460           2 :         Options opts = stdOptions(2, 2);
     461           1 :         opts.outputMode = OutputMode::Ground;
     462           2 :         DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     463             : 
     464           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     465           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, ground.data(),
     466           1 :                                     xlen, ylen, GDT_Float64, 0, 0, nullptr);
     467           1 :         EXPECT_EQ(err, CE_None);
     468             : 
     469             :         std::array<double, xlen * ylen> expected;
     470          26 :         for (size_t i = 0; i < expected.size(); ++i)
     471          25 :             expected[i] = std::max(0.0, observable[i] - in[i]);
     472             : 
     473             :         // Double equality is fine here as all the values are small integers.
     474           1 :         EXPECT_EQ(expected, ground);
     475             :     }
     476           1 : }
     477             : 
     478             : // Addresses cases in #9501
     479           4 : TEST(Viewshed, dem_vs_ground)
     480             : {
     481             :     // Run gdal_viewshed on the input 8 x 1 array in both ground and dem mode and
     482             :     // verify the results are what are expected.
     483           5 :     auto run = [](const std::array<int8_t, 8> &in, Coord observer,
     484             :                   const std::array<double, 8> &ground,
     485             :                   const std::array<double, 8> &dem)
     486             :     {
     487           5 :         const int xlen = 8;
     488           5 :         const int ylen = 1;
     489             : 
     490             :         std::array<double, xlen * ylen> out;
     491          10 :         Options opts = stdOptions(observer);
     492           5 :         opts.outputMode = OutputMode::Ground;
     493             : 
     494             :         // Verify ground mode.
     495          10 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     496           5 :         GDALRasterBand *band = ds->GetRasterBand(1);
     497           5 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     498           5 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     499           5 :         EXPECT_EQ(err, CE_None);
     500          45 :         for (size_t i = 0; i < ground.size(); ++i)
     501          40 :             EXPECT_DOUBLE_EQ(out[i], ground[i]);
     502             : 
     503             :         // Verify DEM mode.
     504           5 :         opts.outputMode = OutputMode::DEM;
     505           5 :         ds = runViewshed(in.data(), xlen, ylen, opts);
     506           5 :         band = ds->GetRasterBand(1);
     507           5 :         err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen, ylen,
     508             :                              GDT_Float64, 0, 0, nullptr);
     509           5 :         EXPECT_EQ(err, CE_None);
     510          45 :         for (size_t i = 0; i < dem.size(); ++i)
     511          40 :             EXPECT_DOUBLE_EQ(out[i], dem[i]);
     512           5 :     };
     513             : 
     514             :     // Input / Observer / Minimum expected above ground / Minimum expected  above zero (DEM)
     515           1 :     run({0, 0, 0, 1, 0, 0, 0, 0}, {2, 0}, {0, 0, 0, 0, 2, 3, 4, 5},
     516             :         {0, 0, 0, 1, 2, 3, 4, 5});
     517           1 :     run({1, 1, 0, 1, 0, 1, 2, 2}, {3, 0}, {0, 0, 0, 0, 0, 0, 0, 1 / 3.0},
     518             :         {1, 1, 0, 1, 0, 1, 2, 7 / 3.0});
     519           1 :     run({0, 0, 0, 1, 1, 0, 0, 0}, {0, 0},
     520             :         {0, 0, 0, 0, 1 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0},
     521             :         {0, 0, 0, 1, 4 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0});
     522           1 :     run({0, 0, 1, 2, 3, 4, 5, 6}, {0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
     523             :         {0, 0, 1, 2, 3, 4, 5, 6});
     524           1 :     run({0, 0, 1, 1, 3, 4, 5, 4}, {0, 0}, {0, 0, 0, .5, 0, 0, 0, 11 / 6.0},
     525             :         {0, 0, 1, 1.5, 3, 4, 5, 35 / 6.0});
     526           1 : }
     527             : 
     528             : // Test an observer to the right of the raster.
     529           4 : TEST(Viewshed, oor_right)
     530             : {
     531             :     // clang-format off
     532           1 :     const int xlen = 5;
     533           1 :     const int ylen = 3;
     534           1 :     std::array<int8_t, xlen * ylen> in
     535             :     {
     536             :         1, 2, 0, 4, 1,
     537             :         0, 0, 2, 1, 0,
     538             :         1, 0, 0, 3, 3
     539             :     };
     540             :     // clang-format on
     541             : 
     542             :     {
     543           2 :         Options opts = stdOptions(6, 1);
     544           1 :         opts.outputMode = OutputMode::DEM;
     545           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     546           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     547             :         std::array<double, xlen * ylen> out;
     548           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     549           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     550           1 :         EXPECT_EQ(err, CE_None);
     551             : 
     552             :         // clang-format off
     553           1 :         std::array<double, xlen * ylen> expected
     554             :         {
     555             :             16 / 3.0, 29 / 6.0, 13 / 3.0, 4, 1,
     556             :             3,        2.5,      2,  1, 0,
     557             :             13 / 3.0, 23 / 6.0, 10 / 3.0, 3, 3
     558             :         };
     559             :         // clang-format on
     560             : 
     561          16 :         for (size_t i = 0; i < out.size(); ++i)
     562          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     563             :     }
     564             : 
     565             :     {
     566           2 :         Options opts = stdOptions(6, 2);
     567           1 :         opts.outputMode = OutputMode::DEM;
     568           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     569           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     570             :         std::array<double, xlen * ylen> out;
     571           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     572           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     573           1 :         EXPECT_EQ(err, CE_None);
     574             : 
     575             :         // clang-format off
     576           1 :         std::array<double, xlen * ylen> expected
     577             :         {
     578             :             26 / 5.0, 17 / 4.0, 11 / 3.0, 4,  1,
     579             :             6,        4.5,      3,        1.5, 0,
     580             :             9,        7.5,      6,        4.5, 3
     581             :         };
     582             :         // clang-format on
     583             : 
     584          16 :         for (size_t i = 0; i < out.size(); ++i)
     585          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     586             :     }
     587           1 : }
     588             : 
     589             : // Test an observer to the left of the raster.
     590           4 : TEST(Viewshed, oor_left)
     591             : {
     592             :     // clang-format off
     593           1 :     const int xlen = 5;
     594           1 :     const int ylen = 3;
     595           1 :     std::array<int8_t, xlen * ylen> in
     596             :     {
     597             :         1, 2, 0, 4, 1,
     598             :         0, 0, 2, 1, 0,
     599             :         1, 0, 0, 3, 3
     600             :     };
     601             :     // clang-format on
     602             : 
     603             :     {
     604           2 :         Options opts = stdOptions(-2, 1);
     605           1 :         opts.outputMode = OutputMode::DEM;
     606           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     607           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     608             :         std::array<double, xlen * ylen> out;
     609           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     610           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     611           1 :         EXPECT_EQ(err, CE_None);
     612             : 
     613             :         // clang-format off
     614           1 :         std::array<double, xlen * ylen> expected
     615             :         {
     616             :             1, 2, 2, 4, 4.5,
     617             :             0, 0, 2, 2.5, 3,
     618             :             1, 1, 1, 3, 3.5
     619             :         };
     620             :         // clang-format on
     621             : 
     622          16 :         for (size_t i = 0; i < out.size(); ++i)
     623          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     624             :     }
     625             : 
     626             :     {
     627           2 :         Options opts = stdOptions(-2, 2);
     628           1 :         opts.outputMode = OutputMode::DEM;
     629           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     630           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     631             :         std::array<double, xlen * ylen> out;
     632           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     633           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     634           1 :         EXPECT_EQ(err, CE_None);
     635             : 
     636             :         // clang-format off
     637           1 :         std::array<double, xlen * ylen> expected
     638             :         {
     639             :             1, 2,   5 / 3.0, 4,   4.2,
     640             :             0, .5,  2,       2.5, 3.1,
     641             :             1, 1.5, 2,       3,   3.6
     642             :         };
     643             :         // clang-format on
     644             : 
     645          16 :         for (size_t i = 0; i < out.size(); ++i)
     646          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     647             :     }
     648           1 : }
     649             : 
     650             : // Test an observer above the raster
     651           4 : TEST(Viewshed, oor_above)
     652             : {
     653             :     // clang-format off
     654           1 :     const int xlen = 5;
     655           1 :     const int ylen = 3;
     656           1 :     std::array<int8_t, xlen * ylen> in
     657             :     {
     658             :         1, 2, 0, 4, 1,
     659             :         0, 0, 2, 1, 0,
     660             :         1, 0, 0, 3, 3
     661             :     };
     662             :     // clang-format on
     663             : 
     664             :     {
     665           2 :         Options opts = stdOptions(2, -2);
     666           1 :         opts.outputMode = OutputMode::DEM;
     667           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     668           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     669             :         std::array<double, xlen * ylen> out;
     670           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     671           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     672             : 
     673           1 :         EXPECT_EQ(err, CE_None);
     674             : 
     675             :         // clang-format off
     676           1 :         std::array<double, xlen * ylen> expected
     677             :         {
     678             :             1,   2,       0,       4,        1,
     679             :             2.5, 2,       2,       4,        4.5,
     680             :             3,   8 / 3.0, 8 / 3.0, 14 / 3.0, 17 / 3.0
     681             :         };
     682             :         // clang-format on
     683             : 
     684          16 :         for (size_t i = 0; i < out.size(); ++i)
     685          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     686             :     }
     687             : 
     688             :     {
     689           2 :         Options opts = stdOptions(-2, -2);
     690           1 :         opts.outputMode = OutputMode::DEM;
     691           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     692           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     693             :         std::array<double, xlen * ylen> out;
     694           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     695           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     696           1 :         EXPECT_EQ(err, CE_None);
     697             : 
     698             :         // clang-format off
     699           1 :         std::array<double, xlen * ylen> expected
     700             :         {
     701             :             1, 2,   0,   4,    1,
     702             :             0, 1.5, 2.5, 1.25, 3.15,
     703             :             1, 0.5, 2,   3,    3
     704             :         };
     705             :         // clang-format on
     706             : 
     707          16 :         for (size_t i = 0; i < out.size(); ++i)
     708          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     709             :     }
     710           1 : }
     711             : 
     712             : // Test an observer below the raster
     713           4 : TEST(Viewshed, oor_below)
     714             : {
     715             :     // clang-format off
     716           1 :     const int xlen = 5;
     717           1 :     const int ylen = 3;
     718           1 :     std::array<int8_t, xlen * ylen> in
     719             :     {
     720             :         1, 2, 0, 4, 1,
     721             :         0, 0, 2, 1, 0,
     722             :         1, 0, 0, 3, 3
     723             :     };
     724             :     // clang-format on
     725             : 
     726             :     {
     727           2 :         Options opts = stdOptions(2, 4);
     728           1 :         opts.outputMode = OutputMode::DEM;
     729           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     730           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     731             :         std::array<double, xlen * ylen> out;
     732           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     733           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     734             : 
     735           1 :         EXPECT_EQ(err, CE_None);
     736             : 
     737             :         // clang-format off
     738           1 :         std::array<double, xlen * ylen> expected
     739             :         {
     740             :             1,    2,  8 / 3.0,  4,  5,
     741             :             0.5,  0,  2,        3,  4.5,
     742             :             1,    0,  0,        3,  3
     743             :         };
     744             :         // clang-format on
     745             : 
     746          16 :         for (size_t i = 0; i < out.size(); ++i)
     747          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     748             :     }
     749             : 
     750             :     {
     751           2 :         Options opts = stdOptions(6, 4);
     752           1 :         opts.outputMode = OutputMode::DEM;
     753           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     754           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     755             :         std::array<double, xlen * ylen> out;
     756           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     757           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     758           1 :         EXPECT_EQ(err, CE_None);
     759             : 
     760             :         // clang-format off
     761           1 :         std::array<double, xlen * ylen> expected
     762             :         {
     763             :             4.2,  6,    6,   4,   1,
     764             :             1.35, 2.25, 4.5, 4.5, 0,
     765             :             1,    0,    0,   3,   3
     766             :         };
     767             :         // clang-format on
     768             : 
     769          16 :         for (size_t i = 0; i < out.size(); ++i)
     770          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     771             :     }
     772           1 : }
     773             : 
     774             : }  // namespace viewshed
     775             : }  // namespace gdal

Generated by: LCOV version 1.14