LCOV - code coverage report
Current view: top level - autotest/cpp - test_alg.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 302 311 97.1 %
Date: 2026-04-15 22:10:00 Functions: 58 59 98.3 %

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

Generated by: LCOV version 1.14