LCOV - code coverage report
Current view: top level - alg - gdalapplyverticalshiftgrid.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 187 251 74.5 %
Date: 2024-05-04 12:52:34 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL algorithms
       4             :  * Purpose:  Apply vertical shift grid
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_string.h"
      30             : #include "gdal.h"
      31             : #include "gdal_alg.h"
      32             : #include "gdal_alg_priv.h"
      33             : #include "gdal_priv.h"
      34             : #include "gdal_utils.h"
      35             : #include "gdalwarper.h"
      36             : #include "vrtdataset.h"
      37             : #include "ogr_spatialref.h"
      38             : 
      39             : #include "proj.h"
      40             : 
      41             : #include <limits>
      42             : 
      43             : /************************************************************************/
      44             : /*                        GDALApplyVSGDataset                           */
      45             : /************************************************************************/
      46             : 
      47             : class GDALApplyVSGDataset final : public GDALDataset
      48             : {
      49             :     friend class GDALApplyVSGRasterBand;
      50             : 
      51             :     GDALDataset *m_poSrcDataset = nullptr;
      52             :     GDALDataset *m_poReprojectedGrid = nullptr;
      53             :     bool m_bInverse = false;
      54             :     double m_dfSrcUnitToMeter = 0.0;
      55             :     double m_dfDstUnitToMeter = 0.0;
      56             : 
      57             :     CPL_DISALLOW_COPY_ASSIGN(GDALApplyVSGDataset)
      58             : 
      59             :   public:
      60             :     GDALApplyVSGDataset(GDALDataset *poSrcDataset,
      61             :                         GDALDataset *poReprojectedGrid, GDALDataType eDT,
      62             :                         bool bInverse, double dfSrcUnitToMeter,
      63             :                         double dfDstUnitToMeter, int nBlockSize);
      64             :     virtual ~GDALApplyVSGDataset();
      65             : 
      66             :     virtual int CloseDependentDatasets() override;
      67             : 
      68             :     virtual CPLErr GetGeoTransform(double *padfGeoTransform) override;
      69             :     virtual const OGRSpatialReference *GetSpatialRef() const override;
      70             : 
      71             :     bool IsInitOK();
      72             : };
      73             : 
      74             : /************************************************************************/
      75             : /*                       GDALApplyVSGRasterBand                         */
      76             : /************************************************************************/
      77             : 
      78             : class GDALApplyVSGRasterBand final : public GDALRasterBand
      79             : {
      80             :     friend class GDALApplyVSGDataset;
      81             : 
      82             :     float *m_pafSrcData = nullptr;
      83             :     float *m_pafGridData = nullptr;
      84             : 
      85             :     CPL_DISALLOW_COPY_ASSIGN(GDALApplyVSGRasterBand)
      86             : 
      87             :   public:
      88             :     GDALApplyVSGRasterBand(GDALDataType eDT, int nBlockSize);
      89             :     virtual ~GDALApplyVSGRasterBand();
      90             : 
      91             :     virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
      92             :                               void *pData) override;
      93             :     virtual double GetNoDataValue(int *pbSuccess) override;
      94             : };
      95             : 
      96             : /************************************************************************/
      97             : /*                        GDALApplyVSGDataset()                         */
      98             : /************************************************************************/
      99             : 
     100          13 : GDALApplyVSGDataset::GDALApplyVSGDataset(GDALDataset *poSrcDataset,
     101             :                                          GDALDataset *poReprojectedGrid,
     102             :                                          GDALDataType eDT, bool bInverse,
     103             :                                          double dfSrcUnitToMeter,
     104             :                                          double dfDstUnitToMeter,
     105          13 :                                          int nBlockSize)
     106             :     : m_poSrcDataset(poSrcDataset), m_poReprojectedGrid(poReprojectedGrid),
     107             :       m_bInverse(bInverse), m_dfSrcUnitToMeter(dfSrcUnitToMeter),
     108          13 :       m_dfDstUnitToMeter(dfDstUnitToMeter)
     109             : {
     110          13 :     m_poSrcDataset->Reference();
     111          13 :     m_poReprojectedGrid->Reference();
     112             : 
     113          13 :     nRasterXSize = poSrcDataset->GetRasterXSize();
     114          13 :     nRasterYSize = poSrcDataset->GetRasterYSize();
     115          13 :     SetBand(1, new GDALApplyVSGRasterBand(eDT, nBlockSize));
     116          13 : }
     117             : 
     118             : /************************************************************************/
     119             : /*                       ~GDALApplyVSGDataset()                         */
     120             : /************************************************************************/
     121             : 
     122          26 : GDALApplyVSGDataset::~GDALApplyVSGDataset()
     123             : {
     124          13 :     GDALApplyVSGDataset::CloseDependentDatasets();
     125          26 : }
     126             : 
     127             : /************************************************************************/
     128             : /*                     CloseDependentDatasets()                         */
     129             : /************************************************************************/
     130             : 
     131          13 : int GDALApplyVSGDataset::CloseDependentDatasets()
     132             : {
     133          13 :     bool bRet = false;
     134          13 :     if (m_poSrcDataset != nullptr)
     135             :     {
     136          13 :         if (m_poSrcDataset->ReleaseRef())
     137             :         {
     138           8 :             bRet = true;
     139             :         }
     140          13 :         m_poSrcDataset = nullptr;
     141             :     }
     142          13 :     if (m_poReprojectedGrid != nullptr)
     143             :     {
     144          13 :         if (m_poReprojectedGrid->ReleaseRef())
     145             :         {
     146          13 :             bRet = true;
     147             :         }
     148          13 :         m_poReprojectedGrid = nullptr;
     149             :     }
     150          13 :     return bRet;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                          GetGeoTransform()                           */
     155             : /************************************************************************/
     156             : 
     157           2 : CPLErr GDALApplyVSGDataset::GetGeoTransform(double *padfGeoTransform)
     158             : {
     159           2 :     return m_poSrcDataset->GetGeoTransform(padfGeoTransform);
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                          GetSpatialRef()                             */
     164             : /************************************************************************/
     165             : 
     166           2 : const OGRSpatialReference *GDALApplyVSGDataset::GetSpatialRef() const
     167             : {
     168           2 :     return m_poSrcDataset->GetSpatialRef();
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                             IsInitOK()                               */
     173             : /************************************************************************/
     174             : 
     175          13 : bool GDALApplyVSGDataset::IsInitOK()
     176             : {
     177             :     GDALApplyVSGRasterBand *poBand =
     178          13 :         reinterpret_cast<GDALApplyVSGRasterBand *>(GetRasterBand(1));
     179          13 :     return poBand->m_pafSrcData != nullptr && poBand->m_pafGridData != nullptr;
     180             : }
     181             : 
     182             : /************************************************************************/
     183             : /*                       GDALApplyVSGRasterBand()                       */
     184             : /************************************************************************/
     185             : 
     186          13 : GDALApplyVSGRasterBand::GDALApplyVSGRasterBand(GDALDataType eDT, int nBlockSize)
     187             : {
     188          13 :     eDataType = eDT;
     189          13 :     nBlockXSize = nBlockSize;
     190          13 :     nBlockYSize = nBlockSize;
     191          13 :     m_pafSrcData = static_cast<float *>(
     192          13 :         VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, sizeof(float)));
     193          13 :     m_pafGridData = static_cast<float *>(
     194          13 :         VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, sizeof(float)));
     195          13 : }
     196             : 
     197             : /************************************************************************/
     198             : /*                      ~GDALApplyVSGRasterBand()                       */
     199             : /************************************************************************/
     200             : 
     201          26 : GDALApplyVSGRasterBand::~GDALApplyVSGRasterBand()
     202             : {
     203          13 :     VSIFree(m_pafSrcData);
     204          13 :     VSIFree(m_pafGridData);
     205          26 : }
     206             : 
     207             : /************************************************************************/
     208             : /*                           GetNoDataValue()                           */
     209             : /************************************************************************/
     210             : 
     211          19 : double GDALApplyVSGRasterBand::GetNoDataValue(int *pbSuccess)
     212             : {
     213          19 :     GDALApplyVSGDataset *poGDS = reinterpret_cast<GDALApplyVSGDataset *>(poDS);
     214          19 :     return poGDS->m_poSrcDataset->GetRasterBand(1)->GetNoDataValue(pbSuccess);
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                              IReadBlock()                            */
     219             : /************************************************************************/
     220             : 
     221          17 : CPLErr GDALApplyVSGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     222             :                                           void *pData)
     223             : {
     224          17 :     GDALApplyVSGDataset *poGDS = reinterpret_cast<GDALApplyVSGDataset *>(poDS);
     225             : 
     226          17 :     const int nXOff = nBlockXOff * nBlockXSize;
     227          34 :     const int nReqXSize = (nXOff > nRasterXSize - nBlockXSize)
     228          17 :                               ? nRasterXSize - nXOff
     229             :                               : nBlockXSize;
     230          17 :     const int nYOff = nBlockYOff * nBlockYSize;
     231          34 :     const int nReqYSize = (nYOff > nRasterYSize - nBlockYSize)
     232          17 :                               ? nRasterYSize - nYOff
     233             :                               : nBlockYSize;
     234             : 
     235          17 :     CPLErr eErr = poGDS->m_poSrcDataset->GetRasterBand(1)->RasterIO(
     236          17 :         GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, m_pafSrcData, nReqXSize,
     237          17 :         nReqYSize, GDT_Float32, sizeof(float), nBlockXSize * sizeof(float),
     238             :         nullptr);
     239          17 :     if (eErr == CE_None)
     240          34 :         eErr = poGDS->m_poReprojectedGrid->GetRasterBand(1)->RasterIO(
     241          17 :             GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, m_pafGridData,
     242             :             nReqXSize, nReqYSize, GDT_Float32, sizeof(float),
     243          17 :             nBlockXSize * sizeof(float), nullptr);
     244          17 :     if (eErr == CE_None)
     245             :     {
     246          17 :         const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     247          17 :         int bHasNoData = FALSE;
     248          17 :         float fNoDataValue = static_cast<float>(GetNoDataValue(&bHasNoData));
     249         279 :         for (int iY = 0; iY < nReqYSize; iY++)
     250             :         {
     251        4666 :             for (int iX = 0; iX < nReqXSize; iX++)
     252             :             {
     253        4404 :                 const float fSrcVal = m_pafSrcData[iY * nBlockXSize + iX];
     254        4404 :                 const float fGridVal = m_pafGridData[iY * nBlockXSize + iX];
     255        4404 :                 if (bHasNoData && fSrcVal == fNoDataValue)
     256             :                 {
     257             :                 }
     258        4403 :                 else if (CPLIsInf(fGridVal))
     259             :                 {
     260           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
     261             :                              "Missing vertical grid value at source (%d,%d)",
     262             :                              nXOff + iX, nYOff + iY);
     263           2 :                     return CE_Failure;
     264             :                 }
     265        4401 :                 else if (poGDS->m_bInverse)
     266             :                 {
     267         800 :                     m_pafSrcData[iY * nBlockXSize + iX] = static_cast<float>(
     268         800 :                         (fSrcVal * poGDS->m_dfSrcUnitToMeter - fGridVal) /
     269         800 :                         poGDS->m_dfDstUnitToMeter);
     270             :                 }
     271             :                 else
     272             :                 {
     273        3601 :                     m_pafSrcData[iY * nBlockXSize + iX] = static_cast<float>(
     274        3601 :                         (fSrcVal * poGDS->m_dfSrcUnitToMeter + fGridVal) /
     275        3601 :                         poGDS->m_dfDstUnitToMeter);
     276             :                 }
     277             :             }
     278         262 :             GDALCopyWords(
     279         262 :                 m_pafSrcData + iY * nBlockXSize, GDT_Float32, sizeof(float),
     280         262 :                 static_cast<GByte *>(pData) + iY * nBlockXSize * nDTSize,
     281             :                 eDataType, nDTSize, nReqXSize);
     282             :         }
     283             :     }
     284             : 
     285          15 :     return eErr;
     286             : }
     287             : 
     288             : /************************************************************************/
     289             : /*                      GDALApplyVerticalShiftGrid()                    */
     290             : /************************************************************************/
     291             : 
     292             : /** Apply a vertical shift grid to a source (DEM typically) dataset.
     293             :  *
     294             :  * hGridDataset will typically use WGS84 as horizontal datum (but this is
     295             :  * not a requirement) and its values are the values to add to go from geoid
     296             :  * elevations to WGS84 ellipsoidal heights.
     297             :  *
     298             :  * hGridDataset will be on-the-fly reprojected and resampled to the projection
     299             :  * and resolution of hSrcDataset, using bilinear resampling by default.
     300             :  *
     301             :  * Both hSrcDataset and hGridDataset must be single band datasets, and have
     302             :  * a valid geotransform and projection.
     303             :  *
     304             :  * On success, a reference will be taken on hSrcDataset and hGridDataset.
     305             :  * Reference counting semantics on the source and grid datasets should be
     306             :  * honoured. That is, don't just GDALClose() it, unless it was opened with
     307             :  * GDALOpenShared(), but rather use GDALReleaseDataset() if wanting to
     308             :  * immediately release the reference(s) and make the returned dataset the
     309             :  * owner of them.
     310             :  *
     311             :  * Valid use cases:
     312             :  *
     313             :  * \code
     314             :  * hSrcDataset = GDALOpen(...)
     315             :  * hGridDataset = GDALOpen(...)
     316             :  * hDstDataset = GDALApplyVerticalShiftGrid(hSrcDataset, hGridDataset, ...)
     317             :  * GDALReleaseDataset(hSrcDataset);
     318             :  * GDALReleaseDataset(hGridDataset);
     319             :  * if( hDstDataset )
     320             :  * {
     321             :  *     // Do things with hDstDataset
     322             :  *     GDALClose(hDstDataset) // will close hSrcDataset and hGridDataset
     323             :  * }
     324             :  * \endcode
     325             : 
     326             :  *
     327             :  * @param hSrcDataset source (DEM) dataset. Must not be NULL.
     328             :  * @param hGridDataset vertical grid shift dataset. Must not be NULL.
     329             :  * @param bInverse if set to FALSE, hGridDataset values will be added to
     330             :  *                 hSrcDataset. If set to TRUE, they will be subtracted.
     331             :  * @param dfSrcUnitToMeter the factor to convert values from hSrcDataset to
     332             :  *                         meters (1.0 if source values are in meter).
     333             :  * @param dfDstUnitToMeter the factor to convert shifted values from meter
     334             :  *                          (1.0 if output values must be in meter).
     335             :  * @param papszOptions list of options, or NULL. Supported options are:
     336             :  * <ul>
     337             :  * <li>RESAMPLING=NEAREST/BILINEAR/CUBIC. Defaults to BILINEAR.</li>
     338             :  * <li>MAX_ERROR=val. Maximum error measured in input pixels that is allowed in
     339             :  * approximating the transformation (0.0 for exact calculations). Defaults
     340             :  * to 0.125</li>
     341             :  * <li>DATATYPE=Byte/UInt16/Int16/Float32/Float64. Output data type. If not
     342             :  * specified will be the same as the one of hSrcDataset.
     343             :  * <li>ERROR_ON_MISSING_VERT_SHIFT=YES/NO. Whether a missing/nodata value in
     344             :  * hGridDataset should cause I/O requests to fail. Default is NO (in which case
     345             :  * 0 will be used)
     346             :  * <li>SRC_SRS=srs_def. Override projection on hSrcDataset;
     347             :  * </ul>
     348             :  *
     349             :  * @return a new dataset corresponding to hSrcDataset adjusted with
     350             :  * hGridDataset, or NULL. If not NULL, it must be closed with GDALClose().
     351             :  *
     352             :  * @since GDAL 2.2
     353             :  * @deprecated GDAL 3.4. Will be removed in GDAL 4.0. This function was used
     354             :  *             by gdalwarp initially, but is no longer needed.
     355             :  */
     356          23 : GDALDatasetH GDALApplyVerticalShiftGrid(GDALDatasetH hSrcDataset,
     357             :                                         GDALDatasetH hGridDataset, int bInverse,
     358             :                                         double dfSrcUnitToMeter,
     359             :                                         double dfDstUnitToMeter,
     360             :                                         const char *const *papszOptions)
     361             : {
     362          23 :     VALIDATE_POINTER1(hSrcDataset, "GDALApplyVerticalShiftGrid", nullptr);
     363          23 :     VALIDATE_POINTER1(hGridDataset, "GDALApplyVerticalShiftGrid", nullptr);
     364             : 
     365             :     double adfSrcGT[6];
     366          23 :     if (GDALGetGeoTransform(hSrcDataset, adfSrcGT) != CE_None)
     367             :     {
     368           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     369             :                  "Source dataset has no geotransform.");
     370           1 :         return nullptr;
     371             :     }
     372          22 :     const char *pszSrcProjection = CSLFetchNameValue(papszOptions, "SRC_SRS");
     373          44 :     OGRSpatialReference oSrcSRS;
     374          22 :     if (pszSrcProjection != nullptr && pszSrcProjection[0] != '\0')
     375             :     {
     376           0 :         oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     377           0 :         oSrcSRS.SetFromUserInput(pszSrcProjection);
     378             :     }
     379             :     else
     380             :     {
     381          22 :         auto poSRS = GDALDataset::FromHandle(hSrcDataset)->GetSpatialRef();
     382          22 :         if (poSRS)
     383          21 :             oSrcSRS = *poSRS;
     384             :     }
     385             : 
     386          22 :     if (oSrcSRS.IsCompound())
     387             :     {
     388           0 :         oSrcSRS.StripVertical();
     389             :     }
     390             : 
     391          22 :     if (oSrcSRS.IsEmpty())
     392             :     {
     393           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     394             :                  "Source dataset has no projection.");
     395           1 :         return nullptr;
     396             :     }
     397          21 :     if (GDALGetRasterCount(hSrcDataset) != 1)
     398             :     {
     399           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     400             :                  "Only single band source dataset is supported.");
     401           1 :         return nullptr;
     402             :     }
     403             : 
     404             :     double adfGridGT[6];
     405          20 :     if (GDALGetGeoTransform(hGridDataset, adfGridGT) != CE_None)
     406             :     {
     407           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     408             :                  "Grid dataset has no geotransform.");
     409           1 :         return nullptr;
     410             :     }
     411             : 
     412          19 :     OGRSpatialReferenceH hGridSRS = GDALGetSpatialRef(hGridDataset);
     413          19 :     if (hGridSRS == nullptr)
     414             :     {
     415           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     416             :                  "Grid dataset has no projection.");
     417           1 :         return nullptr;
     418             :     }
     419          18 :     if (GDALGetRasterCount(hGridDataset) != 1)
     420             :     {
     421           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     422             :                  "Only single band grid dataset is supported.");
     423           1 :         return nullptr;
     424             :     }
     425             : 
     426          17 :     GDALDataType eDT = GDALGetRasterDataType(GDALGetRasterBand(hSrcDataset, 1));
     427          17 :     const char *pszDataType = CSLFetchNameValue(papszOptions, "DATATYPE");
     428          17 :     if (pszDataType)
     429           2 :         eDT = GDALGetDataTypeByName(pszDataType);
     430          17 :     if (eDT == GDT_Unknown)
     431             :     {
     432           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid DATATYPE=%s",
     433             :                  pszDataType);
     434           1 :         return nullptr;
     435             :     }
     436             : 
     437          16 :     const int nSrcXSize = GDALGetRasterXSize(hSrcDataset);
     438          16 :     const int nSrcYSize = GDALGetRasterYSize(hSrcDataset);
     439             : 
     440          16 :     double dfWestLongitudeDeg = 0.0;
     441          16 :     double dfSouthLatitudeDeg = 0.0;
     442          16 :     double dfEastLongitudeDeg = 0.0;
     443          16 :     double dfNorthLatitudeDeg = 0.0;
     444          16 :     GDALComputeAreaOfInterest(&oSrcSRS, adfSrcGT, nSrcXSize, nSrcYSize,
     445             :                               dfWestLongitudeDeg, dfSouthLatitudeDeg,
     446             :                               dfEastLongitudeDeg, dfNorthLatitudeDeg);
     447             : 
     448          32 :     CPLStringList aosOptions;
     449          16 :     if (!(dfWestLongitudeDeg == 0.0 && dfSouthLatitudeDeg == 0.0 &&
     450           1 :           dfEastLongitudeDeg == 0.0 && dfNorthLatitudeDeg == 0.0))
     451             :     {
     452             :         aosOptions.SetNameValue(
     453             :             "AREA_OF_INTEREST",
     454             :             CPLSPrintf("%.16g,%.16g,%.16g,%.16g", dfWestLongitudeDeg,
     455             :                        dfSouthLatitudeDeg, dfEastLongitudeDeg,
     456          15 :                        dfNorthLatitudeDeg));
     457             :     }
     458          16 :     void *hTransform = GDALCreateGenImgProjTransformer4(
     459             :         hGridSRS, adfGridGT, OGRSpatialReference::ToHandle(&oSrcSRS), adfSrcGT,
     460          16 :         aosOptions.List());
     461          16 :     if (hTransform == nullptr)
     462           3 :         return nullptr;
     463          13 :     GDALWarpOptions *psWO = GDALCreateWarpOptions();
     464          13 :     psWO->hSrcDS = hGridDataset;
     465          13 :     psWO->eResampleAlg = GRA_Bilinear;
     466          13 :     const char *pszResampling = CSLFetchNameValue(papszOptions, "RESAMPLING");
     467          13 :     if (pszResampling)
     468             :     {
     469           3 :         if (EQUAL(pszResampling, "NEAREST"))
     470           1 :             psWO->eResampleAlg = GRA_NearestNeighbour;
     471           2 :         else if (EQUAL(pszResampling, "BILINEAR"))
     472           1 :             psWO->eResampleAlg = GRA_Bilinear;
     473           1 :         else if (EQUAL(pszResampling, "CUBIC"))
     474           1 :             psWO->eResampleAlg = GRA_Cubic;
     475             :     }
     476          13 :     psWO->eWorkingDataType = GDT_Float32;
     477          13 :     int bHasNoData = FALSE;
     478          13 :     const double dfSrcNoData = GDALGetRasterNoDataValue(
     479             :         GDALGetRasterBand(hGridDataset, 1), &bHasNoData);
     480          13 :     if (bHasNoData)
     481             :     {
     482           2 :         psWO->padfSrcNoDataReal =
     483           2 :             static_cast<double *>(CPLMalloc(sizeof(double)));
     484           2 :         psWO->padfSrcNoDataReal[0] = dfSrcNoData;
     485             :     }
     486             : 
     487          13 :     psWO->padfDstNoDataReal = static_cast<double *>(CPLMalloc(sizeof(double)));
     488             :     const bool bErrorOnMissingShift =
     489          13 :         CPLFetchBool(papszOptions, "ERROR_ON_MISSING_VERT_SHIFT", false);
     490          13 :     psWO->padfDstNoDataReal[0] =
     491          13 :         (bErrorOnMissingShift) ? -std::numeric_limits<float>::infinity() : 0.0;
     492          13 :     psWO->papszWarpOptions =
     493          13 :         CSLSetNameValue(psWO->papszWarpOptions, "INIT_DEST", "NO_DATA");
     494             : 
     495          13 :     psWO->pfnTransformer = GDALGenImgProjTransform;
     496          13 :     psWO->pTransformerArg = hTransform;
     497             :     const double dfMaxError =
     498          13 :         CPLAtof(CSLFetchNameValueDef(papszOptions, "MAX_ERROR", "0.125"));
     499          13 :     if (dfMaxError > 0.0)
     500             :     {
     501          13 :         psWO->pTransformerArg = GDALCreateApproxTransformer(
     502             :             psWO->pfnTransformer, psWO->pTransformerArg, dfMaxError);
     503          13 :         psWO->pfnTransformer = GDALApproxTransform;
     504          13 :         GDALApproxTransformerOwnsSubtransformer(psWO->pTransformerArg, TRUE);
     505             :     }
     506          13 :     psWO->nBandCount = 1;
     507          13 :     psWO->panSrcBands = static_cast<int *>(CPLMalloc(sizeof(int)));
     508          13 :     psWO->panSrcBands[0] = 1;
     509          13 :     psWO->panDstBands = static_cast<int *>(CPLMalloc(sizeof(int)));
     510          13 :     psWO->panDstBands[0] = 1;
     511             : 
     512             :     VRTWarpedDataset *poReprojectedGrid =
     513          13 :         new VRTWarpedDataset(nSrcXSize, nSrcYSize);
     514             :     // This takes a reference on hGridDataset
     515          13 :     CPLErr eErr = poReprojectedGrid->Initialize(psWO);
     516          13 :     CPLAssert(eErr == CE_None);
     517          13 :     CPL_IGNORE_RET_VAL(eErr);
     518          13 :     GDALDestroyWarpOptions(psWO);
     519          13 :     poReprojectedGrid->SetGeoTransform(adfSrcGT);
     520          13 :     poReprojectedGrid->AddBand(GDT_Float32, nullptr);
     521             : 
     522             :     GDALApplyVSGDataset *poOutDS = new GDALApplyVSGDataset(
     523          13 :         GDALDataset::FromHandle(hSrcDataset), poReprojectedGrid, eDT,
     524          13 :         CPL_TO_BOOL(bInverse), dfSrcUnitToMeter, dfDstUnitToMeter,
     525             :         // Undocumented option. For testing only
     526          13 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", "256")));
     527             : 
     528          13 :     poReprojectedGrid->ReleaseRef();
     529             : 
     530          13 :     if (!poOutDS->IsInitOK())
     531             :     {
     532           1 :         delete poOutDS;
     533           1 :         return nullptr;
     534             :     }
     535          12 :     poOutDS->SetDescription(GDALGetDescription(hSrcDataset));
     536          12 :     return reinterpret_cast<GDALDatasetH>(poOutDS);
     537             : }
     538             : 
     539             : /************************************************************************/
     540             : /*                           GetProj4Filename()                         */
     541             : /************************************************************************/
     542             : 
     543           0 : static CPLString GetProj4Filename(const char *pszFilename)
     544             : {
     545           0 :     CPLString osFilename;
     546             : 
     547             :     /* or fixed path: /name, ./name or ../name */
     548           0 :     if (!CPLIsFilenameRelative(pszFilename) || *pszFilename == '.')
     549             :     {
     550           0 :         return pszFilename;
     551             :     }
     552             : 
     553           0 :     PJ_GRID_INFO info = proj_grid_info(pszFilename);
     554           0 :     if (info.filename[0])
     555             :     {
     556           0 :         osFilename = info.filename;
     557             :     }
     558             : 
     559           0 :     return osFilename;
     560             : }
     561             : 
     562             : /************************************************************************/
     563             : /*                       GDALOpenVerticalShiftGrid()                    */
     564             : /************************************************************************/
     565             : 
     566             : /** Load proj.4 geoidgrids as GDAL dataset
     567             :  *
     568             :  * @param pszProj4Geoidgrids Value of proj.4 geoidgrids parameter.
     569             :  * @param pbError If not NULL, the pointed value will be set to TRUE if an
     570             :  *                error occurred.
     571             :  *
     572             :  * @return a dataset. If not NULL, it must be closed with GDALClose().
     573             :  *
     574             :  * @since GDAL 2.2
     575             :  * @deprecated GDAL 3.4. Will be removed in GDAL 4.0. This function was used
     576             :  *             by gdalwarp initially, but is no longer needed.
     577             :  */
     578           0 : GDALDatasetH GDALOpenVerticalShiftGrid(const char *pszProj4Geoidgrids,
     579             :                                        int *pbError)
     580             : {
     581           0 :     char **papszGrids = CSLTokenizeString2(pszProj4Geoidgrids, ",", 0);
     582           0 :     const int nGridCount = CSLCount(papszGrids);
     583           0 :     if (nGridCount == 1)
     584             :     {
     585           0 :         CSLDestroy(papszGrids);
     586             : 
     587           0 :         bool bMissingOk = false;
     588           0 :         if (*pszProj4Geoidgrids == '@')
     589             :         {
     590           0 :             pszProj4Geoidgrids++;
     591           0 :             bMissingOk = true;
     592             :         }
     593           0 :         const CPLString osFilename(GetProj4Filename(pszProj4Geoidgrids));
     594           0 :         const char *const papszOpenOptions[] = {
     595             :             "@SHIFT_ORIGIN_IN_MINUS_180_PLUS_180=YES", nullptr};
     596             :         GDALDatasetH hDS =
     597           0 :             GDALOpenEx(osFilename, 0, nullptr, papszOpenOptions, nullptr);
     598           0 :         if (hDS == nullptr)
     599             :         {
     600           0 :             CPLDebug("GDAL", "Cannot find file corresponding to %s",
     601             :                      pszProj4Geoidgrids);
     602             :         }
     603           0 :         if (pbError)
     604           0 :             *pbError = (!bMissingOk && hDS == nullptr);
     605           0 :         return hDS;
     606             :     }
     607             : 
     608           0 :     CPLStringList aosFilenames;
     609           0 :     for (int i = nGridCount - 1; i >= 0; i--)
     610             :     {
     611           0 :         const char *pszName = papszGrids[i];
     612           0 :         bool bMissingOk = false;
     613           0 :         if (*pszName == '@')
     614             :         {
     615           0 :             pszName++;
     616           0 :             bMissingOk = true;
     617             :         }
     618           0 :         const CPLString osFilename(GetProj4Filename(pszName));
     619             :         VSIStatBufL sStat;
     620           0 :         if (osFilename.empty() || VSIStatL(osFilename, &sStat) != 0)
     621             :         {
     622           0 :             CPLDebug("GDAL", "Cannot find file corresponding to %s", pszName);
     623           0 :             if (!bMissingOk)
     624             :             {
     625           0 :                 if (pbError)
     626           0 :                     *pbError = true;
     627           0 :                 CSLDestroy(papszGrids);
     628           0 :                 return nullptr;
     629             :             }
     630             :         }
     631             :         else
     632             :         {
     633           0 :             aosFilenames.AddString(osFilename);
     634             :         }
     635             :     }
     636             : 
     637           0 :     CSLDestroy(papszGrids);
     638             : 
     639           0 :     if (aosFilenames.empty())
     640             :     {
     641           0 :         if (pbError)
     642           0 :             *pbError = false;
     643           0 :         return nullptr;
     644             :     }
     645             : 
     646           0 :     char **papszArgv = nullptr;
     647           0 :     papszArgv = CSLAddString(papszArgv, "-resolution");
     648           0 :     papszArgv = CSLAddString(papszArgv, "highest");
     649           0 :     papszArgv = CSLAddString(papszArgv, "-vrtnodata");
     650           0 :     papszArgv = CSLAddString(papszArgv, "-inf");
     651           0 :     papszArgv = CSLAddString(papszArgv, "-oo");
     652             :     papszArgv =
     653           0 :         CSLAddString(papszArgv, "@SHIFT_ORIGIN_IN_MINUS_180_PLUS_180=YES");
     654           0 :     GDALBuildVRTOptions *psOptions = GDALBuildVRTOptionsNew(papszArgv, nullptr);
     655           0 :     CSLDestroy(papszArgv);
     656           0 :     GDALDatasetH hDS = GDALBuildVRT("", aosFilenames.size(), nullptr,
     657           0 :                                     aosFilenames.List(), psOptions, nullptr);
     658           0 :     GDALBuildVRTOptionsFree(psOptions);
     659           0 :     if (pbError)
     660           0 :         *pbError = hDS != nullptr;
     661           0 :     return hDS;
     662             : }

Generated by: LCOV version 1.14