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

Generated by: LCOV version 1.14