LCOV - code coverage report
Current view: top level - autotest/cpp - test_alg.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 245 254 96.5 %
Date: 2024-05-14 13:00:50 Functions: 45 46 97.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * $Id$
       3             :  *
       4             :  * Project:  GDAL algorithms
       5             :  * Purpose:  Test alg
       6             :  * Author:   Even Rouault, even.rouault at spatialys.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2016, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include <array>
      31             : 
      32             : #include "gdal_unit_test.h"
      33             : 
      34             : #include "cpl_conv.h"
      35             : 
      36             : #include "gdal_alg.h"
      37             : #include "gdalwarper.h"
      38             : #include "gdal_priv.h"
      39             : 
      40             : #include "gtest_include.h"
      41             : 
      42             : #include "test_data.h"
      43             : 
      44             : namespace
      45             : {
      46             : // Common fixture with test data
      47             : struct test_alg : public ::testing::Test
      48             : {
      49             :     std::string data_;
      50             : 
      51          11 :     test_alg()
      52          11 :     {
      53             :         // Compose data path for test group
      54          11 :         data_ = tut::common::data_basedir;
      55          11 :     }
      56             : };
      57             : 
      58             : typedef struct
      59             : {
      60             :     double dfLevel;
      61             :     int nPoints;
      62             :     double x;
      63             :     double y;
      64             : } writeCbkData;
      65             : 
      66           0 : static CPLErr writeCbk(double dfLevel, int nPoints, double *padfX,
      67             :                        double *padfY, void *userData)
      68             : {
      69           0 :     writeCbkData *data = (writeCbkData *)userData;
      70           0 :     data->dfLevel = dfLevel;
      71           0 :     data->nPoints = nPoints;
      72           0 :     if (nPoints == 1)
      73             :     {
      74           0 :         data->x = padfX[0];
      75           0 :         data->y = padfY[0];
      76             :     }
      77           0 :     return CE_None;
      78             : }
      79             : 
      80             : // Dummy test
      81           4 : TEST_F(test_alg, GDAL_CG_FeedLine_dummy)
      82             : {
      83             :     writeCbkData data;
      84           1 :     memset(&data, 0, sizeof(data));
      85             :     GDALContourGeneratorH hCG =
      86           1 :         GDAL_CG_Create(1, 1, FALSE, 0, 1, 0, writeCbk, &data);
      87           1 :     double scanline[] = {0};
      88           1 :     EXPECT_EQ(GDAL_CG_FeedLine(hCG, scanline), CE_None);
      89           1 :     EXPECT_EQ(data.dfLevel, 0);
      90           1 :     EXPECT_EQ(data.nPoints, 0);
      91           1 :     EXPECT_DOUBLE_EQ(data.x, 0.0);
      92           1 :     EXPECT_DOUBLE_EQ(data.y, 0.0);
      93           1 :     GDAL_CG_Destroy(hCG);
      94           1 : }
      95             : 
      96             : // GDALWarpResolveWorkingDataType: default type
      97           4 : TEST_F(test_alg, GDALWarpResolveWorkingDataType_default_type)
      98             : {
      99           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     100           1 :     GDALWarpResolveWorkingDataType(psOptions);
     101           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     102           1 :     GDALDestroyWarpOptions(psOptions);
     103           1 : }
     104             : 
     105             : // GDALWarpResolveWorkingDataType: do not change user specified type
     106           4 : TEST_F(test_alg, GDALWarpResolveWorkingDataType_keep_user_type)
     107             : {
     108           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     109           1 :     psOptions->eWorkingDataType = GDT_CFloat64;
     110           1 :     GDALWarpResolveWorkingDataType(psOptions);
     111           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CFloat64);
     112           1 :     GDALDestroyWarpOptions(psOptions);
     113           1 : }
     114             : 
     115             : // GDALWarpResolveWorkingDataType: effect of padfSrcNoDataReal
     116           4 : TEST_F(test_alg, GDALWarpResolveWorkingDataType_padfSrcNoDataReal)
     117             : {
     118           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     119           1 :     psOptions->nBandCount = 1;
     120           1 :     psOptions->padfSrcNoDataReal =
     121           1 :         static_cast<double *>(CPLMalloc(sizeof(double)));
     122           1 :     psOptions->padfSrcNoDataReal[0] = 0.0;
     123           1 :     GDALWarpResolveWorkingDataType(psOptions);
     124           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     125             : 
     126           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     127           1 :     psOptions->padfSrcNoDataReal[0] = -1.0;
     128           1 :     GDALWarpResolveWorkingDataType(psOptions);
     129           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Int16);
     130             : 
     131           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     132           1 :     psOptions->padfSrcNoDataReal[0] = 2.0;
     133           1 :     GDALWarpResolveWorkingDataType(psOptions);
     134           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     135             : 
     136           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     137           1 :     psOptions->padfSrcNoDataReal[0] = 256.0;
     138           1 :     GDALWarpResolveWorkingDataType(psOptions);
     139           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_UInt16);
     140             : 
     141           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     142           1 :     psOptions->padfSrcNoDataReal[0] = 2.5;
     143           1 :     GDALWarpResolveWorkingDataType(psOptions);
     144           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Float32);
     145             : 
     146           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     147           1 :     psOptions->padfSrcNoDataReal[0] = 2.12345678;
     148           1 :     GDALWarpResolveWorkingDataType(psOptions);
     149           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Float64);
     150             : 
     151           1 :     GDALDestroyWarpOptions(psOptions);
     152           1 : }
     153             : 
     154             : // GDALWarpResolveWorkingDataType: effect of padfSrcNoDataImag
     155           4 : TEST_F(test_alg, GDALWarpResolveWorkingDataType_padfSrcNoDataImag)
     156             : {
     157           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     158           1 :     psOptions->nBandCount = 1;
     159           1 :     psOptions->padfSrcNoDataReal =
     160           1 :         static_cast<double *>(CPLMalloc(sizeof(double)));
     161           1 :     psOptions->padfSrcNoDataImag =
     162           1 :         static_cast<double *>(CPLMalloc(sizeof(double)));
     163           1 :     psOptions->padfSrcNoDataReal[0] = 0.0;
     164           1 :     psOptions->padfSrcNoDataImag[0] = 0.0;
     165           1 :     GDALWarpResolveWorkingDataType(psOptions);
     166           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     167             : 
     168           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     169           1 :     psOptions->padfSrcNoDataReal[0] = 0.0;
     170           1 :     psOptions->padfSrcNoDataImag[0] = 1.0;
     171           1 :     GDALWarpResolveWorkingDataType(psOptions);
     172             :     // Could probably be CInt16
     173           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CInt32);
     174             : 
     175           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     176           1 :     psOptions->padfSrcNoDataReal[0] = 0.0;
     177           1 :     psOptions->padfSrcNoDataImag[0] = 1.5;
     178           1 :     GDALWarpResolveWorkingDataType(psOptions);
     179           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CFloat32);
     180             : 
     181           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     182           1 :     psOptions->padfSrcNoDataReal[0] = 0.0;
     183           1 :     psOptions->padfSrcNoDataImag[0] = 2.12345678;
     184           1 :     GDALWarpResolveWorkingDataType(psOptions);
     185           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CFloat64);
     186             : 
     187           1 :     GDALDestroyWarpOptions(psOptions);
     188           1 : }
     189             : 
     190             : // GDALWarpResolveWorkingDataType: effect of padfDstNoDataReal
     191           4 : TEST_F(test_alg, GDALWarpResolveWorkingDataType_padfDstNoDataReal)
     192             : {
     193           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     194           1 :     psOptions->nBandCount = 1;
     195           1 :     psOptions->padfDstNoDataReal =
     196           1 :         static_cast<double *>(CPLMalloc(sizeof(double)));
     197           1 :     psOptions->padfDstNoDataReal[0] = 0.0;
     198           1 :     GDALWarpResolveWorkingDataType(psOptions);
     199           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     200             : 
     201           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     202           1 :     psOptions->padfDstNoDataReal[0] = -1.0;
     203           1 :     GDALWarpResolveWorkingDataType(psOptions);
     204           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Int16);
     205             : 
     206           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     207           1 :     psOptions->padfDstNoDataReal[0] = 2.0;
     208           1 :     GDALWarpResolveWorkingDataType(psOptions);
     209           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     210             : 
     211           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     212           1 :     psOptions->padfDstNoDataReal[0] = 256.0;
     213           1 :     GDALWarpResolveWorkingDataType(psOptions);
     214           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_UInt16);
     215             : 
     216           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     217           1 :     psOptions->padfDstNoDataReal[0] = 2.5;
     218           1 :     GDALWarpResolveWorkingDataType(psOptions);
     219           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Float32);
     220             : 
     221           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     222           1 :     psOptions->padfDstNoDataReal[0] = 2.12345678;
     223           1 :     GDALWarpResolveWorkingDataType(psOptions);
     224           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Float64);
     225             : 
     226           1 :     GDALDestroyWarpOptions(psOptions);
     227           1 : }
     228             : 
     229             : // GDALWarpResolveWorkingDataType: effect of padfDstNoDataImag
     230           4 : TEST_F(test_alg, GDALWarpResolveWorkingDataType_padfDstNoDataImag)
     231             : {
     232           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     233           1 :     psOptions->nBandCount = 1;
     234           1 :     psOptions->padfDstNoDataReal =
     235           1 :         static_cast<double *>(CPLMalloc(sizeof(double)));
     236           1 :     psOptions->padfDstNoDataImag =
     237           1 :         static_cast<double *>(CPLMalloc(sizeof(double)));
     238           1 :     psOptions->padfDstNoDataReal[0] = 0.0;
     239           1 :     psOptions->padfDstNoDataImag[0] = 0.0;
     240           1 :     GDALWarpResolveWorkingDataType(psOptions);
     241           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_Byte);
     242             : 
     243           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     244           1 :     psOptions->padfDstNoDataReal[0] = 0.0;
     245           1 :     psOptions->padfDstNoDataImag[0] = 1.0;
     246           1 :     GDALWarpResolveWorkingDataType(psOptions);
     247             :     // Could probably be CInt16
     248           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CInt32);
     249             : 
     250           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     251           1 :     psOptions->padfDstNoDataImag[0] = 0.0;
     252           1 :     psOptions->padfDstNoDataImag[0] = 1.5;
     253           1 :     GDALWarpResolveWorkingDataType(psOptions);
     254           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CFloat32);
     255             : 
     256           1 :     psOptions->eWorkingDataType = GDT_Unknown;
     257           1 :     psOptions->padfDstNoDataImag[0] = 0.0;
     258           1 :     psOptions->padfDstNoDataImag[0] = 2.12345678;
     259           1 :     GDALWarpResolveWorkingDataType(psOptions);
     260           1 :     EXPECT_EQ(psOptions->eWorkingDataType, GDT_CFloat64);
     261             : 
     262           1 :     GDALDestroyWarpOptions(psOptions);
     263           1 : }
     264             : 
     265             : // Test GDALAutoCreateWarpedVRT() with creation of an alpha band
     266           4 : TEST_F(test_alg, GDALAutoCreateWarpedVRT_alpha_band)
     267             : {
     268             :     GDALDatasetUniquePtr poDS(GDALDriver::FromHandle(GDALGetDriverByName("MEM"))
     269           1 :                                   ->Create("", 1, 1, 1, GDT_Byte, nullptr));
     270           1 :     poDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
     271           1 :     double adfGeoTransform[6] = {10, 1, 0, 20, 0, -1};
     272           1 :     poDS->SetGeoTransform(adfGeoTransform);
     273           1 :     GDALWarpOptions *psOptions = GDALCreateWarpOptions();
     274           1 :     psOptions->nDstAlphaBand = 2;
     275             :     GDALDatasetH hWarpedVRT =
     276           1 :         GDALAutoCreateWarpedVRT(GDALDataset::ToHandle(poDS.get()), nullptr,
     277             :                                 nullptr, GRA_NearestNeighbour, 0.0, psOptions);
     278           1 :     ASSERT_TRUE(hWarpedVRT != nullptr);
     279           1 :     ASSERT_EQ(GDALGetRasterCount(hWarpedVRT), 2);
     280           1 :     EXPECT_EQ(
     281             :         GDALGetRasterColorInterpretation(GDALGetRasterBand(hWarpedVRT, 2)),
     282             :         GCI_AlphaBand);
     283           1 :     GDALDestroyWarpOptions(psOptions);
     284           1 :     GDALClose(hWarpedVRT);
     285             : }
     286             : 
     287             : // Test GDALIsLineOfSightVisible() with single point dataset
     288           4 : TEST_F(test_alg, GDALIsLineOfSightVisible_single_point_dataset)
     289             : {
     290           1 :     auto const sizeX = 1;
     291           1 :     auto const sizeY = 1;
     292           1 :     auto const numBands = 1;
     293             :     GDALDatasetUniquePtr poDS(
     294             :         GDALDriver::FromHandle(GDALGetDriverByName("MEM"))
     295           1 :             ->Create("", sizeX, sizeY, numBands, GDT_Int8, nullptr));
     296           1 :     ASSERT_TRUE(poDS != nullptr);
     297             : 
     298           1 :     int8_t val = 42;
     299           1 :     auto pBand = poDS->GetRasterBand(1);
     300           1 :     ASSERT_TRUE(pBand != nullptr);
     301           1 :     ASSERT_TRUE(poDS->RasterIO(GF_Write, 0, 0, 1, 1, &val, 1, 1, GDT_Int8, 1,
     302             :                                nullptr, 0, 0, 0, nullptr) == CE_None);
     303             :     // Both points below terrain
     304           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 0, 0, 0.0, 0, 0, 0.0, nullptr,
     305             :                                           nullptr, nullptr));
     306             :     // One point below terrain
     307           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 0, 0, 0.0, 0, 0, 43.0, nullptr,
     308             :                                           nullptr, nullptr));
     309             :     int xIntersection, yIntersection;
     310           1 :     int *pXIntersection = &xIntersection;
     311           1 :     int *pYIntersection = &yIntersection;
     312           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(
     313             :         pBand, 0, 0, 0.0, 0, 0, 43.0, pXIntersection, pYIntersection, nullptr));
     314           1 :     EXPECT_EQ(*pXIntersection, 0);
     315           1 :     EXPECT_EQ(*pYIntersection, 0);
     316             :     // Both points above terrain
     317           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 0, 0, 44.0, 0, 0, 43.0, nullptr,
     318             :                                          nullptr, nullptr));
     319           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 0, 0, 44.0, 0, 0, 43.0,
     320             :                                          pXIntersection, pYIntersection,
     321             :                                          nullptr));
     322             : }
     323             : 
     324             : // Test GDALIsLineOfSightVisible() with 10x10 default dataset
     325           4 : TEST_F(test_alg, GDALIsLineOfSightVisible_default_square_dataset)
     326             : {
     327           1 :     auto const sizeX = 10;
     328           1 :     auto const sizeY = 10;
     329           1 :     auto const numBands = 1;
     330             :     GDALDatasetUniquePtr poDS(
     331             :         GDALDriver::FromHandle(GDALGetDriverByName("MEM"))
     332           1 :             ->Create("", sizeX, sizeY, numBands, GDT_Int8, nullptr));
     333           1 :     ASSERT_TRUE(poDS != nullptr);
     334             : 
     335           1 :     auto pBand = poDS->GetRasterBand(1);
     336           1 :     ASSERT_TRUE(pBand != nullptr);
     337             : 
     338           1 :     const int x1 = 1;
     339           1 :     const int y1 = 1;
     340           1 :     const int x2 = 2;
     341           1 :     const int y2 = 2;
     342             : 
     343             :     // Both points are above terrain.
     344           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, x1, y1, 1.0, x2, y2, 1.0,
     345             :                                          nullptr, nullptr, nullptr));
     346             :     // Both points are above terrain, supply intersection.
     347             :     int xIntersection, yIntersection;
     348           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, x1, y1, 1.0, x2, y2, 1.0,
     349             :                                          &xIntersection, &yIntersection,
     350             :                                          nullptr));
     351             :     // Flip the order, same result.
     352           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, x2, y2, 1.0, x1, y1, 1.0,
     353             :                                          nullptr, nullptr, nullptr));
     354             : 
     355             :     // One point is below terrain.
     356           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, x1, y1, -1.0, x2, y2, 1.0,
     357             :                                           nullptr, nullptr, nullptr));
     358           1 :     int *pXIntersection = &xIntersection;
     359           1 :     int *pYIntersection = &yIntersection;
     360           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, x1, y1, -1.0, x2, y2, 1.0,
     361             :                                           pXIntersection, pYIntersection,
     362             :                                           nullptr));
     363           1 :     EXPECT_EQ(*pXIntersection, 1);
     364           1 :     EXPECT_EQ(*pYIntersection, 1);
     365             :     // Flip the order, same result.
     366           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, x2, y2, -1.0, x1, y1, 1.0,
     367             :                                           pXIntersection, pYIntersection,
     368             :                                           nullptr));
     369           1 :     EXPECT_EQ(*pXIntersection, 2);
     370           1 :     EXPECT_EQ(*pYIntersection, 2);
     371             : 
     372             :     // Both points are below terrain.
     373           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, x1, y1, -1.0, x2, y2, -1.0,
     374             :                                           nullptr, nullptr, nullptr));
     375             :     // Flip the order, same result.
     376           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, x2, y2, -1.0, x1, y1, -1.0,
     377             :                                           nullptr, nullptr, nullptr));
     378             : }
     379             : 
     380             : // Test GDALIsLineOfSightVisible() through a mountain (not a unit test)
     381           4 : TEST_F(test_alg, GDALIsLineOfSightVisible_through_mountain)
     382             : {
     383           1 :     GDALAllRegister();
     384             : 
     385           1 :     const std::string path = data_ + SEP + "n43.dt0";
     386             :     const auto poDS = GDALDatasetUniquePtr(
     387           1 :         GDALDataset::FromHandle(GDALOpen(path.c_str(), GA_ReadOnly)));
     388           1 :     if (!poDS)
     389             :     {
     390           0 :         GTEST_SKIP() << "Cannot open " << path;
     391             :     }
     392             : 
     393           1 :     auto pBand = poDS->GetRasterBand(1);
     394           1 :     ASSERT_TRUE(pBand != nullptr);
     395             :     std::array<double, 6> geoFwdTransform;
     396           1 :     ASSERT_TRUE(poDS->GetGeoTransform(geoFwdTransform.data()) == CE_None);
     397             :     std::array<double, 6> geoInvTransform;
     398           1 :     ASSERT_TRUE(
     399             :         GDALInvGeoTransform(geoFwdTransform.data(), geoInvTransform.data()));
     400             : 
     401             :     // Check both sides of a mesa (north and south ends).
     402             :     // Top mesa at (x=8,y=58,alt=221)
     403           1 :     const double mesaLatTop = 43.5159;
     404           1 :     const double mesaLngTop = -79.9327;
     405             : 
     406             :     // Bottom is at (x=12,y=64,alt=199)
     407           1 :     const double mesaLatBottom = 43.4645;
     408           1 :     const double mesaLngBottom = -79.8985;
     409             : 
     410             :     // In between the two locations, the mesa reaches a local max altiude of 321.
     411             : 
     412             :     double dMesaTopX, dMesaTopY, dMesaBottomX, dMesaBottomY;
     413           1 :     GDALApplyGeoTransform(geoInvTransform.data(), mesaLngTop, mesaLatTop,
     414             :                           &dMesaTopX, &dMesaTopY);
     415           1 :     GDALApplyGeoTransform(geoInvTransform.data(), mesaLngBottom, mesaLatBottom,
     416             :                           &dMesaBottomX, &dMesaBottomY);
     417           1 :     const int iMesaTopX = static_cast<int>(dMesaTopX);
     418           1 :     const int iMesaTopY = static_cast<int>(dMesaTopY);
     419           1 :     const int iMesaBottomX = static_cast<int>(dMesaBottomX);
     420           1 :     const int iMesaBottomY = static_cast<int>(dMesaBottomY);
     421             : 
     422             :     // Both points are just above terrain, with terrain between.
     423           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, iMesaTopX, iMesaTopY, 222,
     424             :                                           iMesaBottomX, iMesaBottomY, 199,
     425             :                                           nullptr, nullptr, nullptr));
     426             :     // Flip the order, same result.
     427           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, iMesaBottomX, iMesaBottomY,
     428             :                                           199, iMesaTopX, iMesaTopY, 222,
     429             :                                           nullptr, nullptr, nullptr));
     430             : 
     431             :     // Both points above terrain.
     432           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, iMesaTopX, iMesaTopY, 322,
     433             :                                          iMesaBottomX, iMesaBottomY, 322,
     434             :                                          nullptr, nullptr, nullptr));
     435             : 
     436             :     // Both points below terrain.
     437           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, iMesaTopX, iMesaTopY, 0,
     438             :                                           iMesaBottomX, iMesaBottomY, 0,
     439             :                                           nullptr, nullptr, nullptr));
     440             : 
     441             :     // Test negative slope bresenham diagonals across the whole raster.
     442             :     // Both high above terrain.
     443           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 0, 0, 460, 120, 120, 460,
     444             :                                          nullptr, nullptr, nullptr));
     445             :     // Both heights are 1m above in the corners, but middle terrain violates LOS.
     446           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 0, 0, 295, 120, 120, 183,
     447             :                                           nullptr, nullptr, nullptr));
     448             : 
     449             :     int xIntersection, yIntersection;
     450           1 :     int *pXIntersection = &xIntersection;
     451           1 :     int *pYIntersection = &yIntersection;
     452           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 0, 0, 295, 120, 120, 183,
     453             :                                           pXIntersection, pYIntersection,
     454             :                                           nullptr));
     455           1 :     EXPECT_EQ(*pXIntersection, 2);
     456           1 :     EXPECT_EQ(*pYIntersection, 2);
     457             : 
     458             :     // Test positive slope bresenham diagnoals across the whole raster.
     459             :     // Both high above terrain.
     460           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 0, 120, 460, 120, 0, 460,
     461             :                                          nullptr, nullptr, nullptr));
     462             :     // Both heights are 1m above in the corners, but middle terrain violates LOS.
     463           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 0, 120, 203, 120, 0, 247,
     464             :                                           nullptr, nullptr, nullptr));
     465           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 0, 120, 203, 120, 0, 247,
     466             :                                           pXIntersection, pYIntersection,
     467             :                                           nullptr));
     468             : 
     469           1 :     EXPECT_EQ(*pXIntersection, 120);
     470           1 :     EXPECT_EQ(*pYIntersection, 0);
     471             : 
     472             :     // Vertical line tests with hill between two points, in both directions.
     473           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 83, 111, 154, 83, 117, 198,
     474             :                                           nullptr, nullptr, nullptr));
     475           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 83, 117, 198, 83, 111, 154,
     476             :                                           nullptr, nullptr, nullptr));
     477           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 83, 111, 460, 83, 117, 460,
     478             :                                          nullptr, nullptr, nullptr));
     479           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 83, 117, 460, 83, 111, 460,
     480             :                                          nullptr, nullptr, nullptr));
     481             : 
     482             :     // Horizontal line tests with hill between two points, in both directions.
     483           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 75, 115, 192, 89, 115, 191,
     484             :                                           nullptr, nullptr, nullptr));
     485           1 :     EXPECT_FALSE(GDALIsLineOfSightVisible(pBand, 89, 115, 191, 75, 115, 192,
     486             :                                           nullptr, nullptr, nullptr));
     487           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 75, 115, 460, 89, 115, 460,
     488             :                                          nullptr, nullptr, nullptr));
     489           1 :     EXPECT_TRUE(GDALIsLineOfSightVisible(pBand, 89, 115, 460, 75, 115, 460,
     490             :                                          nullptr, nullptr, nullptr));
     491             : }
     492             : 
     493             : }  // namespace

Generated by: LCOV version 1.14