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

Generated by: LCOV version 1.14