LCOV - code coverage report
Current view: top level - autotest/cpp - test_alg.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 269 278 96.8 %
Date: 2025-07-09 17:50:03 Functions: 50 51 98.0 %

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

Generated by: LCOV version 1.14