LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_gltorthorectification.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 208 227 91.6 %
Date: 2026-04-15 22:10:00 Functions: 15 17 88.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Name:     gdalmultidim_array_gltorthorectification.cpp
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  GLT orthorectification implementation
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_priv.h"
      14             : #include "gdal_pam_multidim.h"
      15             : 
      16             : #include <algorithm>
      17             : #include <limits>
      18             : 
      19             : /************************************************************************/
      20             : /*                        GLTOrthoRectifiedArray                        */
      21             : /************************************************************************/
      22             : 
      23             : class GLTOrthoRectifiedArray final : public GDALPamMDArray
      24             : {
      25             :   private:
      26             :     std::shared_ptr<GDALMDArray> m_poParent{};
      27             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
      28             :     std::vector<GUInt64> m_anBlockSize;
      29             :     GDALExtendedDataType m_dt;
      30             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
      31             :     std::shared_ptr<GDALMDArray> m_poVarX{};
      32             :     std::shared_ptr<GDALMDArray> m_poVarY{};
      33             :     std::shared_ptr<GDALMDArray> m_poGLTX{};
      34             :     std::shared_ptr<GDALMDArray> m_poGLTY{};
      35             :     int m_nGLTIndexOffset = 0;
      36             :     std::vector<GByte> m_abyBandValidity{};
      37             : 
      38             :   protected:
      39           9 :     GLTOrthoRectifiedArray(
      40             :         const std::shared_ptr<GDALMDArray> &poParent,
      41             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
      42             :         const std::vector<GUInt64> &anBlockSize)
      43          18 :         : GDALAbstractMDArray(std::string(), "GLTOrthoRectifiedArray view of " +
      44           9 :                                                  poParent->GetFullName()),
      45          18 :           GDALPamMDArray(std::string(),
      46          18 :                          "GLTOrthoRectifiedArray view of " +
      47           9 :                              poParent->GetFullName(),
      48          18 :                          GDALPamMultiDim::GetPAM(poParent)),
      49           9 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
      50          45 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
      51             :     {
      52           9 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
      53           9 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
      54           9 :     }
      55             : 
      56             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      57             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      58             :                const GDALExtendedDataType &bufferDataType,
      59             :                void *pDstBuffer) const override;
      60             : 
      61             :   public:
      62             :     static std::shared_ptr<GDALMDArray>
      63           9 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
      64             :            const std::shared_ptr<GDALGroup> &poRootGroup,
      65             :            const std::shared_ptr<GDALMDArray> &poGLTX,
      66             :            const std::shared_ptr<GDALMDArray> &poGLTY, int nGLTIndexOffset,
      67             :            const std::vector<double> &adfGeoTransform,
      68             :            CSLConstList papszOptions)
      69             :     {
      70          18 :         std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
      71             : 
      72             :         auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
      73           0 :             std::string(), "lat", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
      74          18 :             poGLTX->GetDimensions()[0]->GetSize());
      75             :         auto varY = GDALMDArrayRegularlySpaced::Create(
      76          27 :             std::string(), poDimY->GetName(), poDimY,
      77          36 :             adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
      78           9 :         poDimY->SetIndexingVariable(varY);
      79           9 :         apoNewDims.emplace_back(poDimY);
      80             : 
      81             :         auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
      82           0 :             std::string(), "lon", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
      83          18 :             poGLTX->GetDimensions()[1]->GetSize());
      84             :         auto varX = GDALMDArrayRegularlySpaced::Create(
      85          27 :             std::string(), poDimX->GetName(), poDimX,
      86          36 :             adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
      87           9 :         poDimX->SetIndexingVariable(varX);
      88           9 :         apoNewDims.emplace_back(poDimX);
      89             : 
      90           9 :         if (poParent->GetDimensionCount() == 3)
      91           8 :             apoNewDims.emplace_back(poParent->GetDimensions()[2]);
      92             : 
      93             :         std::vector<GUInt64> anBlockSize = std::vector<GUInt64>{
      94           9 :             std::min<GUInt64>(apoNewDims[0]->GetSize(), 512),
      95          18 :             std::min<GUInt64>(apoNewDims[1]->GetSize(), 512)};
      96           9 :         if (poParent->GetDimensionCount() == 3)
      97             :         {
      98           8 :             anBlockSize.push_back(poParent->GetDimensions()[2]->GetSize());
      99             :         }
     100             : 
     101             :         auto newAr(std::shared_ptr<GLTOrthoRectifiedArray>(
     102          18 :             new GLTOrthoRectifiedArray(poParent, apoNewDims, anBlockSize)));
     103           9 :         newAr->SetSelf(newAr);
     104           9 :         newAr->m_poVarX = varX;
     105           9 :         newAr->m_poVarY = varY;
     106           9 :         newAr->m_poGLTX = poGLTX;
     107           9 :         newAr->m_poGLTY = poGLTY;
     108           9 :         newAr->m_nGLTIndexOffset = nGLTIndexOffset;
     109          18 :         OGRSpatialReference oSRS;
     110           9 :         oSRS.importFromEPSG(4326);
     111           9 :         newAr->m_poSRS.reset(oSRS.Clone());
     112           9 :         newAr->m_poSRS->SetDataAxisToSRSAxisMapping(
     113             :             {/*latIdx = */ 1, /* lonIdx = */ 2});
     114           9 :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
     115          17 :                                              "USE_GOOD_WAVELENGTHS", "YES")) &&
     116           8 :             newAr->m_poParent->GetDimensionCount() == 3)
     117             :         {
     118             :             const auto poGoodWaveLengths = poRootGroup->OpenMDArrayFromFullname(
     119          21 :                 "/sensor_band_parameters/good_wavelengths");
     120          10 :             if (poGoodWaveLengths &&
     121          13 :                 poGoodWaveLengths->GetDimensionCount() == 1 &&
     122           3 :                 poGoodWaveLengths->GetDimensions()[0]->GetSize() ==
     123           6 :                     newAr->m_poParent->GetDimensions()[2]->GetSize() &&
     124           3 :                 poGoodWaveLengths->GetDimensions()[0]->GetSize() <
     125          10 :                     1000 * 1000 &&
     126           3 :                 poGoodWaveLengths->GetDataType().GetClass() == GEDTC_NUMERIC)
     127             :             {
     128           3 :                 const GUInt64 arrayStartIdx[] = {0};
     129             :                 const size_t count[] = {static_cast<size_t>(
     130           3 :                     poGoodWaveLengths->GetDimensions()[0]->GetSize())};
     131           3 :                 const GInt64 arrayStep[] = {1};
     132           3 :                 const GPtrDiff_t bufferStride[] = {1};
     133           3 :                 newAr->m_abyBandValidity.resize(count[0], 1);
     134           6 :                 CPL_IGNORE_RET_VAL(poGoodWaveLengths->Read(
     135             :                     arrayStartIdx, count, arrayStep, bufferStride,
     136           6 :                     GDALExtendedDataType::Create(GDT_UInt8),
     137           3 :                     newAr->m_abyBandValidity.data()));
     138             :             }
     139             :         }
     140          18 :         return newAr;
     141             :     }
     142             : 
     143           2 :     bool IsWritable() const override
     144             :     {
     145           2 :         return false;
     146             :     }
     147             : 
     148          16 :     const std::string &GetFilename() const override
     149             :     {
     150          16 :         return m_poParent->GetFilename();
     151             :     }
     152             : 
     153             :     const std::vector<std::shared_ptr<GDALDimension>> &
     154         107 :     GetDimensions() const override
     155             :     {
     156         107 :         return m_apoDims;
     157             :     }
     158             : 
     159          77 :     const GDALExtendedDataType &GetDataType() const override
     160             :     {
     161          77 :         return m_dt;
     162             :     }
     163             : 
     164           4 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     165             :     {
     166           4 :         return m_poSRS;
     167             :     }
     168             : 
     169           5 :     std::vector<GUInt64> GetBlockSize() const override
     170             :     {
     171           5 :         return m_anBlockSize;
     172             :     }
     173             : 
     174             :     std::shared_ptr<GDALAttribute>
     175           3 :     GetAttribute(const std::string &osName) const override
     176             :     {
     177           3 :         return m_poParent->GetAttribute(osName);
     178             :     }
     179             : 
     180             :     std::vector<std::shared_ptr<GDALAttribute>>
     181           2 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     182             :     {
     183           2 :         return m_poParent->GetAttributes(papszOptions);
     184             :     }
     185             : 
     186           3 :     const std::string &GetUnit() const override
     187             :     {
     188           3 :         return m_poParent->GetUnit();
     189             :     }
     190             : 
     191          19 :     const void *GetRawNoDataValue() const override
     192             :     {
     193          19 :         return m_poParent->GetRawNoDataValue();
     194             :     }
     195             : 
     196           0 :     double GetOffset(bool *pbHasOffset,
     197             :                      GDALDataType *peStorageType) const override
     198             :     {
     199           0 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
     200             :     }
     201             : 
     202           0 :     double GetScale(bool *pbHasScale,
     203             :                     GDALDataType *peStorageType) const override
     204             :     {
     205           0 :         return m_poParent->GetScale(pbHasScale, peStorageType);
     206             :     }
     207             : };
     208             : 
     209             : /************************************************************************/
     210             : /*                    GDALMDArrayResampled::IRead()                     */
     211             : /************************************************************************/
     212             : 
     213          16 : bool GLTOrthoRectifiedArray::IRead(const GUInt64 *arrayStartIdx,
     214             :                                    const size_t *count, const GInt64 *arrayStep,
     215             :                                    const GPtrDiff_t *bufferStride,
     216             :                                    const GDALExtendedDataType &bufferDataType,
     217             :                                    void *pDstBuffer) const
     218             : {
     219          16 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
     220           0 :         return false;
     221             : 
     222          16 :     const auto eBufferDT = bufferDataType.GetNumericDataType();
     223          16 :     auto pRawNoDataValue = GetRawNoDataValue();
     224          32 :     std::vector<GByte> abyNoData(16);
     225          16 :     if (pRawNoDataValue)
     226          16 :         GDALCopyWords64(pRawNoDataValue, GetDataType().GetNumericDataType(), 0,
     227          16 :                         abyNoData.data(), eBufferDT, 0, 1);
     228             : 
     229          16 :     const auto nBufferDTSize = bufferDataType.GetSize();
     230             :     const int nCopyWordsDstStride =
     231          16 :         m_apoDims.size() == 3
     232          16 :             ? static_cast<int>(bufferStride[2] * nBufferDTSize)
     233          16 :             : 0;
     234             :     const int nOutBandCount =
     235          16 :         m_apoDims.size() == 3 ? static_cast<int>(count[2]) : 1;
     236             : 
     237             :     const auto FillBufferWithNodata =
     238           5 :         [count, bufferStride, pDstBuffer, nBufferDTSize, &eBufferDT,
     239          48 :          nCopyWordsDstStride, nOutBandCount, &abyNoData]()
     240             :     {
     241          12 :         for (size_t iY = 0; iY < count[0]; ++iY)
     242             :         {
     243          13 :             if (nOutBandCount == 1 && bufferStride[1] > 0 &&
     244           6 :                 static_cast<int>(nBufferDTSize) <
     245           6 :                     std::numeric_limits<int>::max() / bufferStride[1])
     246             :             {
     247           6 :                 GDALCopyWords64(
     248           6 :                     abyNoData.data(), eBufferDT, 0,
     249             :                     static_cast<GByte *>(pDstBuffer) +
     250           6 :                         iY * bufferStride[0] * static_cast<int>(nBufferDTSize),
     251             :                     eBufferDT,
     252           6 :                     static_cast<int>(bufferStride[1] * nBufferDTSize),
     253           6 :                     count[1]);
     254             :             }
     255             :             else
     256             :             {
     257           3 :                 for (size_t iX = 0; iX < count[1]; ++iX)
     258             :                 {
     259           2 :                     GByte *pabyDstBuffer =
     260             :                         static_cast<GByte *>(pDstBuffer) +
     261           2 :                         (iY * bufferStride[0] + iX * bufferStride[1]) *
     262           2 :                             static_cast<int>(nBufferDTSize);
     263           2 :                     GDALCopyWords64(abyNoData.data(), eBufferDT, 0,
     264             :                                     pabyDstBuffer, eBufferDT,
     265             :                                     nCopyWordsDstStride, nOutBandCount);
     266             :                 }
     267             :             }
     268             :         }
     269          21 :     };
     270             : 
     271          16 :     bool bSomeBandInvalids = false;
     272          16 :     if (!m_abyBandValidity.empty())
     273             :     {
     274           3 :         size_t nCountInvalid = 0;
     275           7 :         for (size_t i = 0; i < count[2]; ++i)
     276             :         {
     277           4 :             const size_t iBand =
     278           4 :                 static_cast<size_t>(arrayStartIdx[2] + i * arrayStep[2]);
     279           4 :             CPLAssert(iBand < m_abyBandValidity.size());
     280           4 :             if (!m_abyBandValidity[iBand])
     281           2 :                 nCountInvalid++;
     282             :         }
     283           3 :         if (nCountInvalid == count[2])
     284             :         {
     285           1 :             FillBufferWithNodata();
     286           1 :             return true;
     287             :         }
     288           2 :         bSomeBandInvalids = true;
     289             :     }
     290             : 
     291          15 :     const size_t nXYValsCount = count[0] * count[1];
     292          30 :     const auto eInt32DT = GDALExtendedDataType::Create(GDT_Int32);
     293          30 :     std::vector<int32_t> anGLTX;
     294          30 :     std::vector<int32_t> anGLTY;
     295             :     try
     296             :     {
     297          15 :         anGLTX.resize(nXYValsCount);
     298          15 :         anGLTY.resize(nXYValsCount);
     299             :     }
     300           0 :     catch (const std::bad_alloc &e)
     301             :     {
     302           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     303           0 :                  "GLTOrthoRectifiedArray::IRead(): %s", e.what());
     304           0 :         return false;
     305             :     }
     306          30 :     if (!m_poGLTX->Read(arrayStartIdx, count, arrayStep, nullptr, eInt32DT,
     307          45 :                         anGLTX.data()) ||
     308          30 :         !m_poGLTY->Read(arrayStartIdx, count, arrayStep, nullptr, eInt32DT,
     309          15 :                         anGLTY.data()))
     310             :     {
     311           0 :         return false;
     312             :     }
     313             : 
     314          15 :     int32_t nMinX = std::numeric_limits<int32_t>::max();
     315          15 :     int32_t nMaxX = std::numeric_limits<int32_t>::min();
     316          15 :     const auto nXSize = m_poParent->GetDimensions()[0]->GetSize();
     317         104 :     for (size_t i = 0; i < nXYValsCount; ++i)
     318             :     {
     319             :         const int64_t nX64 =
     320          89 :             static_cast<int64_t>(anGLTX[i]) + m_nGLTIndexOffset;
     321          89 :         if (nX64 >= 0 && static_cast<uint64_t>(nX64) < nXSize)
     322             :         {
     323          38 :             const int32_t nX = static_cast<int32_t>(nX64);
     324          38 :             if (nX < nMinX)
     325          11 :                 nMinX = nX;
     326          38 :             if (nX > nMaxX)
     327          20 :                 nMaxX = nX;
     328             :         }
     329             :     }
     330             : 
     331          15 :     int32_t nMinY = std::numeric_limits<int32_t>::max();
     332          15 :     int32_t nMaxY = std::numeric_limits<int32_t>::min();
     333          15 :     const auto nYSize = m_poParent->GetDimensions()[0]->GetSize();
     334         104 :     for (size_t i = 0; i < nXYValsCount; ++i)
     335             :     {
     336             :         const int64_t nY64 =
     337          89 :             static_cast<int64_t>(anGLTY[i]) + m_nGLTIndexOffset;
     338          89 :         if (nY64 >= 0 && static_cast<uint64_t>(nY64) < nYSize)
     339             :         {
     340          38 :             const int32_t nY = static_cast<int32_t>(nY64);
     341          38 :             if (nY < nMinY)
     342          20 :                 nMinY = nY;
     343          38 :             if (nY > nMaxY)
     344          11 :                 nMaxY = nY;
     345             :         }
     346             :     }
     347             : 
     348          15 :     if (nMinX > nMaxX || nMinY > nMaxY)
     349             :     {
     350           4 :         FillBufferWithNodata();
     351           4 :         return true;
     352             :     }
     353             : 
     354             :     GUInt64 parentArrayIdxStart[3] = {
     355          11 :         static_cast<GUInt64>(nMinY), static_cast<GUInt64>(nMinX),
     356          11 :         m_apoDims.size() == 3 ? arrayStartIdx[2] : 0};
     357          11 :     size_t parentCount[3] = {static_cast<size_t>(nMaxY - nMinY + 1),
     358          11 :                              static_cast<size_t>(nMaxX - nMinX + 1),
     359          11 :                              m_apoDims.size() == 3 ? count[2] : 1};
     360          11 :     GInt64 parentArrayStep[3] = {1, 1,
     361          11 :                                  m_apoDims.size() == 3 ? arrayStep[2] : 0};
     362             : 
     363          11 :     size_t nParentValueSize = nBufferDTSize;
     364          44 :     for (int i = 0; i < 3; ++i)
     365             :     {
     366          66 :         if (parentCount[i] >
     367          33 :             std::numeric_limits<size_t>::max() / nParentValueSize)
     368             :         {
     369           0 :             CPLError(
     370             :                 CE_Failure, CPLE_OutOfMemory,
     371             :                 "GLTOrthoRectifiedArray::IRead(): too big temporary array");
     372           0 :             return false;
     373             :         }
     374          33 :         nParentValueSize *= parentCount[i];
     375             :     }
     376             : 
     377          11 :     GPtrDiff_t parentStride[3] = {
     378          11 :         static_cast<GPtrDiff_t>(parentCount[1] * parentCount[2]),
     379          11 :         static_cast<GPtrDiff_t>(parentCount[2]), 1};
     380          22 :     std::vector<GByte> parentValues;
     381             :     try
     382             :     {
     383          11 :         parentValues.resize(nParentValueSize);
     384             :     }
     385           0 :     catch (const std::bad_alloc &e)
     386             :     {
     387           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     388           0 :                  "GLTOrthoRectifiedArray::IRead(): %s", e.what());
     389           0 :         return false;
     390             :     }
     391          22 :     if (!m_poParent->Read(parentArrayIdxStart, parentCount, parentArrayStep,
     392          11 :                           parentStride, bufferDataType, parentValues.data()))
     393             :     {
     394           0 :         return false;
     395             :     }
     396             : 
     397          11 :     size_t iGLTIndex = 0;
     398          11 :     const size_t nXCount = parentCount[1];
     399          11 :     const size_t nBandCount = m_apoDims.size() == 3 ? parentCount[2] : 1;
     400          40 :     for (size_t iY = 0; iY < count[0]; ++iY)
     401             :     {
     402         112 :         for (size_t iX = 0; iX < count[1]; ++iX, ++iGLTIndex)
     403             :         {
     404             :             const int64_t nX64 =
     405          83 :                 static_cast<int64_t>(anGLTX[iGLTIndex]) + m_nGLTIndexOffset;
     406             :             const int64_t nY64 =
     407          83 :                 static_cast<int64_t>(anGLTY[iGLTIndex]) + m_nGLTIndexOffset;
     408          83 :             GByte *pabyDstBuffer =
     409             :                 static_cast<GByte *>(pDstBuffer) +
     410          83 :                 (iY * bufferStride[0] + iX * bufferStride[1]) *
     411          83 :                     static_cast<int>(nBufferDTSize);
     412          83 :             if (nX64 >= nMinX && nX64 <= nMaxX && nY64 >= nMinY &&
     413          38 :                 nY64 <= nMaxY)
     414             :             {
     415          38 :                 const int32_t iSrcX = static_cast<int32_t>(nX64) - nMinX;
     416          38 :                 const int32_t iSrcY = static_cast<int32_t>(nY64) - nMinY;
     417             :                 const GByte *pabySrcBuffer =
     418          38 :                     parentValues.data() +
     419          38 :                     (iSrcY * nXCount + iSrcX) * nBandCount * nBufferDTSize;
     420          38 :                 GDALCopyWords64(pabySrcBuffer, eBufferDT,
     421             :                                 static_cast<int>(nBufferDTSize), pabyDstBuffer,
     422             :                                 eBufferDT, nCopyWordsDstStride, nOutBandCount);
     423          38 :                 if (bSomeBandInvalids)
     424             :                 {
     425          20 :                     for (size_t i = 0; i < count[2]; ++i)
     426             :                     {
     427          12 :                         const size_t iBand = static_cast<size_t>(
     428          12 :                             arrayStartIdx[2] + i * arrayStep[2]);
     429          12 :                         if (!m_abyBandValidity[iBand])
     430             :                         {
     431           4 :                             GDALCopyWords64(abyNoData.data(), eBufferDT, 0,
     432           4 :                                             pabyDstBuffer +
     433           4 :                                                 i * nCopyWordsDstStride,
     434             :                                             eBufferDT, 0, 1);
     435             :                         }
     436             :                     }
     437          38 :                 }
     438             :             }
     439             :             else
     440             :             {
     441          45 :                 GDALCopyWords64(abyNoData.data(), eBufferDT, 0, pabyDstBuffer,
     442             :                                 eBufferDT, nCopyWordsDstStride, nOutBandCount);
     443             :             }
     444             :         }
     445             :     }
     446             : 
     447          11 :     return true;
     448             : }
     449             : 
     450             : /************************************************************************/
     451             : /*                      CreateGLTOrthorectified()                       */
     452             : /************************************************************************/
     453             : 
     454             : //! @cond Doxygen_Suppress
     455             : 
     456           9 : /* static */ std::shared_ptr<GDALMDArray> GDALMDArray::CreateGLTOrthorectified(
     457             :     const std::shared_ptr<GDALMDArray> &poParent,
     458             :     const std::shared_ptr<GDALGroup> &poRootGroup,
     459             :     const std::shared_ptr<GDALMDArray> &poGLTX,
     460             :     const std::shared_ptr<GDALMDArray> &poGLTY, int nGLTIndexOffset,
     461             :     const std::vector<double> &adfGeoTransform, CSLConstList papszOptions)
     462             : {
     463             :     return GLTOrthoRectifiedArray::Create(poParent, poRootGroup, poGLTX, poGLTY,
     464             :                                           nGLTIndexOffset, adfGeoTransform,
     465           9 :                                           papszOptions);
     466             : }
     467             : 
     468             : //! @endcond

Generated by: LCOV version 1.14