LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_mask.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 313 352 88.9 %
Date: 2026-04-15 22:10:00 Functions: 43 70 61.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_array_mask.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDALMDArray::GetMask() implementation
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_float.h"
      15             : #include "gdal_multidim.h"
      16             : #include "gdal_pam_multidim.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cmath>
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : /************************************************************************/
      24             : /*                           GDALMDArrayMask                            */
      25             : /************************************************************************/
      26             : 
      27             : class GDALMDArrayMask final : public GDALPamMDArray
      28             : {
      29             :   private:
      30             :     std::shared_ptr<GDALMDArray> m_poParent{};
      31             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
      32             :     double m_dfMissingValue = 0.0;
      33             :     bool m_bHasMissingValue = false;
      34             :     double m_dfFillValue = 0.0;
      35             :     bool m_bHasFillValue = false;
      36             :     double m_dfValidMin = 0.0;
      37             :     bool m_bHasValidMin = false;
      38             :     double m_dfValidMax = 0.0;
      39             :     bool m_bHasValidMax = false;
      40             :     std::vector<uint32_t> m_anValidFlagMasks{};
      41             :     std::vector<uint32_t> m_anValidFlagValues{};
      42             : 
      43             :     bool Init(CSLConstList papszOptions);
      44             : 
      45             :     template <typename Type>
      46             :     void
      47             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
      48             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
      49             :                  const void *pTempBuffer,
      50             :                  const GDALExtendedDataType &oTmpBufferDT,
      51             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
      52             : 
      53             :   protected:
      54          48 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
      55          96 :         : GDALAbstractMDArray(std::string(),
      56          96 :                               "Mask of " + poParent->GetFullName()),
      57          96 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
      58          96 :                          GDALPamMultiDim::GetPAM(poParent),
      59             :                          poParent->GetContext()),
      60         240 :           m_poParent(std::move(poParent))
      61             :     {
      62          48 :     }
      63             : 
      64             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      65             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      66             :                const GDALExtendedDataType &bufferDataType,
      67             :                void *pDstBuffer) const override;
      68             : 
      69           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      70             :                      CSLConstList papszOptions) const override
      71             :     {
      72           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      73             :     }
      74             : 
      75             :   public:
      76             :     static std::shared_ptr<GDALMDArrayMask>
      77             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
      78             :            CSLConstList papszOptions);
      79             : 
      80           1 :     bool IsWritable() const override
      81             :     {
      82           1 :         return false;
      83             :     }
      84             : 
      85          53 :     const std::string &GetFilename() const override
      86             :     {
      87          53 :         return m_poParent->GetFilename();
      88             :     }
      89             : 
      90             :     const std::vector<std::shared_ptr<GDALDimension>> &
      91         383 :     GetDimensions() const override
      92             :     {
      93         383 :         return m_poParent->GetDimensions();
      94             :     }
      95             : 
      96         138 :     const GDALExtendedDataType &GetDataType() const override
      97             :     {
      98         138 :         return m_dt;
      99             :     }
     100             : 
     101           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     102             :     {
     103           1 :         return m_poParent->GetSpatialRef();
     104             :     }
     105             : 
     106           2 :     std::vector<GUInt64> GetBlockSize() const override
     107             :     {
     108           2 :         return m_poParent->GetBlockSize();
     109             :     }
     110             : };
     111             : 
     112             : /************************************************************************/
     113             : /*                      GDALMDArrayMask::Create()                       */
     114             : /************************************************************************/
     115             : 
     116             : /* static */ std::shared_ptr<GDALMDArrayMask>
     117          48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
     118             :                         CSLConstList papszOptions)
     119             : {
     120          96 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
     121          48 :     newAr->SetSelf(newAr);
     122          48 :     if (!newAr->Init(papszOptions))
     123           6 :         return nullptr;
     124          42 :     return newAr;
     125             : }
     126             : 
     127             : /************************************************************************/
     128             : /*                       GDALMDArrayMask::Init()                        */
     129             : /************************************************************************/
     130             : 
     131          48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
     132             : {
     133             :     const auto GetSingleValNumericAttr =
     134         192 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
     135             :     {
     136         576 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
     137         192 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
     138             :         {
     139          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
     140          21 :             if (anDimSizes.empty() ||
     141          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
     142             :             {
     143          11 :                 bHasVal = true;
     144          11 :                 dfVal = poAttr->ReadAsDouble();
     145             :             }
     146             :         }
     147         192 :     };
     148             : 
     149          48 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
     150          48 :                             m_dfMissingValue);
     151          48 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
     152          48 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
     153          48 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
     154             : 
     155             :     {
     156         144 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
     157          54 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
     158          60 :             poValidRange->GetDimensionsSize()[0] == 2 &&
     159           6 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
     160             :         {
     161           6 :             m_bHasValidMin = true;
     162           6 :             m_bHasValidMax = true;
     163           6 :             auto vals = poValidRange->ReadAsDoubleArray();
     164           6 :             CPLAssert(vals.size() == 2);
     165           6 :             m_dfValidMin = vals[0];
     166           6 :             m_dfValidMax = vals[1];
     167             :         }
     168             :     }
     169             : 
     170             :     // Take into account
     171             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
     172             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
     173             :     const char *pszUnmaskFlags =
     174          48 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
     175          48 :     if (pszUnmaskFlags)
     176             :     {
     177             :         const auto IsScalarStringAttr =
     178          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
     179             :         {
     180          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
     181          26 :                    (poAttr->GetDimensionsSize().empty() ||
     182          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
     183          26 :                      poAttr->GetDimensionsSize()[0] == 1));
     184             :         };
     185             : 
     186          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
     187          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
     188             :         {
     189           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     190             :                      "UNMASK_FLAGS option specified but array has no "
     191             :                      "flag_meanings attribute");
     192           1 :             return false;
     193             :         }
     194          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
     195          13 :         if (!pszFlagMeanings)
     196             :         {
     197           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     198             :                      "Cannot read flag_meanings attribute");
     199           1 :             return false;
     200             :         }
     201             : 
     202             :         const auto IsSingleDimNumericAttr =
     203          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
     204             :         {
     205          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
     206          26 :                    poAttr->GetDimensionsSize().size() == 1;
     207             :         };
     208             : 
     209          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
     210             :         const bool bHasFlagValues =
     211          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
     212             : 
     213          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
     214             :         const bool bHasFlagMasks =
     215          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
     216             : 
     217          12 :         if (!bHasFlagValues && !bHasFlagMasks)
     218             :         {
     219           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     220             :                      "Cannot find flag_values and/or flag_masks attribute");
     221           1 :             return false;
     222             :         }
     223             : 
     224             :         const CPLStringList aosUnmaskFlags(
     225          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
     226             :         const CPLStringList aosFlagMeanings(
     227          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
     228             : 
     229          11 :         if (bHasFlagValues)
     230             :         {
     231           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
     232             :             // We could support Int64 or UInt64, but more work...
     233           7 :             if (eType != GDT_UInt8 && eType != GDT_Int8 &&
     234           7 :                 eType != GDT_UInt16 && eType != GDT_Int16 &&
     235           0 :                 eType != GDT_UInt32 && eType != GDT_Int32)
     236             :             {
     237           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
     238             :                          "Unsupported data type for flag_values attribute: %s",
     239             :                          GDALGetDataTypeName(eType));
     240           0 :                 return false;
     241             :             }
     242             :         }
     243             : 
     244          11 :         if (bHasFlagMasks)
     245             :         {
     246           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
     247             :             // We could support Int64 or UInt64, but more work...
     248           6 :             if (eType != GDT_UInt8 && eType != GDT_Int8 &&
     249           6 :                 eType != GDT_UInt16 && eType != GDT_Int16 &&
     250           0 :                 eType != GDT_UInt32 && eType != GDT_Int32)
     251             :             {
     252           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
     253             :                          "Unsupported data type for flag_masks attribute: %s",
     254             :                          GDALGetDataTypeName(eType));
     255           0 :                 return false;
     256             :             }
     257             :         }
     258             : 
     259             :         const std::vector<double> adfValues(
     260             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
     261          11 :                            : std::vector<double>());
     262             :         const std::vector<double> adfMasks(
     263             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
     264          11 :                           : std::vector<double>());
     265             : 
     266          18 :         if (bHasFlagValues &&
     267           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
     268             :         {
     269           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     270             :                      "Number of values in flag_values attribute is different "
     271             :                      "from the one in flag_meanings");
     272           1 :             return false;
     273             :         }
     274             : 
     275          16 :         if (bHasFlagMasks &&
     276           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
     277             :         {
     278           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     279             :                      "Number of values in flag_masks attribute is different "
     280             :                      "from the one in flag_meanings");
     281           1 :             return false;
     282             :         }
     283             : 
     284          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
     285             :         {
     286          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
     287          11 :             if (nIdxFlag < 0)
     288             :             {
     289           1 :                 CPLError(
     290             :                     CE_Failure, CPLE_AppDefined,
     291             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
     292             :                     aosUnmaskFlags[i], pszFlagMeanings);
     293           1 :                 return false;
     294             :             }
     295             : 
     296          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
     297             :             {
     298           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     299             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
     300           0 :                          adfValues[nIdxFlag]);
     301           0 :                 return false;
     302             :             }
     303             : 
     304          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
     305             :             {
     306           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     307             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
     308           0 :                          adfMasks[nIdxFlag]);
     309           0 :                 return false;
     310             :             }
     311             : 
     312          10 :             if (bHasFlagValues)
     313             :             {
     314          12 :                 m_anValidFlagValues.push_back(
     315           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
     316             :             }
     317             : 
     318          10 :             if (bHasFlagMasks)
     319             :             {
     320          12 :                 m_anValidFlagMasks.push_back(
     321           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
     322             :             }
     323             :         }
     324             :     }
     325             : 
     326          42 :     return true;
     327             : }
     328             : 
     329             : /************************************************************************/
     330             : /*                               IRead()                                */
     331             : /************************************************************************/
     332             : 
     333          51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     334             :                             const GInt64 *arrayStep,
     335             :                             const GPtrDiff_t *bufferStride,
     336             :                             const GDALExtendedDataType &bufferDataType,
     337             :                             void *pDstBuffer) const
     338             : {
     339          51 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
     340             :     {
     341           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     342             :                  "%s: only reading to a numeric data type is supported",
     343             :                  __func__);
     344           0 :         return false;
     345             :     }
     346          51 :     size_t nElts = 1;
     347          51 :     const size_t nDims = GetDimensionCount();
     348         102 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
     349         139 :     for (size_t i = 0; i < nDims; i++)
     350          88 :         nElts *= count[i];
     351          51 :     if (nDims > 0)
     352             :     {
     353          46 :         tmpBufferStrideVector.back() = 1;
     354          88 :         for (size_t i = nDims - 1; i > 0;)
     355             :         {
     356          42 :             --i;
     357          42 :             tmpBufferStrideVector[i] =
     358          42 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
     359             :         }
     360             :     }
     361             : 
     362             :     /* Optimized case: if we are an integer data type and that there is no */
     363             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
     364             :     /* directly */
     365          49 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
     366          74 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
     367          34 :         m_anValidFlagMasks.empty() &&
     368         111 :         m_poParent->GetRawNoDataValue() == nullptr &&
     369          11 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
     370             :     {
     371           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
     372           7 :         if (bBufferDataTypeIsByte)  // Byte case
     373             :         {
     374           4 :             bool bContiguous = true;
     375          10 :             for (size_t i = 0; i < nDims; i++)
     376             :             {
     377           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
     378             :                 {
     379           1 :                     bContiguous = false;
     380           1 :                     break;
     381             :                 }
     382             :             }
     383           4 :             if (bContiguous)
     384             :             {
     385             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
     386           3 :                 memset(pDstBuffer, 1, nElts);
     387           3 :                 return true;
     388             :             }
     389             :         }
     390             : 
     391             :         struct Stack
     392             :         {
     393             :             size_t nIters = 0;
     394             :             GByte *dst_ptr = nullptr;
     395             :             GPtrDiff_t dst_inc_offset = 0;
     396             :         };
     397             : 
     398           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
     399           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
     400          13 :         for (size_t i = 0; i < nDims; i++)
     401             :         {
     402           9 :             stack[i].dst_inc_offset =
     403           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
     404             :         }
     405           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
     406             : 
     407           4 :         size_t dimIdx = 0;
     408           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
     409             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
     410           4 :         CPLAssert(nBufferDTSize <= 16);
     411           4 :         const GByte flag = 1;
     412           4 :         GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
     413             :                         bufferDataType.GetNumericDataType(), 0, 1);
     414             : 
     415          28 :     lbl_next_depth:
     416          28 :         if (dimIdx == nDimsMinus1)
     417             :         {
     418          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
     419          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
     420             : 
     421             :             while (true)
     422             :             {
     423             :                 // cppcheck-suppress knownConditionTrueFalse
     424          73 :                 if (bBufferDataTypeIsByte)
     425             :                 {
     426          24 :                     *dst_ptr = flag;
     427             :                 }
     428             :                 else
     429             :                 {
     430          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
     431             :                 }
     432             : 
     433          73 :                 if ((--nIters) == 0)
     434          19 :                     break;
     435          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
     436             :             }
     437             :         }
     438             :         else
     439             :         {
     440           9 :             stack[dimIdx].nIters = count[dimIdx];
     441             :             while (true)
     442             :             {
     443          24 :                 dimIdx++;
     444          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     445          24 :                 goto lbl_next_depth;
     446          24 :             lbl_return_to_caller:
     447          24 :                 dimIdx--;
     448          24 :                 if ((--stack[dimIdx].nIters) == 0)
     449           9 :                     break;
     450          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     451             :             }
     452             :         }
     453          28 :         if (dimIdx > 0)
     454          24 :             goto lbl_return_to_caller;
     455             : 
     456           4 :         return true;
     457             :     }
     458             : 
     459             :     const auto oTmpBufferDT =
     460          44 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
     461             :             ? GDALExtendedDataType::Create(GDT_Float64)
     462          88 :             : m_poParent->GetDataType();
     463          44 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
     464          44 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
     465          44 :     if (!pTempBuffer)
     466           0 :         return false;
     467          88 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
     468          44 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
     469             :                           pTempBuffer))
     470             :     {
     471           0 :         VSIFree(pTempBuffer);
     472           0 :         return false;
     473             :     }
     474             : 
     475          44 :     switch (oTmpBufferDT.GetNumericDataType())
     476             :     {
     477           7 :         case GDT_UInt8:
     478           7 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
     479             :                                 pTempBuffer, oTmpBufferDT,
     480             :                                 tmpBufferStrideVector);
     481           7 :             break;
     482             : 
     483           0 :         case GDT_Int8:
     484           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
     485             :                                 pTempBuffer, oTmpBufferDT,
     486             :                                 tmpBufferStrideVector);
     487           0 :             break;
     488             : 
     489           1 :         case GDT_UInt16:
     490           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
     491             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
     492             :                                   tmpBufferStrideVector);
     493           1 :             break;
     494             : 
     495          14 :         case GDT_Int16:
     496          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
     497             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
     498             :                                  tmpBufferStrideVector);
     499          14 :             break;
     500             : 
     501           1 :         case GDT_UInt32:
     502           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
     503             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
     504             :                                   tmpBufferStrideVector);
     505           1 :             break;
     506             : 
     507           5 :         case GDT_Int32:
     508           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
     509             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
     510             :                                  tmpBufferStrideVector);
     511           5 :             break;
     512             : 
     513           0 :         case GDT_UInt64:
     514           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
     515             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
     516             :                                         tmpBufferStrideVector);
     517           0 :             break;
     518             : 
     519           0 :         case GDT_Int64:
     520           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
     521             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
     522             :                                        tmpBufferStrideVector);
     523           0 :             break;
     524             : 
     525           0 :         case GDT_Float16:
     526           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
     527             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
     528             :                                    tmpBufferStrideVector);
     529           0 :             break;
     530             : 
     531           7 :         case GDT_Float32:
     532           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
     533             :                                 pTempBuffer, oTmpBufferDT,
     534             :                                 tmpBufferStrideVector);
     535           7 :             break;
     536             : 
     537           9 :         case GDT_Float64:
     538           9 :             ReadInternal<double>(count, bufferStride, bufferDataType,
     539             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
     540             :                                  tmpBufferStrideVector);
     541           9 :             break;
     542           0 :         case GDT_Unknown:
     543             :         case GDT_CInt16:
     544             :         case GDT_CInt32:
     545             :         case GDT_CFloat16:
     546             :         case GDT_CFloat32:
     547             :         case GDT_CFloat64:
     548             :         case GDT_TypeCount:
     549           0 :             CPLAssert(false);
     550             :             break;
     551             :     }
     552             : 
     553          44 :     VSIFree(pTempBuffer);
     554             : 
     555          44 :     return true;
     556             : }
     557             : 
     558             : /************************************************************************/
     559             : /*                            IsValidForDT()                            */
     560             : /************************************************************************/
     561             : 
     562          40 : template <typename Type> static bool IsValidForDT(double dfVal)
     563             : {
     564          40 :     if (std::isnan(dfVal))
     565           0 :         return false;
     566          40 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
     567           0 :         return false;
     568          40 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
     569           0 :         return false;
     570          40 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
     571             : }
     572             : 
     573           9 : template <> bool IsValidForDT<double>(double)
     574             : {
     575           9 :     return true;
     576             : }
     577             : 
     578             : /************************************************************************/
     579             : /*                               IsNan()                                */
     580             : /************************************************************************/
     581             : 
     582        1438 : template <typename Type> inline bool IsNan(Type)
     583             : {
     584        1438 :     return false;
     585             : }
     586             : 
     587          65 : template <> bool IsNan<double>(double val)
     588             : {
     589          65 :     return std::isnan(val);
     590             : }
     591             : 
     592          26 : template <> bool IsNan<float>(float val)
     593             : {
     594          26 :     return std::isnan(val);
     595             : }
     596             : 
     597             : /************************************************************************/
     598             : /*                            ReadInternal()                            */
     599             : /************************************************************************/
     600             : 
     601             : template <typename Type>
     602          44 : void GDALMDArrayMask::ReadInternal(
     603             :     const size_t *count, const GPtrDiff_t *bufferStride,
     604             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
     605             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
     606             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
     607             : {
     608          44 :     const size_t nDims = GetDimensionCount();
     609             : 
     610         220 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
     611             :     {
     612         220 :         if (bHasVal)
     613             :         {
     614          49 :             if (IsValidForDT<Type>(dfVal))
     615             :             {
     616          49 :                 return static_cast<Type>(dfVal);
     617             :             }
     618             :             else
     619             :             {
     620           0 :                 bHasVal = false;
     621             :             }
     622             :         }
     623         171 :         return 0;
     624             :     };
     625             : 
     626          44 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
     627          44 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
     628             :     const Type nNoDataValue =
     629          44 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
     630          44 :     bool bHasMissingValue = m_bHasMissingValue;
     631          44 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
     632          44 :     bool bHasFillValue = m_bHasFillValue;
     633          44 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
     634          44 :     bool bHasValidMin = m_bHasValidMin;
     635          44 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
     636          44 :     bool bHasValidMax = m_bHasValidMax;
     637          44 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
     638          44 :     const bool bHasValidFlags =
     639          44 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
     640             : 
     641         351 :     const auto IsValidFlag = [this](Type v)
     642             :     {
     643          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
     644             :         {
     645          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
     646             :             {
     647          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
     648             :                     m_anValidFlagValues[i])
     649             :                 {
     650           4 :                     return true;
     651             :                 }
     652             :             }
     653             :         }
     654          42 :         else if (!m_anValidFlagValues.empty())
     655             :         {
     656          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
     657             :             {
     658          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
     659             :                 {
     660           4 :                     return true;
     661             :                 }
     662             :             }
     663             :         }
     664             :         else /* if( !m_anValidFlagMasks.empty() ) */
     665             :         {
     666          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
     667             :             {
     668          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
     669             :                 {
     670           9 :                     return true;
     671             :                 }
     672             :             }
     673             :         }
     674          37 :         return false;
     675             :     };
     676             : 
     677             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
     678             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
     679             :                        !(bHasMissingValue && v == nMissingValue) &&            \
     680             :                        !(bHasFillValue && v == nFillValue) &&                  \
     681             :                        !(bHasValidMin && v < nValidMin) &&                     \
     682             :                        !(bHasValidMax && v > nValidMax) &&                     \
     683             :                        (!bHasValidFlags || IsValidFlag(v)));
     684             : 
     685          44 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
     686             :     /* Optimized case: Byte output and output buffer is contiguous */
     687          44 :     if (bBufferDataTypeIsByte)
     688             :     {
     689          40 :         bool bContiguous = true;
     690         103 :         for (size_t i = 0; i < nDims; i++)
     691             :         {
     692          64 :             if (bufferStride[i] != tmpBufferStrideVector[i])
     693             :             {
     694           1 :                 bContiguous = false;
     695           1 :                 break;
     696             :             }
     697             :         }
     698          40 :         if (bContiguous)
     699             :         {
     700          39 :             size_t nElts = 1;
     701         102 :             for (size_t i = 0; i < nDims; i++)
     702          63 :                 nElts *= count[i];
     703             : 
     704        1113 :             for (size_t i = 0; i < nElts; i++)
     705             :             {
     706        1074 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
     707        1074 :                 static_cast<GByte *>(pDstBuffer)[i] =
     708        1074 :                     GET_MASK_FOR_SAMPLE(*pSrc);
     709             :             }
     710          39 :             return;
     711             :         }
     712             :     }
     713             : 
     714           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
     715             : 
     716             :     struct Stack
     717             :     {
     718             :         size_t nIters = 0;
     719             :         const GByte *src_ptr = nullptr;
     720             :         GByte *dst_ptr = nullptr;
     721             :         GPtrDiff_t src_inc_offset = 0;
     722             :         GPtrDiff_t dst_inc_offset = 0;
     723             :     };
     724             : 
     725          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
     726           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
     727          15 :     for (size_t i = 0; i < nDims; i++)
     728             :     {
     729          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
     730          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
     731          10 :         stack[i].dst_inc_offset =
     732          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
     733             :     }
     734           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
     735           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
     736             : 
     737           5 :     size_t dimIdx = 0;
     738           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
     739             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
     740           5 :     CPLAssert(nBufferDTSize <= 16);
     741          15 :     for (GByte flag = 0; flag <= 1; flag++)
     742             :     {
     743          10 :         GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
     744             :                         bufferDataType.GetNumericDataType(), 0, 1);
     745             :     }
     746             : 
     747          43 : lbl_next_depth:
     748          43 :     if (dimIdx == nDimsMinus1)
     749             :     {
     750          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
     751          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
     752          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
     753             : 
     754         420 :         while (true)
     755             :         {
     756         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
     757         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
     758             : 
     759         455 :             if (bBufferDataTypeIsByte)
     760             :             {
     761          24 :                 *dst_ptr = flag;
     762             :             }
     763             :             else
     764             :             {
     765         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
     766             :             }
     767             : 
     768         455 :             if ((--nIters) == 0)
     769          35 :                 break;
     770         420 :             src_ptr += stack[dimIdx].src_inc_offset;
     771         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
     772             :         }
     773             :     }
     774             :     else
     775             :     {
     776           8 :         stack[dimIdx].nIters = count[dimIdx];
     777             :         while (true)
     778             :         {
     779          38 :             dimIdx++;
     780          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
     781          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     782          38 :             goto lbl_next_depth;
     783          38 :         lbl_return_to_caller:
     784          38 :             dimIdx--;
     785          38 :             if ((--stack[dimIdx].nIters) == 0)
     786           8 :                 break;
     787          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
     788          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     789             :         }
     790             :     }
     791          43 :     if (dimIdx > 0)
     792          38 :         goto lbl_return_to_caller;
     793             : }
     794             : 
     795             : //! @endcond
     796             : 
     797             : /************************************************************************/
     798             : /*                              GetMask()                               */
     799             : /************************************************************************/
     800             : 
     801             : /** Return an array that is a mask for the current array
     802             : 
     803             :  This array will be of type Byte, with values set to 0 to indicate invalid
     804             :  pixels of the current array, and values set to 1 to indicate valid pixels.
     805             : 
     806             :  The generic implementation honours the NoDataValue, as well as various
     807             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
     808             :  and valid_range.
     809             : 
     810             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
     811             :  can be used to specify strings of the "flag_meanings" attribute
     812             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
     813             :  for which pixels matching any of those flags will be set at 1 in the mask array,
     814             :  and pixels matching none of those flags will be set at 0.
     815             :  For example, let's consider the following netCDF variable defined with:
     816             :  \verbatim
     817             :  l2p_flags:valid_min = 0s ;
     818             :  l2p_flags:valid_max = 256s ;
     819             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
     820             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
     821             :  \endverbatim
     822             : 
     823             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
     824             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
     825             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
     826             :    will be 1.
     827             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
     828             :    will be 0.
     829             : 
     830             :  This is the same as the C function GDALMDArrayGetMask().
     831             : 
     832             :  @param papszOptions NULL-terminated list of options, or NULL.
     833             : 
     834             :  @return a new array, that holds a reference to the original one, and thus is
     835             :  a view of it (not a copy), or nullptr in case of error.
     836             : */
     837             : std::shared_ptr<GDALMDArray>
     838          49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
     839             : {
     840          98 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
     841          49 :     if (!self)
     842             :     {
     843           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     844             :                  "Driver implementation issue: m_pSelf not set !");
     845           0 :         return nullptr;
     846             :     }
     847          49 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
     848             :     {
     849           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     850             :                  "GetMask() only supports numeric data type");
     851           1 :         return nullptr;
     852             :     }
     853          48 :     return GDALMDArrayMask::Create(self, papszOptions);
     854             : }

Generated by: LCOV version 1.14