LCOV - code coverage report
Current view: top level - autotest/cpp - test_viewshed.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 326 326 100.0 %
Date: 2025-05-31 00:00:17 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[110] = 1;
     250           1 :     in[111] = 3;
     251           1 :     in[112] = 5;
     252           1 :     in[113] = 7;
     253           1 :     in[114] = 9;
     254           1 :     in[115] = 11;
     255           1 :     in[116] = 13;
     256           1 :     in[117] = 15;
     257           1 :     in[118] = 17;
     258           1 :     in[119] = 19;
     259             : 
     260           2 :     SCOPED_TRACE("high_mask");
     261           2 :     Options opts(stdOptions(3, 7));
     262             : 
     263           1 :     opts.highPitch = 58;
     264             : 
     265           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     266             : 
     267             :     std::array<int8_t, xlen * ylen> out;
     268           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     269             : 
     270           1 :     int xOutLen = band->GetXSize();
     271           1 :     int yOutLen = band->GetYSize();
     272           1 :     EXPECT_EQ(xOutLen, 15);
     273           1 :     EXPECT_EQ(yOutLen, 15);
     274           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     275           1 :                                 xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
     276           1 :     EXPECT_EQ(err, CE_None);
     277             : 
     278           1 :     std::array<int8_t, 15 * 15> expected{
     279             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0,
     280             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0, 0, 0,
     281             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   0, 0, 0,
     282             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0, 0, 0,
     283             :         127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   0, 0, 0,
     284             :         127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   0,   0, 0, 0,
     285             :         127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   0,   0,   0, 0, 0,
     286             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0,
     287             :         127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   0,   0,   0, 0, 0,
     288             :         127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   0,   0, 0, 0,
     289             :         127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0,   0, 0, 0,
     290             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   0,   0, 0, 0,
     291             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0,   0, 0, 0,
     292             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,   0, 0, 0,
     293             :         127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0};
     294             : 
     295           1 :     int8_t *o = out.data();
     296           1 :     int8_t *e = expected.data();
     297         122 :     for (size_t i = 0; i < 11 * 11; ++i)
     298         121 :         EXPECT_EQ(*e++, *o++);
     299           1 : }
     300             : 
     301           4 : TEST(Viewshed, low_mask)
     302             : {
     303           1 :     const int xlen = 5;
     304           1 :     const int ylen = 5;
     305             :     std::array<int8_t, xlen * ylen> in;
     306           1 :     in.fill(0);
     307           1 :     in[12] = 5;
     308             : 
     309           2 :     SCOPED_TRACE("low_mask");
     310           2 :     Options opts(stdOptions(2, 2));
     311             : 
     312           1 :     opts.lowPitch = -45;
     313           1 :     opts.outputMode = OutputMode::DEM;
     314             : 
     315           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     316             : 
     317             :     std::array<double, xlen * ylen> out;
     318           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     319             : 
     320           1 :     int xOutLen = band->GetXSize();
     321           1 :     int yOutLen = band->GetYSize();
     322           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
     323           1 :                                 xOutLen, yOutLen, GDT_Float64, 0, 0, nullptr);
     324           1 :     EXPECT_EQ(err, CE_None);
     325             : 
     326           1 :     std::array<double, 5 * 5> expected{2.17157, 2.76393, 3, 2.76393, 2.17157,
     327             :                                        2.76393, 3.58579, 4, 3.58579, 2.76393,
     328             :                                        3,       4,       5, 4,       3,
     329             :                                        2.76393, 3.58579, 4, 3.58579, 2.76393,
     330             :                                        2.17157, 2.76393, 3, 2.76393, 2.17157};
     331             : 
     332           1 :     const double *o = out.data();
     333           1 :     const double *e = expected.data();
     334          26 :     for (size_t i = 0; i < expected.size(); ++i)
     335             :     {
     336          25 :         EXPECT_NEAR(*o, *e, .00001);
     337          25 :         o++;
     338          25 :         e++;
     339             :     }
     340           1 : }
     341             : 
     342           4 : TEST(Viewshed, all_visible)
     343             : {
     344             :     // clang-format off
     345           1 :     const int xlen = 3;
     346           1 :     const int ylen = 3;
     347           1 :     std::array<int8_t, xlen * ylen> in
     348             :     {
     349             :         1, 2, 3,
     350             :         4, 5, 6,
     351             :         3, 2, 1
     352             :     };
     353             :     // clang-format on
     354             : 
     355           2 :     SCOPED_TRACE("all_visible");
     356           2 :     DatasetPtr output = runViewshed(in.data(), xlen, ylen, stdOptions(1, 1));
     357             : 
     358             :     std::array<uint8_t, xlen * ylen> out;
     359           1 :     GDALRasterBand *band = output->GetRasterBand(1);
     360           1 :     CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     361           1 :                                 ylen, GDT_Int8, 0, 0, nullptr);
     362           1 :     EXPECT_EQ(err, CE_None);
     363             : 
     364          10 :     for (uint8_t o : out)
     365           9 :         EXPECT_EQ(o, 127);
     366           1 : }
     367             : 
     368           4 : TEST(Viewshed, simple_height)
     369             : {
     370             :     // clang-format off
     371           1 :     const int xlen = 5;
     372           1 :     const int ylen = 5;
     373           1 :     std::array<int8_t, xlen * ylen> in
     374             :     {
     375             :         -1, 0, 1, 0, -1,
     376             :         -1, 2, 0, 4, -1,
     377             :         -1, 1, 0, -1, -1,
     378             :          0, 3, 0, 2, 0,
     379             :         -1, 0, 0, 3, -1
     380             :     };
     381             : 
     382           1 :     std::array<double, xlen * ylen> observable
     383             :     {
     384             :         4, 2, 0, 4, 8,
     385             :         3, 2, 0, 4, 3,
     386             :         2, 1, 0, -1, -2,
     387             :         4, 3, 0, 2, 1,
     388             :         6, 3, 0, 2, 4
     389             :     };
     390             :     // clang-format on
     391             : 
     392             :     {
     393           2 :         SCOPED_TRACE("simple_height:normal");
     394             : 
     395             :         DatasetPtr output =
     396           2 :             runViewshed(in.data(), xlen, ylen, stdOptions(2, 2));
     397             : 
     398             :         std::array<int8_t, xlen * ylen> out;
     399           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     400           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     401           1 :                                     ylen, GDT_Int8, 0, 0, nullptr);
     402           1 :         EXPECT_EQ(err, CE_None);
     403             : 
     404             :         // We expect the cell to be observable when the input is higher than the observable
     405             :         // height.
     406             :         std::array<int8_t, xlen * ylen> expected;
     407          26 :         for (size_t i = 0; i < in.size(); ++i)
     408          25 :             expected[i] = (in[i] >= observable[i] ? 127 : 0);
     409             : 
     410           1 :         EXPECT_EQ(expected, out);
     411             :     }
     412             : 
     413             :     {
     414             :         std::array<double, xlen * ylen> dem;
     415           2 :         SCOPED_TRACE("simple_height:dem");
     416           2 :         Options opts = stdOptions(2, 2);
     417           1 :         opts.outputMode = OutputMode::DEM;
     418             : 
     419           2 :         DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     420             : 
     421           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     422           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, dem.data(), xlen,
     423           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     424           1 :         EXPECT_EQ(err, CE_None);
     425             : 
     426             :         // DEM values are observable values clamped at 0. Not sure why.
     427           1 :         std::array<double, xlen *ylen> expected = observable;
     428          26 :         for (double &d : expected)
     429          25 :             d = std::max(0.0, d);
     430             : 
     431             :         // Double equality is fine here as all the values are small integers.
     432           1 :         EXPECT_EQ(dem, expected);
     433             :     }
     434             : 
     435             :     {
     436             :         std::array<double, xlen * ylen> ground;
     437           2 :         SCOPED_TRACE("simple_height:ground");
     438           2 :         Options opts = stdOptions(2, 2);
     439           1 :         opts.outputMode = OutputMode::Ground;
     440           2 :         DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
     441             : 
     442           1 :         GDALRasterBand *band = output->GetRasterBand(1);
     443           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, ground.data(),
     444           1 :                                     xlen, ylen, GDT_Float64, 0, 0, nullptr);
     445           1 :         EXPECT_EQ(err, CE_None);
     446             : 
     447             :         std::array<double, xlen * ylen> expected;
     448          26 :         for (size_t i = 0; i < expected.size(); ++i)
     449          25 :             expected[i] = std::max(0.0, observable[i] - in[i]);
     450             : 
     451             :         // Double equality is fine here as all the values are small integers.
     452           1 :         EXPECT_EQ(expected, ground);
     453             :     }
     454           1 : }
     455             : 
     456             : // Addresses cases in #9501
     457           4 : TEST(Viewshed, dem_vs_ground)
     458             : {
     459             :     // Run gdal_viewshed on the input 8 x 1 array in both ground and dem mode and
     460             :     // verify the results are what are expected.
     461           5 :     auto run = [](const std::array<int8_t, 8> &in, Coord observer,
     462             :                   const std::array<double, 8> &ground,
     463             :                   const std::array<double, 8> &dem)
     464             :     {
     465           5 :         const int xlen = 8;
     466           5 :         const int ylen = 1;
     467             : 
     468             :         std::array<double, xlen * ylen> out;
     469          10 :         Options opts = stdOptions(observer);
     470           5 :         opts.outputMode = OutputMode::Ground;
     471             : 
     472             :         // Verify ground mode.
     473          10 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     474           5 :         GDALRasterBand *band = ds->GetRasterBand(1);
     475           5 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     476           5 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     477           5 :         EXPECT_EQ(err, CE_None);
     478          45 :         for (size_t i = 0; i < ground.size(); ++i)
     479          40 :             EXPECT_DOUBLE_EQ(out[i], ground[i]);
     480             : 
     481             :         // Verify DEM mode.
     482           5 :         opts.outputMode = OutputMode::DEM;
     483           5 :         ds = runViewshed(in.data(), xlen, ylen, opts);
     484           5 :         band = ds->GetRasterBand(1);
     485           5 :         err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen, ylen,
     486             :                              GDT_Float64, 0, 0, nullptr);
     487           5 :         EXPECT_EQ(err, CE_None);
     488          45 :         for (size_t i = 0; i < dem.size(); ++i)
     489          40 :             EXPECT_DOUBLE_EQ(out[i], dem[i]);
     490           5 :     };
     491             : 
     492             :     // Input / Observer / Minimum expected above ground / Minimum expected above zero
     493           1 :     run({0, 0, 0, 1, 0, 0, 0, 0}, {2, 0}, {0, 0, 0, 0, 2, 3, 4, 5},
     494             :         {0, 0, 0, 1, 2, 3, 4, 5});
     495           1 :     run({1, 1, 0, 1, 0, 1, 2, 2}, {3, 0}, {0, 0, 0, 0, 0, 0, 0, 1 / 3.0},
     496             :         {1, 0, 0, 1, 0, 0, 1, 7 / 3.0});
     497           1 :     run({0, 0, 0, 1, 1, 0, 0, 0}, {0, 0},
     498             :         {0, 0, 0, 0, 1 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0},
     499             :         {0, 0, 0, 0, 4 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0});
     500           1 :     run({0, 0, 1, 2, 3, 4, 5, 6}, {0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
     501             :         {0, 0, 0, 3 / 2.0, 8 / 3.0, 15 / 4.0, 24 / 5.0, 35 / 6.0});
     502           1 :     run({0, 0, 1, 1, 3, 4, 5, 4}, {0, 0}, {0, 0, 0, .5, 0, 0, 0, 11 / 6.0},
     503             :         {0, 0, 0, 3 / 2.0, 2, 15 / 4.0, 24 / 5.0, 35 / 6.0});
     504           1 : }
     505             : 
     506             : // Test an observer to the right of the raster.
     507           4 : TEST(Viewshed, oor_right)
     508             : {
     509             :     // clang-format off
     510           1 :     const int xlen = 5;
     511           1 :     const int ylen = 3;
     512           1 :     std::array<int8_t, xlen * ylen> in
     513             :     {
     514             :         1, 2, 0, 4, 1,
     515             :         0, 0, 2, 1, 0,
     516             :         1, 0, 0, 3, 3
     517             :     };
     518             :     // clang-format on
     519             : 
     520             :     {
     521           2 :         Options opts = stdOptions(6, 1);
     522           1 :         opts.outputMode = OutputMode::DEM;
     523           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     524           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     525             :         std::array<double, xlen * ylen> out;
     526           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     527           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     528           1 :         EXPECT_EQ(err, CE_None);
     529             : 
     530             :         // clang-format off
     531           1 :         std::array<double, xlen * ylen> expected
     532             :         {
     533             :             16 / 3.0, 29 / 6.0, 13 / 3.0, 1, 1,
     534             :             3,        2.5,      4 / 3.0,  0, 0,
     535             :             13 / 3.0, 23 / 6.0, 10 / 3.0, 3, 3
     536             :         };
     537             :         // clang-format on
     538             : 
     539          16 :         for (size_t i = 0; i < out.size(); ++i)
     540          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     541             :     }
     542             : 
     543             :     {
     544           2 :         Options opts = stdOptions(6, 2);
     545           1 :         opts.outputMode = OutputMode::DEM;
     546           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     547           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     548             :         std::array<double, xlen * ylen> out;
     549           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     550           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     551           1 :         EXPECT_EQ(err, CE_None);
     552             : 
     553             :         // clang-format off
     554           1 :         std::array<double, xlen * ylen> expected
     555             :         {
     556             :             26 / 5.0, 17 / 4.0, 11 / 3.0, .5,  1,
     557             :             6,        4.5,      3,        1.5, 0,
     558             :             9,        7.5,      6,        4.5, 3
     559             :         };
     560             :         // clang-format on
     561             : 
     562          16 :         for (size_t i = 0; i < out.size(); ++i)
     563          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     564             :     }
     565           1 : }
     566             : 
     567             : // Test an observer to the left of the raster.
     568           4 : TEST(Viewshed, oor_left)
     569             : {
     570             :     // clang-format off
     571           1 :     const int xlen = 5;
     572           1 :     const int ylen = 3;
     573           1 :     std::array<int8_t, xlen * ylen> in
     574             :     {
     575             :         1, 2, 0, 4, 1,
     576             :         0, 0, 2, 1, 0,
     577             :         1, 0, 0, 3, 3
     578             :     };
     579             :     // clang-format on
     580             : 
     581             :     {
     582           2 :         Options opts = stdOptions(-2, 1);
     583           1 :         opts.outputMode = OutputMode::DEM;
     584           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     585           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     586             :         std::array<double, xlen * ylen> out;
     587           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     588           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     589           1 :         EXPECT_EQ(err, CE_None);
     590             : 
     591             :         // clang-format off
     592           1 :         std::array<double, xlen * ylen> expected
     593             :         {
     594             :             1, 1, 2, 2.5, 4.5,
     595             :             0, 0, 0, 2.5, 3,
     596             :             1, 1, 1, 1.5, 3.5
     597             :         };
     598             :         // clang-format on
     599             : 
     600          16 :         for (size_t i = 0; i < out.size(); ++i)
     601          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     602             :     }
     603             : 
     604             :     {
     605           2 :         Options opts = stdOptions(-2, 2);
     606           1 :         opts.outputMode = OutputMode::DEM;
     607           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     608           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     609             :         std::array<double, xlen * ylen> out;
     610           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     611           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     612           1 :         EXPECT_EQ(err, CE_None);
     613             : 
     614             :         // clang-format off
     615           1 :         std::array<double, xlen * ylen> expected
     616             :         {
     617             :             1, .5,  5 / 3.0, 2.25, 4.2,
     618             :             0, .5,  1,       2.5,  3.1,
     619             :             1, 1.5, 2,       2.5,  3.6
     620             :         };
     621             :         // clang-format on
     622             : 
     623          16 :         for (size_t i = 0; i < out.size(); ++i)
     624          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     625             :     }
     626           1 : }
     627             : 
     628             : // Test an observer above the raster
     629           4 : TEST(Viewshed, oor_above)
     630             : {
     631             :     // clang-format off
     632           1 :     const int xlen = 5;
     633           1 :     const int ylen = 3;
     634           1 :     std::array<int8_t, xlen * ylen> in
     635             :     {
     636             :         1, 2, 0, 4, 1,
     637             :         0, 0, 2, 1, 0,
     638             :         1, 0, 0, 3, 3
     639             :     };
     640             :     // clang-format on
     641             : 
     642             :     {
     643           2 :         Options opts = stdOptions(2, -2);
     644           1 :         opts.outputMode = OutputMode::DEM;
     645           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     646           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     647             :         std::array<double, xlen * ylen> out;
     648           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     649           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     650             : 
     651           1 :         EXPECT_EQ(err, CE_None);
     652             : 
     653             :         // clang-format off
     654           1 :         std::array<double, xlen * ylen> expected
     655             :         {
     656             :             1,   2,       0,       4,        1,
     657             :             2.5, 2,       0,       4,        4.5,
     658             :             3,   8 / 3.0, 8 / 3.0, 14 / 3.0, 17 / 3.0
     659             :         };
     660             :         // clang-format on
     661             : 
     662          16 :         for (size_t i = 0; i < out.size(); ++i)
     663          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     664             :     }
     665             : 
     666             :     {
     667           2 :         Options opts = stdOptions(-2, -2);
     668           1 :         opts.outputMode = OutputMode::DEM;
     669           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     670           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     671             :         std::array<double, xlen * ylen> out;
     672           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     673           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     674           1 :         EXPECT_EQ(err, CE_None);
     675             : 
     676             :         // clang-format off
     677           1 :         std::array<double, xlen * ylen> expected
     678             :         {
     679             :             1, 2,   0,   4,    1,
     680             :             0, 1.5, 2.5, 1.25, 3.15,
     681             :             1, 0.5, 2,   3,    2.2
     682             :         };
     683             :         // clang-format on
     684             : 
     685          16 :         for (size_t i = 0; i < out.size(); ++i)
     686          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     687             :     }
     688           1 : }
     689             : 
     690             : // Test an observer below the raster
     691           4 : TEST(Viewshed, oor_below)
     692             : {
     693             :     // clang-format off
     694           1 :     const int xlen = 5;
     695           1 :     const int ylen = 3;
     696           1 :     std::array<int8_t, xlen * ylen> in
     697             :     {
     698             :         1, 2, 0, 4, 1,
     699             :         0, 0, 2, 1, 0,
     700             :         1, 0, 0, 3, 3
     701             :     };
     702             :     // clang-format on
     703             : 
     704             :     {
     705           2 :         Options opts = stdOptions(2, 4);
     706           1 :         opts.outputMode = OutputMode::DEM;
     707           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     708           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     709             :         std::array<double, xlen * ylen> out;
     710           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     711           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     712             : 
     713           1 :         EXPECT_EQ(err, CE_None);
     714             : 
     715             :         // clang-format off
     716           1 :         std::array<double, xlen * ylen> expected
     717             :         {
     718             :             1 / 3.0, 2 / 3.0, 8 / 3.0, 11 / 3.0, 5,
     719             :             0.5,     0,       0,       3,        4.5,
     720             :             1,       0,       0,       3,        3
     721             :         };
     722             :         // clang-format on
     723             : 
     724          16 :         for (size_t i = 0; i < out.size(); ++i)
     725          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     726             :     }
     727             : 
     728             :     {
     729           2 :         Options opts = stdOptions(6, 4);
     730           1 :         opts.outputMode = OutputMode::DEM;
     731           2 :         DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
     732           1 :         GDALRasterBand *band = ds->GetRasterBand(1);
     733             :         std::array<double, xlen * ylen> out;
     734           1 :         CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
     735           1 :                                     ylen, GDT_Float64, 0, 0, nullptr);
     736           1 :         EXPECT_EQ(err, CE_None);
     737             : 
     738             :         // clang-format off
     739           1 :         std::array<double, xlen * ylen> expected
     740             :         {
     741             :             4.2,  6,    6,   1.5, 1,
     742             :             1.35, 2.25, 4.5, 4.5, 0,
     743             :             1,    0,    0,   3,   3
     744             :         };
     745             :         // clang-format on
     746             : 
     747          16 :         for (size_t i = 0; i < out.size(); ++i)
     748          15 :             EXPECT_DOUBLE_EQ(out[i], expected[i]);
     749             :     }
     750           1 : }
     751             : 
     752             : }  // namespace viewshed
     753             : }  // namespace gdal

Generated by: LCOV version 1.14