LCOV - code coverage report
Current view: top level - alg - gdalrasterize.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 502 751 66.8 %
Date: 2025-02-20 10:14:44 Functions: 17 36 47.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Vector rasterization.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal_alg.h"
      16             : #include "gdal_alg_priv.h"
      17             : 
      18             : #include <climits>
      19             : #include <cstddef>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : #include <cfloat>
      23             : #include <limits>
      24             : #include <vector>
      25             : #include <algorithm>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_progress.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_vsi.h"
      32             : #include "gdal.h"
      33             : #include "gdal_priv.h"
      34             : #include "gdal_priv_templates.hpp"
      35             : #include "ogr_api.h"
      36             : #include "ogr_core.h"
      37             : #include "ogr_feature.h"
      38             : #include "ogr_geometry.h"
      39             : #include "ogr_spatialref.h"
      40             : #include "ogrsf_frmts.h"
      41             : 
      42           0 : template <typename T> static inline T SaturatedAddSigned(T a, T b)
      43             : {
      44           0 :     if (a > 0 && b > 0 && a > std::numeric_limits<T>::max() - b)
      45             :     {
      46           0 :         return std::numeric_limits<T>::max();
      47             :     }
      48           0 :     else if (a < 0 && b < 0 && a < std::numeric_limits<T>::min() - b)
      49             :     {
      50           0 :         return std::numeric_limits<T>::min();
      51             :     }
      52             :     else
      53             :     {
      54           0 :         return a + b;
      55             :     }
      56             : }
      57             : 
      58             : /************************************************************************/
      59             : /*                              MakeKey()                               */
      60             : /************************************************************************/
      61             : 
      62         781 : inline uint64_t MakeKey(int y, int x)
      63             : {
      64         781 :     return (static_cast<uint64_t>(y) << 32) | static_cast<uint64_t>(x);
      65             : }
      66             : 
      67             : /************************************************************************/
      68             : /*                        gvBurnScanlineBasic()                         */
      69             : /************************************************************************/
      70             : template <typename T>
      71        8460 : static inline void gvBurnScanlineBasic(GDALRasterizeInfo *psInfo, int nY,
      72             :                                        int nXStart, int nXEnd, double dfVariant)
      73             : 
      74             : {
      75       17498 :     for (int iBand = 0; iBand < psInfo->nBands; iBand++)
      76             :     {
      77        9038 :         const double burnValue =
      78        9038 :             (psInfo->burnValues.double_values[iBand] +
      79        9038 :              ((psInfo->eBurnValueSource == GBV_UserBurnValue) ? 0 : dfVariant));
      80             : 
      81        9038 :         unsigned char *pabyInsert =
      82        9038 :             psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
      83        9038 :             nY * psInfo->nLineSpace + nXStart * psInfo->nPixelSpace;
      84        9038 :         if (psInfo->eMergeAlg == GRMA_Add)
      85             :         {
      86         265 :             if (psInfo->poSetVisitedPoints)
      87             :             {
      88         265 :                 CPLAssert(!psInfo->bFillSetVisitedPoints);
      89         265 :                 uint64_t nKey = MakeKey(nY, nXStart);
      90         265 :                 auto &oSetVisitedPoints = *(psInfo->poSetVisitedPoints);
      91        3755 :                 for (int nX = nXStart; nX <= nXEnd; ++nX)
      92             :                 {
      93        3490 :                     if (oSetVisitedPoints.find(nKey) == oSetVisitedPoints.end())
      94             :                     {
      95        3376 :                         double dfVal = static_cast<double>(
      96        3376 :                                            *reinterpret_cast<T *>(pabyInsert)) +
      97             :                                        burnValue;
      98        3376 :                         GDALCopyWord(dfVal, *reinterpret_cast<T *>(pabyInsert));
      99             :                     }
     100        3490 :                     pabyInsert += psInfo->nPixelSpace;
     101        3490 :                     ++nKey;
     102             :                 }
     103             :             }
     104             :             else
     105             :             {
     106           0 :                 for (int nX = nXStart; nX <= nXEnd; ++nX)
     107             :                 {
     108           0 :                     double dfVal = static_cast<double>(
     109           0 :                                        *reinterpret_cast<T *>(pabyInsert)) +
     110             :                                    burnValue;
     111           0 :                     GDALCopyWord(dfVal, *reinterpret_cast<T *>(pabyInsert));
     112           0 :                     pabyInsert += psInfo->nPixelSpace;
     113             :                 }
     114             :             }
     115             :         }
     116             :         else
     117             :         {
     118             :             T nVal;
     119        8773 :             GDALCopyWord(burnValue, nVal);
     120    11584429 :             for (int nX = nXStart; nX <= nXEnd; ++nX)
     121             :             {
     122    11575678 :                 *reinterpret_cast<T *>(pabyInsert) = nVal;
     123    11575678 :                 pabyInsert += psInfo->nPixelSpace;
     124             :             }
     125             :         }
     126             :     }
     127        8460 : }
     128             : 
     129           3 : static inline void gvBurnScanlineInt64UserBurnValue(GDALRasterizeInfo *psInfo,
     130             :                                                     int nY, int nXStart,
     131             :                                                     int nXEnd)
     132             : 
     133             : {
     134           6 :     for (int iBand = 0; iBand < psInfo->nBands; iBand++)
     135             :     {
     136           3 :         const std::int64_t burnValue = psInfo->burnValues.int64_values[iBand];
     137             : 
     138           3 :         unsigned char *pabyInsert =
     139           3 :             psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
     140           3 :             nY * psInfo->nLineSpace + nXStart * psInfo->nPixelSpace;
     141           3 :         if (psInfo->eMergeAlg == GRMA_Add)
     142             :         {
     143           0 :             if (psInfo->poSetVisitedPoints)
     144             :             {
     145           0 :                 CPLAssert(!psInfo->bFillSetVisitedPoints);
     146           0 :                 uint64_t nKey = MakeKey(nY, nXStart);
     147           0 :                 auto &oSetVisitedPoints = *(psInfo->poSetVisitedPoints);
     148           0 :                 for (int nX = nXStart; nX <= nXEnd; ++nX)
     149             :                 {
     150           0 :                     if (oSetVisitedPoints.find(nKey) == oSetVisitedPoints.end())
     151             :                     {
     152           0 :                         *reinterpret_cast<std::int64_t *>(pabyInsert) =
     153           0 :                             SaturatedAddSigned(
     154             :                                 *reinterpret_cast<std::int64_t *>(pabyInsert),
     155             :                                 burnValue);
     156             :                     }
     157           0 :                     pabyInsert += psInfo->nPixelSpace;
     158           0 :                     ++nKey;
     159             :                 }
     160             :             }
     161             :             else
     162             :             {
     163           0 :                 for (int nX = nXStart; nX <= nXEnd; ++nX)
     164             :                 {
     165           0 :                     *reinterpret_cast<std::int64_t *>(pabyInsert) =
     166           0 :                         SaturatedAddSigned(
     167             :                             *reinterpret_cast<std::int64_t *>(pabyInsert),
     168             :                             burnValue);
     169           0 :                     pabyInsert += psInfo->nPixelSpace;
     170             :                 }
     171             :             }
     172             :         }
     173             :         else
     174             :         {
     175           9 :             for (int nX = nXStart; nX <= nXEnd; ++nX)
     176             :             {
     177           6 :                 *reinterpret_cast<std::int64_t *>(pabyInsert) = burnValue;
     178           6 :                 pabyInsert += psInfo->nPixelSpace;
     179             :             }
     180             :         }
     181             :     }
     182           3 : }
     183             : 
     184             : /************************************************************************/
     185             : /*                           gvBurnScanline()                           */
     186             : /************************************************************************/
     187        8477 : static void gvBurnScanline(GDALRasterizeInfo *psInfo, int nY, int nXStart,
     188             :                            int nXEnd, double dfVariant)
     189             : 
     190             : {
     191        8477 :     if (nXStart > nXEnd)
     192          14 :         return;
     193             : 
     194        8463 :     CPLAssert(nY >= 0 && nY < psInfo->nYSize);
     195        8463 :     CPLAssert(nXStart < psInfo->nXSize);
     196        8463 :     CPLAssert(nXEnd >= 0);
     197             : 
     198        8463 :     if (nXStart < 0)
     199        6145 :         nXStart = 0;
     200        8463 :     if (nXEnd >= psInfo->nXSize)
     201         113 :         nXEnd = psInfo->nXSize - 1;
     202             : 
     203        8463 :     if (psInfo->eBurnValueType == GDT_Int64)
     204             :     {
     205           3 :         if (psInfo->eType == GDT_Int64 &&
     206           3 :             psInfo->eBurnValueSource == GBV_UserBurnValue)
     207             :         {
     208           3 :             gvBurnScanlineInt64UserBurnValue(psInfo, nY, nXStart, nXEnd);
     209             :         }
     210             :         else
     211             :         {
     212           0 :             CPLAssert(false);
     213             :         }
     214           3 :         return;
     215             :     }
     216             : 
     217        8460 :     switch (psInfo->eType)
     218             :     {
     219        7709 :         case GDT_Byte:
     220        7709 :             gvBurnScanlineBasic<GByte>(psInfo, nY, nXStart, nXEnd, dfVariant);
     221        7709 :             break;
     222           0 :         case GDT_Int8:
     223           0 :             gvBurnScanlineBasic<GInt8>(psInfo, nY, nXStart, nXEnd, dfVariant);
     224           0 :             break;
     225           0 :         case GDT_Int16:
     226           0 :             gvBurnScanlineBasic<GInt16>(psInfo, nY, nXStart, nXEnd, dfVariant);
     227           0 :             break;
     228           0 :         case GDT_UInt16:
     229           0 :             gvBurnScanlineBasic<GUInt16>(psInfo, nY, nXStart, nXEnd, dfVariant);
     230           0 :             break;
     231           0 :         case GDT_Int32:
     232           0 :             gvBurnScanlineBasic<GInt32>(psInfo, nY, nXStart, nXEnd, dfVariant);
     233           0 :             break;
     234           0 :         case GDT_UInt32:
     235           0 :             gvBurnScanlineBasic<GUInt32>(psInfo, nY, nXStart, nXEnd, dfVariant);
     236           0 :             break;
     237           0 :         case GDT_Int64:
     238           0 :             gvBurnScanlineBasic<std::int64_t>(psInfo, nY, nXStart, nXEnd,
     239             :                                               dfVariant);
     240           0 :             break;
     241           0 :         case GDT_UInt64:
     242           0 :             gvBurnScanlineBasic<std::uint64_t>(psInfo, nY, nXStart, nXEnd,
     243             :                                                dfVariant);
     244           0 :             break;
     245           0 :         case GDT_Float16:
     246           0 :             gvBurnScanlineBasic<GFloat16>(psInfo, nY, nXStart, nXEnd,
     247             :                                           dfVariant);
     248           0 :             break;
     249         263 :         case GDT_Float32:
     250         263 :             gvBurnScanlineBasic<float>(psInfo, nY, nXStart, nXEnd, dfVariant);
     251         263 :             break;
     252         488 :         case GDT_Float64:
     253         488 :             gvBurnScanlineBasic<double>(psInfo, nY, nXStart, nXEnd, dfVariant);
     254         488 :             break;
     255           0 :         case GDT_CInt16:
     256             :         case GDT_CInt32:
     257             :         case GDT_CFloat16:
     258             :         case GDT_CFloat32:
     259             :         case GDT_CFloat64:
     260             :         case GDT_Unknown:
     261             :         case GDT_TypeCount:
     262           0 :             CPLAssert(false);
     263             :             break;
     264             :     }
     265             : }
     266             : 
     267             : /************************************************************************/
     268             : /*                        gvBurnPointBasic()                            */
     269             : /************************************************************************/
     270             : template <typename T>
     271       42241 : static inline void gvBurnPointBasic(GDALRasterizeInfo *psInfo, int nY, int nX,
     272             :                                     double dfVariant)
     273             : 
     274             : {
     275       87301 :     for (int iBand = 0; iBand < psInfo->nBands; iBand++)
     276             :     {
     277       45060 :         double burnValue =
     278       45060 :             (psInfo->burnValues.double_values[iBand] +
     279       45060 :              ((psInfo->eBurnValueSource == GBV_UserBurnValue) ? 0 : dfVariant));
     280       45060 :         unsigned char *pbyInsert =
     281       45060 :             psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
     282       45060 :             nY * psInfo->nLineSpace + nX * psInfo->nPixelSpace;
     283             : 
     284       45060 :         T *pbyPixel = reinterpret_cast<T *>(pbyInsert);
     285       45060 :         if (psInfo->eMergeAlg == GRMA_Add)
     286         966 :             burnValue += static_cast<double>(*pbyPixel);
     287       45060 :         GDALCopyWord(burnValue, *pbyPixel);
     288             :     }
     289       42241 : }
     290             : 
     291           0 : static inline void gvBurnPointInt64UserBurnValue(GDALRasterizeInfo *psInfo,
     292             :                                                  int nY, int nX)
     293             : 
     294             : {
     295           0 :     for (int iBand = 0; iBand < psInfo->nBands; iBand++)
     296             :     {
     297           0 :         std::int64_t burnValue = psInfo->burnValues.int64_values[iBand];
     298           0 :         unsigned char *pbyInsert =
     299           0 :             psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
     300           0 :             nY * psInfo->nLineSpace + nX * psInfo->nPixelSpace;
     301             : 
     302           0 :         std::int64_t *pbyPixel = reinterpret_cast<std::int64_t *>(pbyInsert);
     303           0 :         if (psInfo->eMergeAlg == GRMA_Add)
     304             :         {
     305           0 :             burnValue = SaturatedAddSigned(burnValue, *pbyPixel);
     306             :         }
     307           0 :         *pbyPixel = burnValue;
     308             :     }
     309           0 : }
     310             : 
     311             : /************************************************************************/
     312             : /*                            gvBurnPoint()                             */
     313             : /************************************************************************/
     314       42249 : static void gvBurnPoint(GDALRasterizeInfo *psInfo, int nY, int nX,
     315             :                         double dfVariant)
     316             : 
     317             : {
     318             : 
     319       42249 :     CPLAssert(nY >= 0 && nY < psInfo->nYSize);
     320       42249 :     CPLAssert(nX >= 0 && nX < psInfo->nXSize);
     321             : 
     322       42249 :     if (psInfo->poSetVisitedPoints)
     323             :     {
     324         516 :         const uint64_t nKey = MakeKey(nY, nX);
     325         516 :         if (psInfo->poSetVisitedPoints->find(nKey) ==
     326        1032 :             psInfo->poSetVisitedPoints->end())
     327             :         {
     328         508 :             if (psInfo->bFillSetVisitedPoints)
     329         508 :                 psInfo->poSetVisitedPoints->insert(nKey);
     330             :         }
     331             :         else
     332             :         {
     333           8 :             return;
     334             :         }
     335             :     }
     336             : 
     337       42241 :     if (psInfo->eBurnValueType == GDT_Int64)
     338             :     {
     339           0 :         if (psInfo->eType == GDT_Int64 &&
     340           0 :             psInfo->eBurnValueSource == GBV_UserBurnValue)
     341             :         {
     342           0 :             gvBurnPointInt64UserBurnValue(psInfo, nY, nX);
     343             :         }
     344             :         else
     345             :         {
     346           0 :             CPLAssert(false);
     347             :         }
     348           0 :         return;
     349             :     }
     350             : 
     351       42241 :     switch (psInfo->eType)
     352             :     {
     353       15634 :         case GDT_Byte:
     354       15634 :             gvBurnPointBasic<GByte>(psInfo, nY, nX, dfVariant);
     355       15634 :             break;
     356           0 :         case GDT_Int8:
     357           0 :             gvBurnPointBasic<GInt8>(psInfo, nY, nX, dfVariant);
     358           0 :             break;
     359           0 :         case GDT_Int16:
     360           0 :             gvBurnPointBasic<GInt16>(psInfo, nY, nX, dfVariant);
     361           0 :             break;
     362           0 :         case GDT_UInt16:
     363           0 :             gvBurnPointBasic<GUInt16>(psInfo, nY, nX, dfVariant);
     364           0 :             break;
     365           0 :         case GDT_Int32:
     366           0 :             gvBurnPointBasic<GInt32>(psInfo, nY, nX, dfVariant);
     367           0 :             break;
     368           0 :         case GDT_UInt32:
     369           0 :             gvBurnPointBasic<GUInt32>(psInfo, nY, nX, dfVariant);
     370           0 :             break;
     371           0 :         case GDT_Int64:
     372           0 :             gvBurnPointBasic<std::int64_t>(psInfo, nY, nX, dfVariant);
     373           0 :             break;
     374           0 :         case GDT_UInt64:
     375           0 :             gvBurnPointBasic<std::uint64_t>(psInfo, nY, nX, dfVariant);
     376           0 :             break;
     377           0 :         case GDT_Float16:
     378           0 :             gvBurnPointBasic<GFloat16>(psInfo, nY, nX, dfVariant);
     379           0 :             break;
     380         732 :         case GDT_Float32:
     381         732 :             gvBurnPointBasic<float>(psInfo, nY, nX, dfVariant);
     382         732 :             break;
     383       25875 :         case GDT_Float64:
     384       25875 :             gvBurnPointBasic<double>(psInfo, nY, nX, dfVariant);
     385       25875 :             break;
     386           0 :         case GDT_CInt16:
     387             :         case GDT_CInt32:
     388             :         case GDT_CFloat16:
     389             :         case GDT_CFloat32:
     390             :         case GDT_CFloat64:
     391             :         case GDT_Unknown:
     392             :         case GDT_TypeCount:
     393           0 :             CPLAssert(false);
     394             :     }
     395             : }
     396             : 
     397             : /************************************************************************/
     398             : /*                    GDALCollectRingsFromGeometry()                    */
     399             : /************************************************************************/
     400             : 
     401        2103 : static void GDALCollectRingsFromGeometry(const OGRGeometry *poShape,
     402             :                                          std::vector<double> &aPointX,
     403             :                                          std::vector<double> &aPointY,
     404             :                                          std::vector<double> &aPointVariant,
     405             :                                          std::vector<int> &aPartSize,
     406             :                                          GDALBurnValueSrc eBurnValueSrc)
     407             : 
     408             : {
     409        2103 :     if (poShape == nullptr || poShape->IsEmpty())
     410           0 :         return;
     411             : 
     412        2103 :     const OGRwkbGeometryType eFlatType = wkbFlatten(poShape->getGeometryType());
     413             : 
     414        2103 :     if (eFlatType == wkbPoint)
     415             :     {
     416           5 :         const auto poPoint = poShape->toPoint();
     417             : 
     418           5 :         aPointX.push_back(poPoint->getX());
     419           5 :         aPointY.push_back(poPoint->getY());
     420           5 :         aPartSize.push_back(1);
     421           5 :         if (eBurnValueSrc != GBV_UserBurnValue)
     422             :         {
     423             :             // TODO(schwehr): Why not have the option for M r18164?
     424             :             // switch( eBurnValueSrc )
     425             :             // {
     426             :             // case GBV_Z:*/
     427           0 :             aPointVariant.push_back(poPoint->getZ());
     428             :             // break;
     429             :             // case GBV_M:
     430             :             //    aPointVariant.reserve( nNewCount );
     431             :             //    aPointVariant.push_back( poPoint->getM() );
     432             :         }
     433             :     }
     434        2098 :     else if (EQUAL(poShape->getGeometryName(), "LINEARRING"))
     435             :     {
     436         113 :         const auto poRing = poShape->toLinearRing();
     437         113 :         const int nCount = poRing->getNumPoints();
     438         113 :         const size_t nNewCount = aPointX.size() + static_cast<size_t>(nCount);
     439             : 
     440         113 :         aPointX.reserve(nNewCount);
     441         113 :         aPointY.reserve(nNewCount);
     442         113 :         if (eBurnValueSrc != GBV_UserBurnValue)
     443           4 :             aPointVariant.reserve(nNewCount);
     444         113 :         if (poRing->isClockwise())
     445             :         {
     446        1665 :             for (int i = 0; i < nCount; i++)
     447             :             {
     448        1599 :                 aPointX.push_back(poRing->getX(i));
     449        1599 :                 aPointY.push_back(poRing->getY(i));
     450        1599 :                 if (eBurnValueSrc != GBV_UserBurnValue)
     451             :                 {
     452             :                     /*switch( eBurnValueSrc )
     453             :                     {
     454             :                     case GBV_Z:*/
     455          15 :                     aPointVariant.push_back(poRing->getZ(i));
     456             :                     /*break;
     457             :                 case GBV_M:
     458             :                     aPointVariant.push_back( poRing->getM(i) );
     459             :                 }*/
     460             :                 }
     461             :             }
     462             :         }
     463             :         else
     464             :         {
     465        5108 :             for (int i = nCount - 1; i >= 0; i--)
     466             :             {
     467        5061 :                 aPointX.push_back(poRing->getX(i));
     468        5061 :                 aPointY.push_back(poRing->getY(i));
     469        5061 :                 if (eBurnValueSrc != GBV_UserBurnValue)
     470             :                 {
     471             :                     /*switch( eBurnValueSrc )
     472             :                     {
     473             :                     case GBV_Z:*/
     474           5 :                     aPointVariant.push_back(poRing->getZ(i));
     475             :                     /*break;
     476             :                 case GBV_M:
     477             :                     aPointVariant.push_back( poRing->getM(i) );
     478             :                 }*/
     479             :                 }
     480             :             }
     481             :         }
     482         113 :         aPartSize.push_back(nCount);
     483             :     }
     484        1985 :     else if (eFlatType == wkbLineString)
     485             :     {
     486        1880 :         const auto poLine = poShape->toLineString();
     487        1880 :         const int nCount = poLine->getNumPoints();
     488        1880 :         const size_t nNewCount = aPointX.size() + static_cast<size_t>(nCount);
     489             : 
     490        1880 :         aPointX.reserve(nNewCount);
     491        1880 :         aPointY.reserve(nNewCount);
     492        1880 :         if (eBurnValueSrc != GBV_UserBurnValue)
     493        1856 :             aPointVariant.reserve(nNewCount);
     494       50573 :         for (int i = nCount - 1; i >= 0; i--)
     495             :         {
     496       48693 :             aPointX.push_back(poLine->getX(i));
     497       48693 :             aPointY.push_back(poLine->getY(i));
     498       48693 :             if (eBurnValueSrc != GBV_UserBurnValue)
     499             :             {
     500             :                 /*switch( eBurnValueSrc )
     501             :                 {
     502             :                     case GBV_Z:*/
     503       48614 :                 aPointVariant.push_back(poLine->getZ(i));
     504             :                 /*break;
     505             :             case GBV_M:
     506             :                 aPointVariant.push_back( poLine->getM(i) );
     507             :         }*/
     508             :             }
     509             :         }
     510        1880 :         aPartSize.push_back(nCount);
     511             :     }
     512         105 :     else if (eFlatType == wkbPolygon)
     513             :     {
     514         105 :         const auto poPolygon = poShape->toPolygon();
     515             : 
     516         105 :         GDALCollectRingsFromGeometry(poPolygon->getExteriorRing(), aPointX,
     517             :                                      aPointY, aPointVariant, aPartSize,
     518             :                                      eBurnValueSrc);
     519             : 
     520         113 :         for (int i = 0; i < poPolygon->getNumInteriorRings(); i++)
     521           8 :             GDALCollectRingsFromGeometry(poPolygon->getInteriorRing(i), aPointX,
     522             :                                          aPointY, aPointVariant, aPartSize,
     523             :                                          eBurnValueSrc);
     524             :     }
     525           0 :     else if (eFlatType == wkbMultiPoint || eFlatType == wkbMultiLineString ||
     526           0 :              eFlatType == wkbMultiPolygon || eFlatType == wkbGeometryCollection)
     527             :     {
     528           0 :         const auto poGC = poShape->toGeometryCollection();
     529           0 :         for (int i = 0; i < poGC->getNumGeometries(); i++)
     530           0 :             GDALCollectRingsFromGeometry(poGC->getGeometryRef(i), aPointX,
     531             :                                          aPointY, aPointVariant, aPartSize,
     532           0 :                                          eBurnValueSrc);
     533             :     }
     534             :     else
     535             :     {
     536           0 :         CPLDebug("GDAL", "Rasterizer ignoring non-polygonal geometry.");
     537             :     }
     538             : }
     539             : 
     540             : /************************************************************************
     541             :  *                       gv_rasterize_one_shape()
     542             :  *
     543             :  * @param pabyChunkBuf buffer to which values will be burned
     544             :  * @param nXOff chunk column offset from left edge of raster
     545             :  * @param nYOff chunk scanline offset from top of raster
     546             :  * @param nXSize number of columns in chunk
     547             :  * @param nYSize number of rows in chunk
     548             :  * @param nBands number of bands in chunk
     549             :  * @param eType data type of pabyChunkBuf
     550             :  * @param nPixelSpace number of bytes between adjacent pixels in chunk
     551             :  *                    (0 to calculate automatically)
     552             :  * @param nLineSpace number of bytes between adjacent scanlines in chunk
     553             :  *                   (0 to calculate automatically)
     554             :  * @param nBandSpace number of bytes between adjacent bands in chunk
     555             :  *                   (0 to calculate automatically)
     556             :  * @param bAllTouched burn value to all touched pixels?
     557             :  * @param poShape geometry to rasterize, in original coordinates
     558             :  * @param eBurnValueType type of value to be burned (must be Float64 or Int64)
     559             :  * @param padfBurnValues array of nBands values to burn (Float64), or nullptr
     560             :  * @param panBurnValues array of nBands values to burn (Int64), or nullptr
     561             :  * @param eBurnValueSrc whether to burn values from padfBurnValues /
     562             :  *                      panBurnValues, or from the Z or M values of poShape
     563             :  * @param eMergeAlg whether the burn value should replace or be added to the
     564             :  *                  existing values
     565             :  * @param pfnTransformer transformer from CRS of geometry to pixel/line
     566             :  *                       coordinates of raster
     567             :  * @param pTransformArg arguments to pass to pfnTransformer
     568             :  ************************************************************************/
     569        2017 : static void gv_rasterize_one_shape(
     570             :     unsigned char *pabyChunkBuf, int nXOff, int nYOff, int nXSize, int nYSize,
     571             :     int nBands, GDALDataType eType, int nPixelSpace, GSpacing nLineSpace,
     572             :     GSpacing nBandSpace, int bAllTouched, const OGRGeometry *poShape,
     573             :     GDALDataType eBurnValueType, const double *padfBurnValues,
     574             :     const int64_t *panBurnValues, GDALBurnValueSrc eBurnValueSrc,
     575             :     GDALRasterMergeAlg eMergeAlg, GDALTransformerFunc pfnTransformer,
     576             :     void *pTransformArg)
     577             : 
     578             : {
     579        2017 :     if (poShape == nullptr || poShape->IsEmpty())
     580          27 :         return;
     581        2016 :     const auto eGeomType = wkbFlatten(poShape->getGeometryType());
     582             : 
     583        2016 :     if ((eGeomType == wkbMultiLineString || eGeomType == wkbMultiPolygon ||
     584          26 :          eGeomType == wkbGeometryCollection) &&
     585             :         eMergeAlg == GRMA_Replace)
     586             :     {
     587             :         // Speed optimization: in replace mode, we can rasterize each part of
     588             :         // a geometry collection separately.
     589          26 :         const auto poGC = poShape->toGeometryCollection();
     590          59 :         for (const auto poPart : *poGC)
     591             :         {
     592          33 :             gv_rasterize_one_shape(
     593             :                 pabyChunkBuf, nXOff, nYOff, nXSize, nYSize, nBands, eType,
     594             :                 nPixelSpace, nLineSpace, nBandSpace, bAllTouched, poPart,
     595             :                 eBurnValueType, padfBurnValues, panBurnValues, eBurnValueSrc,
     596             :                 eMergeAlg, pfnTransformer, pTransformArg);
     597             :         }
     598          26 :         return;
     599             :     }
     600             : 
     601        1990 :     if (nPixelSpace == 0)
     602             :     {
     603        1990 :         nPixelSpace = GDALGetDataTypeSizeBytes(eType);
     604             :     }
     605        1990 :     if (nLineSpace == 0)
     606             :     {
     607        1990 :         nLineSpace = static_cast<GSpacing>(nXSize) * nPixelSpace;
     608             :     }
     609        1990 :     if (nBandSpace == 0)
     610             :     {
     611        1990 :         nBandSpace = nYSize * nLineSpace;
     612             :     }
     613             : 
     614             :     GDALRasterizeInfo sInfo;
     615        1990 :     sInfo.nXSize = nXSize;
     616        1990 :     sInfo.nYSize = nYSize;
     617        1990 :     sInfo.nBands = nBands;
     618        1990 :     sInfo.pabyChunkBuf = pabyChunkBuf;
     619        1990 :     sInfo.eType = eType;
     620        1990 :     sInfo.nPixelSpace = nPixelSpace;
     621        1990 :     sInfo.nLineSpace = nLineSpace;
     622        1990 :     sInfo.nBandSpace = nBandSpace;
     623        1990 :     sInfo.eBurnValueType = eBurnValueType;
     624        1990 :     if (eBurnValueType == GDT_Float64)
     625        1989 :         sInfo.burnValues.double_values = padfBurnValues;
     626           1 :     else if (eBurnValueType == GDT_Int64)
     627           1 :         sInfo.burnValues.int64_values = panBurnValues;
     628             :     else
     629             :     {
     630           0 :         CPLAssert(false);
     631             :     }
     632        1990 :     sInfo.eBurnValueSource = eBurnValueSrc;
     633        1990 :     sInfo.eMergeAlg = eMergeAlg;
     634        1990 :     sInfo.bFillSetVisitedPoints = false;
     635        1990 :     sInfo.poSetVisitedPoints = nullptr;
     636             : 
     637             :     /* -------------------------------------------------------------------- */
     638             :     /*      Transform polygon geometries into a set of rings and a part     */
     639             :     /*      size list.                                                      */
     640             :     /* -------------------------------------------------------------------- */
     641             :     std::vector<double>
     642        3980 :         aPointX;  // coordinate X values from all rings/components
     643             :     std::vector<double>
     644        3980 :         aPointY;  // coordinate Y values from all rings/components
     645        3980 :     std::vector<double> aPointVariant;  // coordinate Z values
     646        3980 :     std::vector<int> aPartSize;  // number of X/Y/(Z) values associated with
     647             :                                  // each ring/component
     648             : 
     649        1990 :     GDALCollectRingsFromGeometry(poShape, aPointX, aPointY, aPointVariant,
     650             :                                  aPartSize, eBurnValueSrc);
     651             : 
     652             :     /* -------------------------------------------------------------------- */
     653             :     /*      Transform points if needed.                                     */
     654             :     /* -------------------------------------------------------------------- */
     655        1990 :     if (pfnTransformer != nullptr)
     656             :     {
     657             :         int *panSuccess =
     658        1990 :             static_cast<int *>(CPLCalloc(sizeof(int), aPointX.size()));
     659             : 
     660             :         // TODO: We need to add all appropriate error checking at some point.
     661        1990 :         pfnTransformer(pTransformArg, FALSE, static_cast<int>(aPointX.size()),
     662             :                        aPointX.data(), aPointY.data(), nullptr, panSuccess);
     663        1990 :         CPLFree(panSuccess);
     664             :     }
     665             : 
     666             :     /* -------------------------------------------------------------------- */
     667             :     /*      Shift to account for the buffer offset of this buffer.          */
     668             :     /* -------------------------------------------------------------------- */
     669       57348 :     for (unsigned int i = 0; i < aPointX.size(); i++)
     670       55358 :         aPointX[i] -= nXOff;
     671       57348 :     for (unsigned int i = 0; i < aPointY.size(); i++)
     672       55358 :         aPointY[i] -= nYOff;
     673             : 
     674             :     /* -------------------------------------------------------------------- */
     675             :     /*      Perform the rasterization.                                      */
     676             :     /*      According to the C++ Standard/23.2.4, elements of a vector are  */
     677             :     /*      stored in continuous memory block.                              */
     678             :     /* -------------------------------------------------------------------- */
     679             : 
     680        1990 :     switch (eGeomType)
     681             :     {
     682           5 :         case wkbPoint:
     683             :         case wkbMultiPoint:
     684          10 :             GDALdllImagePoint(
     685           5 :                 sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
     686           5 :                 aPartSize.data(), aPointX.data(), aPointY.data(),
     687             :                 (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
     688           0 :                                                      : aPointVariant.data(),
     689             :                 gvBurnPoint, &sInfo);
     690           5 :             break;
     691        1880 :         case wkbLineString:
     692             :         case wkbMultiLineString:
     693             :         {
     694        1880 :             if (eMergeAlg == GRMA_Add)
     695             :             {
     696          10 :                 sInfo.bFillSetVisitedPoints = true;
     697          10 :                 sInfo.poSetVisitedPoints = new std::set<uint64_t>();
     698             :             }
     699        1880 :             if (bAllTouched)
     700          12 :                 GDALdllImageLineAllTouched(
     701           6 :                     sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
     702           6 :                     aPartSize.data(), aPointX.data(), aPointY.data(),
     703             :                     (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
     704           0 :                                                          : aPointVariant.data(),
     705             :                     gvBurnPoint, &sInfo, eMergeAlg == GRMA_Add, false);
     706             :             else
     707        3748 :                 GDALdllImageLine(
     708        1874 :                     sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
     709        1874 :                     aPartSize.data(), aPointX.data(), aPointY.data(),
     710             :                     (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
     711        1856 :                                                          : aPointVariant.data(),
     712             :                     gvBurnPoint, &sInfo);
     713             :         }
     714        1880 :         break;
     715             : 
     716         105 :         default:
     717             :         {
     718         105 :             if (eMergeAlg == GRMA_Add)
     719             :             {
     720          12 :                 sInfo.bFillSetVisitedPoints = true;
     721          12 :                 sInfo.poSetVisitedPoints = new std::set<uint64_t>();
     722             :             }
     723         105 :             if (bAllTouched)
     724             :             {
     725             :                 // Reverting the variants to the first value because the
     726             :                 // polygon is filled using the variant from the first point of
     727             :                 // the first segment. Should be removed when the code to full
     728             :                 // polygons more appropriately is added.
     729          33 :                 if (eBurnValueSrc == GBV_UserBurnValue)
     730             :                 {
     731          33 :                     GDALdllImageLineAllTouched(
     732             :                         sInfo.nXSize, nYSize,
     733          33 :                         static_cast<int>(aPartSize.size()), aPartSize.data(),
     734          33 :                         aPointX.data(), aPointY.data(), nullptr, gvBurnPoint,
     735             :                         &sInfo, eMergeAlg == GRMA_Add, true);
     736             :                 }
     737             :                 else
     738             :                 {
     739           0 :                     for (unsigned int i = 0, n = 0;
     740           0 :                          i < static_cast<unsigned int>(aPartSize.size()); i++)
     741             :                     {
     742           0 :                         for (int j = 0; j < aPartSize[i]; j++)
     743           0 :                             aPointVariant[n++] = aPointVariant[0];
     744             :                     }
     745             : 
     746           0 :                     GDALdllImageLineAllTouched(
     747             :                         sInfo.nXSize, nYSize,
     748           0 :                         static_cast<int>(aPartSize.size()), aPartSize.data(),
     749           0 :                         aPointX.data(), aPointY.data(), aPointVariant.data(),
     750             :                         gvBurnPoint, &sInfo, eMergeAlg == GRMA_Add, true);
     751             :                 }
     752             :             }
     753         105 :             sInfo.bFillSetVisitedPoints = false;
     754         210 :             GDALdllImageFilledPolygon(
     755         105 :                 sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
     756         105 :                 aPartSize.data(), aPointX.data(), aPointY.data(),
     757             :                 (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
     758           3 :                                                      : aPointVariant.data(),
     759             :                 gvBurnScanline, &sInfo, eMergeAlg == GRMA_Add);
     760             :         }
     761         105 :         break;
     762             :     }
     763             : 
     764        1990 :     delete sInfo.poSetVisitedPoints;
     765             : }
     766             : 
     767             : /************************************************************************/
     768             : /*                        GDALRasterizeOptions()                        */
     769             : /*                                                                      */
     770             : /*      Recognise a few rasterize options used by all three entry       */
     771             : /*      points.                                                         */
     772             : /************************************************************************/
     773             : 
     774          92 : static CPLErr GDALRasterizeOptions(CSLConstList papszOptions, int *pbAllTouched,
     775             :                                    GDALBurnValueSrc *peBurnValueSource,
     776             :                                    GDALRasterMergeAlg *peMergeAlg,
     777             :                                    GDALRasterizeOptim *peOptim)
     778             : {
     779          92 :     *pbAllTouched = CPLFetchBool(papszOptions, "ALL_TOUCHED", false);
     780             : 
     781          92 :     const char *pszOpt = CSLFetchNameValue(papszOptions, "BURN_VALUE_FROM");
     782          92 :     *peBurnValueSource = GBV_UserBurnValue;
     783          92 :     if (pszOpt)
     784             :     {
     785           4 :         if (EQUAL(pszOpt, "Z"))
     786             :         {
     787           4 :             *peBurnValueSource = GBV_Z;
     788             :         }
     789             :         // else if( EQUAL(pszOpt, "M"))
     790             :         //     eBurnValueSource = GBV_M;
     791             :         else
     792             :         {
     793           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     794             :                      "Unrecognized value '%s' for BURN_VALUE_FROM.", pszOpt);
     795           0 :             return CE_Failure;
     796             :         }
     797             :     }
     798             : 
     799             :     /* -------------------------------------------------------------------- */
     800             :     /*      MERGE_ALG=[REPLACE]/ADD                                         */
     801             :     /* -------------------------------------------------------------------- */
     802          92 :     *peMergeAlg = GRMA_Replace;
     803          92 :     pszOpt = CSLFetchNameValue(papszOptions, "MERGE_ALG");
     804          92 :     if (pszOpt)
     805             :     {
     806          17 :         if (EQUAL(pszOpt, "ADD"))
     807             :         {
     808          17 :             *peMergeAlg = GRMA_Add;
     809             :         }
     810           0 :         else if (EQUAL(pszOpt, "REPLACE"))
     811             :         {
     812           0 :             *peMergeAlg = GRMA_Replace;
     813             :         }
     814             :         else
     815             :         {
     816           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     817             :                      "Unrecognized value '%s' for MERGE_ALG.", pszOpt);
     818           0 :             return CE_Failure;
     819             :         }
     820             :     }
     821             : 
     822             :     /* -------------------------------------------------------------------- */
     823             :     /*      OPTIM=[AUTO]/RASTER/VECTOR                                      */
     824             :     /* -------------------------------------------------------------------- */
     825          92 :     pszOpt = CSLFetchNameValue(papszOptions, "OPTIM");
     826          92 :     if (pszOpt)
     827             :     {
     828           3 :         if (peOptim)
     829             :         {
     830           3 :             *peOptim = GRO_Auto;
     831           3 :             if (EQUAL(pszOpt, "RASTER"))
     832             :             {
     833           1 :                 *peOptim = GRO_Raster;
     834             :             }
     835           2 :             else if (EQUAL(pszOpt, "VECTOR"))
     836             :             {
     837           1 :                 *peOptim = GRO_Vector;
     838             :             }
     839           1 :             else if (EQUAL(pszOpt, "AUTO"))
     840             :             {
     841           1 :                 *peOptim = GRO_Auto;
     842             :             }
     843             :             else
     844             :             {
     845           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     846             :                          "Unrecognized value '%s' for OPTIM.", pszOpt);
     847           0 :                 return CE_Failure;
     848             :             }
     849             :         }
     850             :         else
     851             :         {
     852           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     853             :                      "Option OPTIM is not supported by this function");
     854             :         }
     855             :     }
     856             : 
     857          92 :     return CE_None;
     858             : }
     859             : 
     860             : /************************************************************************/
     861             : /*                      GDALRasterizeGeometries()                       */
     862             : /************************************************************************/
     863             : 
     864             : static CPLErr GDALRasterizeGeometriesInternal(
     865             :     GDALDatasetH hDS, int nBandCount, const int *panBandList, int nGeomCount,
     866             :     const OGRGeometryH *pahGeometries, GDALTransformerFunc pfnTransformer,
     867             :     void *pTransformArg, GDALDataType eBurnValueType,
     868             :     const double *padfGeomBurnValues, const int64_t *panGeomBurnValues,
     869             :     CSLConstList papszOptions, GDALProgressFunc pfnProgress,
     870             :     void *pProgressArg);
     871             : 
     872             : /**
     873             :  * Burn geometries into raster.
     874             :  *
     875             :  * Rasterize a list of geometric objects into a raster dataset.  The
     876             :  * geometries are passed as an array of OGRGeometryH handlers.
     877             :  *
     878             :  * If the geometries are in the georeferenced coordinates of the raster
     879             :  * dataset, then the pfnTransform may be passed in NULL and one will be
     880             :  * derived internally from the geotransform of the dataset.  The transform
     881             :  * needs to transform the geometry locations into pixel/line coordinates
     882             :  * on the raster dataset.
     883             :  *
     884             :  * The output raster may be of any GDAL supported datatype. An explicit list
     885             :  * of burn values for each geometry for each band must be passed in.
     886             :  *
     887             :  * The papszOption list of options currently only supports one option. The
     888             :  * "ALL_TOUCHED" option may be enabled by setting it to "TRUE".
     889             :  *
     890             :  * @param hDS output data, must be opened in update mode.
     891             :  * @param nBandCount the number of bands to be updated.
     892             :  * @param panBandList the list of bands to be updated.
     893             :  * @param nGeomCount the number of geometries being passed in pahGeometries.
     894             :  * @param pahGeometries the array of geometries to burn in.
     895             :  * @param pfnTransformer transformation to apply to geometries to put into
     896             :  * pixel/line coordinates on raster.  If NULL a geotransform based one will
     897             :  * be created internally.
     898             :  * @param pTransformArg callback data for transformer.
     899             :  * @param padfGeomBurnValues the array of values to burn into the raster.
     900             :  * There should be nBandCount values for each geometry.
     901             :  * @param papszOptions special options controlling rasterization
     902             :  * <ul>
     903             :  * <li>"ALL_TOUCHED": May be set to TRUE to set all pixels touched
     904             :  * by the line or polygons, not just those whose center is within the polygon
     905             :  * (behavior is unspecified when the polygon is just touching the pixel center)
     906             :  * or that are selected by Brezenham's line algorithm.  Defaults to FALSE.</li>
     907             :  * <li>"BURN_VALUE_FROM": May be set to "Z" to use the Z values of the
     908             :  * geometries. dfBurnValue is added to this before burning.
     909             :  * Defaults to GDALBurnValueSrc.GBV_UserBurnValue in which case just the
     910             :  * dfBurnValue is burned. This is implemented only for points and lines for
     911             :  * now. The M value may be supported in the future.</li>
     912             :  * <li>"MERGE_ALG": May be REPLACE (the default) or ADD.  REPLACE results in
     913             :  * overwriting of value, while ADD adds the new value to the existing raster,
     914             :  * suitable for heatmaps for instance.</li>
     915             :  * <li>"CHUNKYSIZE": The height in lines of the chunk to operate on.
     916             :  * The larger the chunk size the less times we need to make a pass through all
     917             :  * the shapes. If it is not set or set to zero the default chunk size will be
     918             :  * used. Default size will be estimated based on the GDAL cache buffer size
     919             :  * using formula: cache_size_bytes/scanline_size_bytes, so the chunk will
     920             :  * not exceed the cache. Not used in OPTIM=RASTER mode.</li>
     921             :  * <li>"OPTIM": May be set to "AUTO", "RASTER", "VECTOR". Force the algorithm
     922             :  * used (results are identical). The raster mode is used in most cases and
     923             :  * optimise read/write operations. The vector mode is useful with a decent
     924             :  * amount of input features and optimize the CPU use. That mode has to be used
     925             :  * with tiled images to be efficient. The auto mode (the default) will chose
     926             :  * the algorithm based on input and output properties.
     927             :  * </li>
     928             :  * </ul>
     929             :  * @param pfnProgress the progress function to report completion.
     930             :  * @param pProgressArg callback data for progress function.
     931             :  *
     932             :  * @return CE_None on success or CE_Failure on error.
     933             :  *
     934             :  * <strong>Example</strong><br>
     935             :  * GDALRasterizeGeometries rasterize output to MEM Dataset :<br>
     936             :  * @code
     937             :  *     int nBufXSize      = 1024;
     938             :  *     int nBufYSize      = 1024;
     939             :  *     int nBandCount     = 1;
     940             :  *     GDALDataType eType = GDT_Byte;
     941             :  *     int nDataTypeSize  = GDALGetDataTypeSizeBytes(eType);
     942             :  *
     943             :  *     void* pData = CPLCalloc( nBufXSize*nBufYSize*nBandCount, nDataTypeSize );
     944             :  *     char memdsetpath[1024];
     945             :  *     sprintf(memdsetpath,"MEM:::DATAPOINTER=0x%p,PIXELS=%d,LINES=%d,"
     946             :  *             "BANDS=%d,DATATYPE=%s,PIXELOFFSET=%d,LINEOFFSET=%d",
     947             :  *             pData,nBufXSize,nBufYSize,nBandCount,GDALGetDataTypeName(eType),
     948             :  *             nBandCount*nDataTypeSize, nBufXSize*nBandCount*nDataTypeSize );
     949             :  *
     950             :  *      // Open Memory Dataset
     951             :  *      GDALDatasetH hMemDset = GDALOpen(memdsetpath, GA_Update);
     952             :  *      // or create it as follows
     953             :  *      // GDALDriverH hMemDriver = GDALGetDriverByName("MEM");
     954             :  *      // GDALDatasetH hMemDset = GDALCreate(hMemDriver, "", nBufXSize,
     955             :  *                                      nBufYSize, nBandCount, eType, NULL);
     956             :  *
     957             :  *      double adfGeoTransform[6];
     958             :  *      // Assign GeoTransform parameters,Omitted here.
     959             :  *
     960             :  *      GDALSetGeoTransform(hMemDset,adfGeoTransform);
     961             :  *      GDALSetProjection(hMemDset,pszProjection); // Can not
     962             :  *
     963             :  *      // Do something ...
     964             :  *      // Need an array of OGRGeometry objects,The assumption here is pahGeoms
     965             :  *
     966             :  *      int bandList[3] = { 1, 2, 3};
     967             :  *      std::vector<double> geomBurnValue(nGeomCount*nBandCount,255.0);
     968             :  *      CPLErr err = GDALRasterizeGeometries(
     969             :  *          hMemDset, nBandCount, bandList, nGeomCount, pahGeoms, pfnTransformer,
     970             :  *          pTransformArg, geomBurnValue.data(), papszOptions,
     971             :  *          pfnProgress, pProgressArg);
     972             :  *      if( err != CE_None )
     973             :  *      {
     974             :  *          // Do something ...
     975             :  *      }
     976             :  *      GDALClose(hMemDset);
     977             :  *      CPLFree(pData);
     978             :  *@endcode
     979             :  */
     980             : 
     981          50 : CPLErr GDALRasterizeGeometries(
     982             :     GDALDatasetH hDS, int nBandCount, const int *panBandList, int nGeomCount,
     983             :     const OGRGeometryH *pahGeometries, GDALTransformerFunc pfnTransformer,
     984             :     void *pTransformArg, const double *padfGeomBurnValues,
     985             :     CSLConstList papszOptions, GDALProgressFunc pfnProgress, void *pProgressArg)
     986             : 
     987             : {
     988          50 :     VALIDATE_POINTER1(hDS, "GDALRasterizeGeometries", CE_Failure);
     989             : 
     990          50 :     return GDALRasterizeGeometriesInternal(
     991             :         hDS, nBandCount, panBandList, nGeomCount, pahGeometries, pfnTransformer,
     992             :         pTransformArg, GDT_Float64, padfGeomBurnValues, nullptr, papszOptions,
     993          50 :         pfnProgress, pProgressArg);
     994             : }
     995             : 
     996             : /**
     997             :  * Burn geometries into raster.
     998             :  *
     999             :  * Same as GDALRasterizeGeometries(), except that the burn values array is
    1000             :  * of type Int64. And the datatype of the output raster *must* be GDT_Int64.
    1001             :  *
    1002             :  * @since GDAL 3.5
    1003             :  */
    1004           1 : CPLErr GDALRasterizeGeometriesInt64(
    1005             :     GDALDatasetH hDS, int nBandCount, const int *panBandList, int nGeomCount,
    1006             :     const OGRGeometryH *pahGeometries, GDALTransformerFunc pfnTransformer,
    1007             :     void *pTransformArg, const int64_t *panGeomBurnValues,
    1008             :     CSLConstList papszOptions, GDALProgressFunc pfnProgress, void *pProgressArg)
    1009             : 
    1010             : {
    1011           1 :     VALIDATE_POINTER1(hDS, "GDALRasterizeGeometriesInt64", CE_Failure);
    1012             : 
    1013           1 :     return GDALRasterizeGeometriesInternal(
    1014             :         hDS, nBandCount, panBandList, nGeomCount, pahGeometries, pfnTransformer,
    1015             :         pTransformArg, GDT_Int64, nullptr, panGeomBurnValues, papszOptions,
    1016           1 :         pfnProgress, pProgressArg);
    1017             : }
    1018             : 
    1019          51 : static CPLErr GDALRasterizeGeometriesInternal(
    1020             :     GDALDatasetH hDS, int nBandCount, const int *panBandList, int nGeomCount,
    1021             :     const OGRGeometryH *pahGeometries, GDALTransformerFunc pfnTransformer,
    1022             :     void *pTransformArg, GDALDataType eBurnValueType,
    1023             :     const double *padfGeomBurnValues, const int64_t *panGeomBurnValues,
    1024             :     CSLConstList papszOptions, GDALProgressFunc pfnProgress, void *pProgressArg)
    1025             : 
    1026             : {
    1027          51 :     if (pfnProgress == nullptr)
    1028          25 :         pfnProgress = GDALDummyProgress;
    1029             : 
    1030          51 :     GDALDataset *poDS = GDALDataset::FromHandle(hDS);
    1031             :     /* -------------------------------------------------------------------- */
    1032             :     /*      Do some rudimentary arg checking.                               */
    1033             :     /* -------------------------------------------------------------------- */
    1034          51 :     if (nBandCount == 0 || nGeomCount == 0)
    1035             :     {
    1036           0 :         pfnProgress(1.0, "", pProgressArg);
    1037           0 :         return CE_None;
    1038             :     }
    1039             : 
    1040          51 :     if (eBurnValueType == GDT_Int64)
    1041             :     {
    1042           2 :         for (int i = 0; i < nBandCount; i++)
    1043             :         {
    1044           1 :             GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[i]);
    1045           1 :             if (poBand == nullptr)
    1046           0 :                 return CE_Failure;
    1047           1 :             if (poBand->GetRasterDataType() != GDT_Int64)
    1048             :             {
    1049           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1050             :                          "GDALRasterizeGeometriesInt64() only supported on "
    1051             :                          "Int64 raster");
    1052           0 :                 return CE_Failure;
    1053             :             }
    1054             :         }
    1055             :     }
    1056             : 
    1057             :     // Prototype band.
    1058          51 :     GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[0]);
    1059          51 :     if (poBand == nullptr)
    1060           0 :         return CE_Failure;
    1061             : 
    1062             :     /* -------------------------------------------------------------------- */
    1063             :     /*      Options                                                         */
    1064             :     /* -------------------------------------------------------------------- */
    1065          51 :     int bAllTouched = FALSE;
    1066          51 :     GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
    1067          51 :     GDALRasterMergeAlg eMergeAlg = GRMA_Replace;
    1068          51 :     GDALRasterizeOptim eOptim = GRO_Auto;
    1069          51 :     if (GDALRasterizeOptions(papszOptions, &bAllTouched, &eBurnValueSource,
    1070          51 :                              &eMergeAlg, &eOptim) == CE_Failure)
    1071             :     {
    1072           0 :         return CE_Failure;
    1073             :     }
    1074             : 
    1075             :     /* -------------------------------------------------------------------- */
    1076             :     /*      If we have no transformer, assume the geometries are in file    */
    1077             :     /*      georeferenced coordinates, and create a transformer to          */
    1078             :     /*      convert that to pixel/line coordinates.                         */
    1079             :     /*                                                                      */
    1080             :     /*      We really just need to apply an affine transform, but for       */
    1081             :     /*      simplicity we use the more general GenImgProjTransformer.       */
    1082             :     /* -------------------------------------------------------------------- */
    1083          51 :     bool bNeedToFreeTransformer = false;
    1084             : 
    1085          51 :     if (pfnTransformer == nullptr)
    1086             :     {
    1087          25 :         bNeedToFreeTransformer = true;
    1088             : 
    1089          25 :         char **papszTransformerOptions = nullptr;
    1090          25 :         double adfGeoTransform[6] = {0.0};
    1091          25 :         if (poDS->GetGeoTransform(adfGeoTransform) != CE_None &&
    1092          25 :             poDS->GetGCPCount() == 0 && poDS->GetMetadata("RPC") == nullptr)
    1093             :         {
    1094           3 :             papszTransformerOptions = CSLSetNameValue(
    1095             :                 papszTransformerOptions, "DST_METHOD", "NO_GEOTRANSFORM");
    1096             :         }
    1097             : 
    1098          25 :         pTransformArg = GDALCreateGenImgProjTransformer2(
    1099             :             nullptr, hDS, papszTransformerOptions);
    1100          25 :         CSLDestroy(papszTransformerOptions);
    1101             : 
    1102          25 :         pfnTransformer = GDALGenImgProjTransform;
    1103          25 :         if (pTransformArg == nullptr)
    1104             :         {
    1105           0 :             return CE_Failure;
    1106             :         }
    1107             :     }
    1108             : 
    1109             :     /* -------------------------------------------------------------------- */
    1110             :     /*      Choice of optimisation in auto mode. Use vector optim :         */
    1111             :     /*      1) if output is tiled                                           */
    1112             :     /*      2) if large number of features is present (>10000)              */
    1113             :     /*      3) if the nb of pixels > 50 * nb of features (not-too-small ft) */
    1114             :     /* -------------------------------------------------------------------- */
    1115             :     int nXBlockSize, nYBlockSize;
    1116          51 :     poBand->GetBlockSize(&nXBlockSize, &nYBlockSize);
    1117             : 
    1118          51 :     if (eOptim == GRO_Auto)
    1119             :     {
    1120          49 :         eOptim = GRO_Raster;
    1121             :         // TODO make more tests with various inputs/outputs to adjust the
    1122             :         // parameters
    1123          49 :         if (nYBlockSize > 1 && nGeomCount > 10000 &&
    1124           0 :             (poBand->GetXSize() * static_cast<long long>(poBand->GetYSize()) /
    1125           0 :                  nGeomCount >
    1126             :              50))
    1127             :         {
    1128           0 :             eOptim = GRO_Vector;
    1129           0 :             CPLDebug("GDAL", "The vector optim has been chosen automatically");
    1130             :         }
    1131             :     }
    1132             : 
    1133             :     /* -------------------------------------------------------------------- */
    1134             :     /*      The original algorithm                                          */
    1135             :     /*      Optimized for raster writing                                    */
    1136             :     /*      (optimal on a small number of large vectors)                    */
    1137             :     /* -------------------------------------------------------------------- */
    1138             :     unsigned char *pabyChunkBuf;
    1139          51 :     CPLErr eErr = CE_None;
    1140          51 :     if (eOptim == GRO_Raster)
    1141             :     {
    1142             :         /* --------------------------------------------------------------------
    1143             :          */
    1144             :         /*      Establish a chunksize to operate on.  The larger the chunk */
    1145             :         /*      size the less times we need to make a pass through all the */
    1146             :         /*      shapes. */
    1147             :         /* --------------------------------------------------------------------
    1148             :          */
    1149             :         const GDALDataType eType =
    1150          50 :             GDALGetNonComplexDataType(poBand->GetRasterDataType());
    1151             : 
    1152         100 :         const uint64_t nScanlineBytes = static_cast<uint64_t>(nBandCount) *
    1153          50 :                                         poDS->GetRasterXSize() *
    1154          50 :                                         GDALGetDataTypeSizeBytes(eType);
    1155             : 
    1156             : #if SIZEOF_VOIDP < 8
    1157             :         // Only on 32-bit systems and in pathological cases
    1158             :         if (nScanlineBytes > std::numeric_limits<size_t>::max())
    1159             :         {
    1160             :             CPLError(CE_Failure, CPLE_OutOfMemory, "Too big raster");
    1161             :             if (bNeedToFreeTransformer)
    1162             :                 GDALDestroyTransformer(pTransformArg);
    1163             :             return CE_Failure;
    1164             :         }
    1165             : #endif
    1166             : 
    1167             :         int nYChunkSize =
    1168          50 :             atoi(CSLFetchNameValueDef(papszOptions, "CHUNKYSIZE", "0"));
    1169          50 :         if (nYChunkSize <= 0)
    1170             :         {
    1171          50 :             const GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes;
    1172          50 :             const int knIntMax = std::numeric_limits<int>::max();
    1173          50 :             nYChunkSize = nYChunkSize64 > knIntMax
    1174          50 :                               ? knIntMax
    1175             :                               : static_cast<int>(nYChunkSize64);
    1176             :         }
    1177             : 
    1178          50 :         if (nYChunkSize < 1)
    1179           0 :             nYChunkSize = 1;
    1180          50 :         if (nYChunkSize > poDS->GetRasterYSize())
    1181          50 :             nYChunkSize = poDS->GetRasterYSize();
    1182             : 
    1183          50 :         CPLDebug("GDAL", "Rasterizer operating on %d swaths of %d scanlines.",
    1184          50 :                  (poDS->GetRasterYSize() + nYChunkSize - 1) / nYChunkSize,
    1185             :                  nYChunkSize);
    1186             : 
    1187          50 :         pabyChunkBuf = static_cast<unsigned char *>(VSI_MALLOC2_VERBOSE(
    1188             :             nYChunkSize, static_cast<size_t>(nScanlineBytes)));
    1189          50 :         if (pabyChunkBuf == nullptr)
    1190             :         {
    1191           0 :             if (bNeedToFreeTransformer)
    1192           0 :                 GDALDestroyTransformer(pTransformArg);
    1193           0 :             return CE_Failure;
    1194             :         }
    1195             : 
    1196             :         /* ====================================================================
    1197             :          */
    1198             :         /*      Loop over image in designated chunks. */
    1199             :         /* ====================================================================
    1200             :          */
    1201          50 :         pfnProgress(0.0, nullptr, pProgressArg);
    1202             : 
    1203         100 :         for (int iY = 0; iY < poDS->GetRasterYSize() && eErr == CE_None;
    1204          50 :              iY += nYChunkSize)
    1205             :         {
    1206          50 :             int nThisYChunkSize = nYChunkSize;
    1207          50 :             if (nThisYChunkSize + iY > poDS->GetRasterYSize())
    1208           0 :                 nThisYChunkSize = poDS->GetRasterYSize() - iY;
    1209             : 
    1210          50 :             eErr = poDS->RasterIO(
    1211             :                 GF_Read, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
    1212             :                 pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize, eType,
    1213             :                 nBandCount, panBandList, 0, 0, 0, nullptr);
    1214          50 :             if (eErr != CE_None)
    1215           0 :                 break;
    1216             : 
    1217        1974 :             for (int iShape = 0; iShape < nGeomCount; iShape++)
    1218             :             {
    1219        3849 :                 gv_rasterize_one_shape(
    1220             :                     pabyChunkBuf, 0, iY, poDS->GetRasterXSize(),
    1221             :                     nThisYChunkSize, nBandCount, eType, 0, 0, 0, bAllTouched,
    1222        1924 :                     OGRGeometry::FromHandle(pahGeometries[iShape]),
    1223             :                     eBurnValueType,
    1224             :                     padfGeomBurnValues
    1225        1923 :                         ? padfGeomBurnValues +
    1226        1923 :                               static_cast<size_t>(iShape) * nBandCount
    1227             :                         : nullptr,
    1228             :                     panGeomBurnValues
    1229           1 :                         ? panGeomBurnValues +
    1230           1 :                               static_cast<size_t>(iShape) * nBandCount
    1231             :                         : nullptr,
    1232             :                     eBurnValueSource, eMergeAlg, pfnTransformer, pTransformArg);
    1233             :             }
    1234             : 
    1235          50 :             eErr = poDS->RasterIO(
    1236             :                 GF_Write, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
    1237             :                 pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize, eType,
    1238             :                 nBandCount, panBandList, 0, 0, 0, nullptr);
    1239             : 
    1240          50 :             if (!pfnProgress((iY + nThisYChunkSize) /
    1241          50 :                                  static_cast<double>(poDS->GetRasterYSize()),
    1242             :                              "", pProgressArg))
    1243             :             {
    1244           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1245           0 :                 eErr = CE_Failure;
    1246             :             }
    1247             :         }
    1248             :     }
    1249             :     /* -------------------------------------------------------------------- */
    1250             :     /*      The new algorithm                                               */
    1251             :     /*      Optimized to minimize the vector computation                    */
    1252             :     /*      (optimal on a large number of vectors & tiled raster)           */
    1253             :     /* -------------------------------------------------------------------- */
    1254             :     else
    1255             :     {
    1256             :         /* --------------------------------------------------------------------
    1257             :          */
    1258             :         /*      Establish a chunksize to operate on.  Its size is defined by */
    1259             :         /*      the block size of the output file. */
    1260             :         /* --------------------------------------------------------------------
    1261             :          */
    1262             :         const int nXBlocks =
    1263           1 :             (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize;
    1264             :         const int nYBlocks =
    1265           1 :             (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize;
    1266             : 
    1267             :         const GDALDataType eType =
    1268           1 :             poBand->GetRasterDataType() == GDT_Byte ? GDT_Byte : GDT_Float64;
    1269             : 
    1270           1 :         const int nPixelSize = nBandCount * GDALGetDataTypeSizeBytes(eType);
    1271             : 
    1272             :         // rem: optimized for square blocks
    1273             :         const GIntBig nbMaxBlocks64 =
    1274           1 :             GDALGetCacheMax64() / nPixelSize / nYBlockSize / nXBlockSize;
    1275           1 :         const int knIntMax = std::numeric_limits<int>::max();
    1276             :         const int nbMaxBlocks = static_cast<int>(
    1277           2 :             std::min(static_cast<GIntBig>(knIntMax / nPixelSize / nYBlockSize /
    1278             :                                           nXBlockSize),
    1279           1 :                      nbMaxBlocks64));
    1280           1 :         const int nbBlocksX = std::max(
    1281           2 :             1,
    1282           2 :             std::min(static_cast<int>(sqrt(static_cast<double>(nbMaxBlocks))),
    1283           1 :                      nXBlocks));
    1284             :         const int nbBlocksY =
    1285           1 :             std::max(1, std::min(nbMaxBlocks / nbBlocksX, nYBlocks));
    1286             : 
    1287           1 :         const uint64_t nChunkSize = static_cast<uint64_t>(nXBlockSize) *
    1288           1 :                                     nbBlocksX * nYBlockSize * nbBlocksY;
    1289             : 
    1290             : #if SIZEOF_VOIDP < 8
    1291             :         // Only on 32-bit systems and in pathological cases
    1292             :         if (nChunkSize > std::numeric_limits<size_t>::max())
    1293             :         {
    1294             :             CPLError(CE_Failure, CPLE_OutOfMemory, "Too big raster");
    1295             :             if (bNeedToFreeTransformer)
    1296             :                 GDALDestroyTransformer(pTransformArg);
    1297             :             return CE_Failure;
    1298             :         }
    1299             : #endif
    1300             : 
    1301             :         pabyChunkBuf = static_cast<unsigned char *>(
    1302           1 :             VSI_MALLOC2_VERBOSE(nPixelSize, static_cast<size_t>(nChunkSize)));
    1303           1 :         if (pabyChunkBuf == nullptr)
    1304             :         {
    1305           0 :             if (bNeedToFreeTransformer)
    1306           0 :                 GDALDestroyTransformer(pTransformArg);
    1307           0 :             return CE_Failure;
    1308             :         }
    1309             : 
    1310           1 :         OGREnvelope sRasterEnvelope;
    1311           1 :         sRasterEnvelope.MinX = 0;
    1312           1 :         sRasterEnvelope.MinY = 0;
    1313           1 :         sRasterEnvelope.MaxX = poDS->GetRasterXSize();
    1314           1 :         sRasterEnvelope.MaxY = poDS->GetRasterYSize();
    1315             : 
    1316             :         /* --------------------------------------------------------------------
    1317             :          */
    1318             :         /*      loop over the vectorial geometries */
    1319             :         /* --------------------------------------------------------------------
    1320             :          */
    1321           1 :         pfnProgress(0.0, nullptr, pProgressArg);
    1322           3 :         for (int iShape = 0; iShape < nGeomCount; iShape++)
    1323             :         {
    1324             : 
    1325             :             const OGRGeometry *poGeometry =
    1326           2 :                 OGRGeometry::FromHandle(pahGeometries[iShape]);
    1327           2 :             if (poGeometry == nullptr || poGeometry->IsEmpty())
    1328           0 :                 continue;
    1329             :             /* ------------------------------------------------------------ */
    1330             :             /*      get the envelope of the geometry and transform it to    */
    1331             :             /*      pixels coordinates.                                     */
    1332             :             /* ------------------------------------------------------------ */
    1333           2 :             OGREnvelope sGeomEnvelope;
    1334           2 :             poGeometry->getEnvelope(&sGeomEnvelope);
    1335           2 :             if (pfnTransformer != nullptr)
    1336             :             {
    1337           2 :                 int anSuccessTransform[2] = {0};
    1338             :                 double apCorners[4];
    1339           2 :                 apCorners[0] = sGeomEnvelope.MinX;
    1340           2 :                 apCorners[1] = sGeomEnvelope.MaxX;
    1341           2 :                 apCorners[2] = sGeomEnvelope.MinY;
    1342           2 :                 apCorners[3] = sGeomEnvelope.MaxY;
    1343             : 
    1344           2 :                 if (!pfnTransformer(pTransformArg, FALSE, 2, &(apCorners[0]),
    1345             :                                     &(apCorners[2]), nullptr,
    1346           2 :                                     anSuccessTransform) ||
    1347           2 :                     !anSuccessTransform[0] || !anSuccessTransform[1])
    1348             :                 {
    1349           0 :                     continue;
    1350             :                 }
    1351           2 :                 sGeomEnvelope.MinX = std::min(apCorners[0], apCorners[1]);
    1352           2 :                 sGeomEnvelope.MaxX = std::max(apCorners[0], apCorners[1]);
    1353           2 :                 sGeomEnvelope.MinY = std::min(apCorners[2], apCorners[3]);
    1354           2 :                 sGeomEnvelope.MaxY = std::max(apCorners[2], apCorners[3]);
    1355             :             }
    1356           2 :             if (!sGeomEnvelope.Intersects(sRasterEnvelope))
    1357           0 :                 continue;
    1358           2 :             sGeomEnvelope.Intersect(sRasterEnvelope);
    1359           2 :             CPLAssert(sGeomEnvelope.MinX >= 0 &&
    1360             :                       sGeomEnvelope.MinX <= poDS->GetRasterXSize());
    1361           2 :             CPLAssert(sGeomEnvelope.MinY >= 0 &&
    1362             :                       sGeomEnvelope.MinY <= poDS->GetRasterYSize());
    1363           2 :             CPLAssert(sGeomEnvelope.MaxX >= 0 &&
    1364             :                       sGeomEnvelope.MaxX <= poDS->GetRasterXSize());
    1365           2 :             CPLAssert(sGeomEnvelope.MaxY >= 0 &&
    1366             :                       sGeomEnvelope.MaxY <= poDS->GetRasterYSize());
    1367           2 :             const int minBlockX = int(sGeomEnvelope.MinX) / nXBlockSize;
    1368           2 :             const int minBlockY = int(sGeomEnvelope.MinY) / nYBlockSize;
    1369           2 :             const int maxBlockX = int(sGeomEnvelope.MaxX + 1) / nXBlockSize;
    1370           2 :             const int maxBlockY = int(sGeomEnvelope.MaxY + 1) / nYBlockSize;
    1371             : 
    1372             :             /* ------------------------------------------------------------ */
    1373             :             /*      loop over the blocks concerned by the geometry          */
    1374             :             /*      (by packs of nbBlocksX x nbBlocksY)                     */
    1375             :             /* ------------------------------------------------------------ */
    1376             : 
    1377           5 :             for (int xB = minBlockX; xB <= maxBlockX; xB += nbBlocksX)
    1378             :             {
    1379           6 :                 for (int yB = minBlockY; yB <= maxBlockY; yB += nbBlocksY)
    1380             :                 {
    1381             : 
    1382             :                     /* --------------------------------------------------------------------
    1383             :                      */
    1384             :                     /*      ensure to stay in the image */
    1385             :                     /* --------------------------------------------------------------------
    1386             :                      */
    1387           3 :                     int remSBX = std::min(maxBlockX - xB + 1, nbBlocksX);
    1388           3 :                     int remSBY = std::min(maxBlockY - yB + 1, nbBlocksY);
    1389           3 :                     int nThisXChunkSize = nXBlockSize * remSBX;
    1390           3 :                     int nThisYChunkSize = nYBlockSize * remSBY;
    1391           6 :                     if (xB * nXBlockSize + nThisXChunkSize >
    1392           3 :                         poDS->GetRasterXSize())
    1393           1 :                         nThisXChunkSize =
    1394           1 :                             poDS->GetRasterXSize() - xB * nXBlockSize;
    1395           6 :                     if (yB * nYBlockSize + nThisYChunkSize >
    1396           3 :                         poDS->GetRasterYSize())
    1397           2 :                         nThisYChunkSize =
    1398           2 :                             poDS->GetRasterYSize() - yB * nYBlockSize;
    1399             : 
    1400             :                     /* --------------------------------------------------------------------
    1401             :                      */
    1402             :                     /*      read image / process buffer / write buffer */
    1403             :                     /* --------------------------------------------------------------------
    1404             :                      */
    1405           3 :                     eErr = poDS->RasterIO(
    1406             :                         GF_Read, xB * nXBlockSize, yB * nYBlockSize,
    1407             :                         nThisXChunkSize, nThisYChunkSize, pabyChunkBuf,
    1408             :                         nThisXChunkSize, nThisYChunkSize, eType, nBandCount,
    1409             :                         panBandList, 0, 0, 0, nullptr);
    1410           3 :                     if (eErr != CE_None)
    1411           0 :                         break;
    1412             : 
    1413           6 :                     gv_rasterize_one_shape(
    1414             :                         pabyChunkBuf, xB * nXBlockSize, yB * nYBlockSize,
    1415             :                         nThisXChunkSize, nThisYChunkSize, nBandCount, eType, 0,
    1416             :                         0, 0, bAllTouched,
    1417           3 :                         OGRGeometry::FromHandle(pahGeometries[iShape]),
    1418             :                         eBurnValueType,
    1419             :                         padfGeomBurnValues
    1420           3 :                             ? padfGeomBurnValues +
    1421           3 :                                   static_cast<size_t>(iShape) * nBandCount
    1422             :                             : nullptr,
    1423             :                         panGeomBurnValues
    1424           0 :                             ? panGeomBurnValues +
    1425           0 :                                   static_cast<size_t>(iShape) * nBandCount
    1426             :                             : nullptr,
    1427             :                         eBurnValueSource, eMergeAlg, pfnTransformer,
    1428             :                         pTransformArg);
    1429             : 
    1430           3 :                     eErr = poDS->RasterIO(
    1431             :                         GF_Write, xB * nXBlockSize, yB * nYBlockSize,
    1432             :                         nThisXChunkSize, nThisYChunkSize, pabyChunkBuf,
    1433             :                         nThisXChunkSize, nThisYChunkSize, eType, nBandCount,
    1434             :                         panBandList, 0, 0, 0, nullptr);
    1435           3 :                     if (eErr != CE_None)
    1436           0 :                         break;
    1437             :                 }
    1438             :             }
    1439             : 
    1440           2 :             if (!pfnProgress(iShape / static_cast<double>(nGeomCount), "",
    1441             :                              pProgressArg))
    1442             :             {
    1443           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1444           0 :                 eErr = CE_Failure;
    1445             :             }
    1446             :         }
    1447             : 
    1448           1 :         if (!pfnProgress(1., "", pProgressArg))
    1449             :         {
    1450           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1451           0 :             eErr = CE_Failure;
    1452             :         }
    1453             :     }
    1454             : 
    1455             :     /* -------------------------------------------------------------------- */
    1456             :     /*      cleanup                                                         */
    1457             :     /* -------------------------------------------------------------------- */
    1458          51 :     VSIFree(pabyChunkBuf);
    1459             : 
    1460          51 :     if (bNeedToFreeTransformer)
    1461          25 :         GDALDestroyTransformer(pTransformArg);
    1462             : 
    1463          51 :     return eErr;
    1464             : }
    1465             : 
    1466             : /************************************************************************/
    1467             : /*                        GDALRasterizeLayers()                         */
    1468             : /************************************************************************/
    1469             : 
    1470             : /**
    1471             :  * Burn geometries from the specified list of layers into raster.
    1472             :  *
    1473             :  * Rasterize all the geometric objects from a list of layers into a raster
    1474             :  * dataset.  The layers are passed as an array of OGRLayerH handlers.
    1475             :  *
    1476             :  * If the geometries are in the georeferenced coordinates of the raster
    1477             :  * dataset, then the pfnTransform may be passed in NULL and one will be
    1478             :  * derived internally from the geotransform of the dataset.  The transform
    1479             :  * needs to transform the geometry locations into pixel/line coordinates
    1480             :  * on the raster dataset.
    1481             :  *
    1482             :  * The output raster may be of any GDAL supported datatype. An explicit list
    1483             :  * of burn values for each layer for each band must be passed in.
    1484             :  *
    1485             :  * @param hDS output data, must be opened in update mode.
    1486             :  * @param nBandCount the number of bands to be updated.
    1487             :  * @param panBandList the list of bands to be updated.
    1488             :  * @param nLayerCount the number of layers being passed in pahLayers array.
    1489             :  * @param pahLayers the array of layers to burn in.
    1490             :  * @param pfnTransformer transformation to apply to geometries to put into
    1491             :  * pixel/line coordinates on raster.  If NULL a geotransform based one will
    1492             :  * be created internally.
    1493             :  * @param pTransformArg callback data for transformer.
    1494             :  * @param padfLayerBurnValues the array of values to burn into the raster.
    1495             :  * There should be nBandCount values for each layer.
    1496             :  * @param papszOptions special options controlling rasterization:
    1497             :  * <ul>
    1498             :  * <li>"ATTRIBUTE": Identifies an attribute field on the features to be
    1499             :  * used for a burn in value. The value will be burned into all output
    1500             :  * bands. If specified, padfLayerBurnValues will not be used and can be a NULL
    1501             :  * pointer.</li>
    1502             :  * <li>"CHUNKYSIZE": The height in lines of the chunk to operate on.
    1503             :  * The larger the chunk size the less times we need to make a pass through all
    1504             :  * the shapes. If it is not set or set to zero the default chunk size will be
    1505             :  * used. Default size will be estimated based on the GDAL cache buffer size
    1506             :  * using formula: cache_size_bytes/scanline_size_bytes, so the chunk will
    1507             :  * not exceed the cache.</li>
    1508             :  * <li>"ALL_TOUCHED": May be set to TRUE to set all pixels touched
    1509             :  * by the line or polygons, not just those whose center is within the polygon
    1510             :  * (behavior is unspecified when the polygon is just touching the pixel center)
    1511             :  * or that are selected by Brezenham's line algorithm.  Defaults to FALSE.
    1512             :  * <li>"BURN_VALUE_FROM": May be set to "Z" to use the Z values of the</li>
    1513             :  * geometries. The value from padfLayerBurnValues or the attribute field value
    1514             :  * is added to this before burning. In default case dfBurnValue is burned as it
    1515             :  * is. This is implemented properly only for points and lines for now. Polygons
    1516             :  * will be burned using the Z value from the first point. The M value may be
    1517             :  * supported in the future.</li>
    1518             :  * <li>"MERGE_ALG": May be REPLACE (the default) or ADD.  REPLACE results in
    1519             :  * overwriting of value, while ADD adds the new value to the existing raster,
    1520             :  * suitable for heatmaps for instance.</li>
    1521             :  * </ul>
    1522             :  * @param pfnProgress the progress function to report completion.
    1523             :  * @param pProgressArg callback data for progress function.
    1524             :  *
    1525             :  * @return CE_None on success or CE_Failure on error.
    1526             :  */
    1527             : 
    1528          41 : CPLErr GDALRasterizeLayers(GDALDatasetH hDS, int nBandCount, int *panBandList,
    1529             :                            int nLayerCount, OGRLayerH *pahLayers,
    1530             :                            GDALTransformerFunc pfnTransformer,
    1531             :                            void *pTransformArg, double *padfLayerBurnValues,
    1532             :                            char **papszOptions, GDALProgressFunc pfnProgress,
    1533             :                            void *pProgressArg)
    1534             : 
    1535             : {
    1536          41 :     VALIDATE_POINTER1(hDS, "GDALRasterizeLayers", CE_Failure);
    1537             : 
    1538          41 :     if (pfnProgress == nullptr)
    1539          41 :         pfnProgress = GDALDummyProgress;
    1540             : 
    1541             :     /* -------------------------------------------------------------------- */
    1542             :     /*      Do some rudimentary arg checking.                               */
    1543             :     /* -------------------------------------------------------------------- */
    1544          41 :     if (nBandCount == 0 || nLayerCount == 0)
    1545           0 :         return CE_None;
    1546             : 
    1547          41 :     GDALDataset *poDS = GDALDataset::FromHandle(hDS);
    1548             : 
    1549             :     // Prototype band.
    1550          41 :     GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[0]);
    1551          41 :     if (poBand == nullptr)
    1552           0 :         return CE_Failure;
    1553             : 
    1554             :     /* -------------------------------------------------------------------- */
    1555             :     /*      Options                                                         */
    1556             :     /* -------------------------------------------------------------------- */
    1557          41 :     int bAllTouched = FALSE;
    1558          41 :     GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
    1559          41 :     GDALRasterMergeAlg eMergeAlg = GRMA_Replace;
    1560          41 :     if (GDALRasterizeOptions(papszOptions, &bAllTouched, &eBurnValueSource,
    1561          41 :                              &eMergeAlg, nullptr) == CE_Failure)
    1562             :     {
    1563           0 :         return CE_Failure;
    1564             :     }
    1565             : 
    1566             :     /* -------------------------------------------------------------------- */
    1567             :     /*      Establish a chunksize to operate on.  The larger the chunk      */
    1568             :     /*      size the less times we need to make a pass through all the      */
    1569             :     /*      shapes.                                                         */
    1570             :     /* -------------------------------------------------------------------- */
    1571          41 :     const char *pszYChunkSize = CSLFetchNameValue(papszOptions, "CHUNKYSIZE");
    1572             : 
    1573          41 :     const GDALDataType eType = poBand->GetRasterDataType();
    1574             : 
    1575             :     const int nScanlineBytes =
    1576          41 :         nBandCount * poDS->GetRasterXSize() * GDALGetDataTypeSizeBytes(eType);
    1577             : 
    1578          41 :     int nYChunkSize = 0;
    1579          41 :     if (!(pszYChunkSize && ((nYChunkSize = atoi(pszYChunkSize))) != 0))
    1580             :     {
    1581          41 :         const GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes;
    1582          41 :         nYChunkSize = static_cast<int>(
    1583          41 :             std::min<GIntBig>(nYChunkSize64, std::numeric_limits<int>::max()));
    1584             :     }
    1585             : 
    1586          41 :     if (nYChunkSize < 1)
    1587           0 :         nYChunkSize = 1;
    1588          41 :     if (nYChunkSize > poDS->GetRasterYSize())
    1589          41 :         nYChunkSize = poDS->GetRasterYSize();
    1590             : 
    1591          41 :     CPLDebug("GDAL", "Rasterizer operating on %d swaths of %d scanlines.",
    1592          41 :              (poDS->GetRasterYSize() + nYChunkSize - 1) / nYChunkSize,
    1593             :              nYChunkSize);
    1594             :     unsigned char *pabyChunkBuf = static_cast<unsigned char *>(
    1595          41 :         VSI_MALLOC2_VERBOSE(nYChunkSize, nScanlineBytes));
    1596          41 :     if (pabyChunkBuf == nullptr)
    1597             :     {
    1598           0 :         return CE_Failure;
    1599             :     }
    1600             : 
    1601             :     /* -------------------------------------------------------------------- */
    1602             :     /*      Read the image once for all layers if user requested to render  */
    1603             :     /*      the whole raster in single chunk.                               */
    1604             :     /* -------------------------------------------------------------------- */
    1605          41 :     if (nYChunkSize == poDS->GetRasterYSize())
    1606             :     {
    1607          41 :         if (poDS->RasterIO(GF_Read, 0, 0, poDS->GetRasterXSize(), nYChunkSize,
    1608             :                            pabyChunkBuf, poDS->GetRasterXSize(), nYChunkSize,
    1609             :                            eType, nBandCount, panBandList, 0, 0, 0,
    1610          41 :                            nullptr) != CE_None)
    1611             :         {
    1612           0 :             CPLFree(pabyChunkBuf);
    1613           0 :             return CE_Failure;
    1614             :         }
    1615             :     }
    1616             : 
    1617             :     /* ==================================================================== */
    1618             :     /*      Read the specified layers transforming and rasterizing          */
    1619             :     /*      geometries.                                                     */
    1620             :     /* ==================================================================== */
    1621          41 :     CPLErr eErr = CE_None;
    1622          41 :     const char *pszBurnAttribute = CSLFetchNameValue(papszOptions, "ATTRIBUTE");
    1623             : 
    1624          41 :     pfnProgress(0.0, nullptr, pProgressArg);
    1625             : 
    1626          82 :     for (int iLayer = 0; iLayer < nLayerCount; iLayer++)
    1627             :     {
    1628          41 :         OGRLayer *poLayer = reinterpret_cast<OGRLayer *>(pahLayers[iLayer]);
    1629             : 
    1630          41 :         if (!poLayer)
    1631             :         {
    1632           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1633             :                      "Layer element number %d is NULL, skipping.", iLayer);
    1634           0 :             continue;
    1635             :         }
    1636             : 
    1637             :         /* --------------------------------------------------------------------
    1638             :          */
    1639             :         /*      If the layer does not contain any features just skip it. */
    1640             :         /*      Do not force the feature count, so if driver doesn't know */
    1641             :         /*      exact number of features, go down the normal way. */
    1642             :         /* --------------------------------------------------------------------
    1643             :          */
    1644          41 :         if (poLayer->GetFeatureCount(FALSE) == 0)
    1645           0 :             continue;
    1646             : 
    1647          41 :         int iBurnField = -1;
    1648          41 :         double *padfBurnValues = nullptr;
    1649             : 
    1650          41 :         if (pszBurnAttribute)
    1651             :         {
    1652             :             iBurnField =
    1653           1 :                 poLayer->GetLayerDefn()->GetFieldIndex(pszBurnAttribute);
    1654           1 :             if (iBurnField == -1)
    1655             :             {
    1656           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1657             :                          "Failed to find field %s on layer %s, skipping.",
    1658           0 :                          pszBurnAttribute, poLayer->GetLayerDefn()->GetName());
    1659           0 :                 continue;
    1660             :             }
    1661             :         }
    1662             :         else
    1663             :         {
    1664          40 :             padfBurnValues = padfLayerBurnValues + iLayer * nBandCount;
    1665             :         }
    1666             : 
    1667             :         /* --------------------------------------------------------------------
    1668             :          */
    1669             :         /*      If we have no transformer, create the one from input file */
    1670             :         /*      projection. Note that each layer can be georefernced */
    1671             :         /*      separately. */
    1672             :         /* --------------------------------------------------------------------
    1673             :          */
    1674          41 :         bool bNeedToFreeTransformer = false;
    1675             : 
    1676          41 :         if (pfnTransformer == nullptr)
    1677             :         {
    1678          41 :             char *pszProjection = nullptr;
    1679          41 :             bNeedToFreeTransformer = true;
    1680             : 
    1681          41 :             OGRSpatialReference *poSRS = poLayer->GetSpatialRef();
    1682          41 :             if (!poSRS)
    1683             :             {
    1684           3 :                 if (poDS->GetSpatialRef() != nullptr ||
    1685           4 :                     poDS->GetGCPSpatialRef() != nullptr ||
    1686           1 :                     poDS->GetMetadata("RPC") != nullptr)
    1687             :                 {
    1688           2 :                     CPLError(
    1689             :                         CE_Warning, CPLE_AppDefined,
    1690             :                         "Failed to fetch spatial reference on layer %s "
    1691             :                         "to build transformer, assuming matching coordinate "
    1692             :                         "systems.",
    1693           2 :                         poLayer->GetLayerDefn()->GetName());
    1694             :                 }
    1695             :             }
    1696             :             else
    1697             :             {
    1698          38 :                 poSRS->exportToWkt(&pszProjection);
    1699             :             }
    1700             : 
    1701          41 :             char **papszTransformerOptions = nullptr;
    1702          41 :             if (pszProjection != nullptr)
    1703          38 :                 papszTransformerOptions = CSLSetNameValue(
    1704             :                     papszTransformerOptions, "SRC_SRS", pszProjection);
    1705          41 :             double adfGeoTransform[6] = {};
    1706          41 :             if (poDS->GetGeoTransform(adfGeoTransform) != CE_None &&
    1707          41 :                 poDS->GetGCPCount() == 0 && poDS->GetMetadata("RPC") == nullptr)
    1708             :             {
    1709           0 :                 papszTransformerOptions = CSLSetNameValue(
    1710             :                     papszTransformerOptions, "DST_METHOD", "NO_GEOTRANSFORM");
    1711             :             }
    1712             : 
    1713          41 :             pTransformArg = GDALCreateGenImgProjTransformer2(
    1714             :                 nullptr, hDS, papszTransformerOptions);
    1715          41 :             pfnTransformer = GDALGenImgProjTransform;
    1716             : 
    1717          41 :             CPLFree(pszProjection);
    1718          41 :             CSLDestroy(papszTransformerOptions);
    1719          41 :             if (pTransformArg == nullptr)
    1720             :             {
    1721           0 :                 CPLFree(pabyChunkBuf);
    1722           0 :                 return CE_Failure;
    1723             :             }
    1724             :         }
    1725             : 
    1726          41 :         poLayer->ResetReading();
    1727             : 
    1728             :         /* --------------------------------------------------------------------
    1729             :          */
    1730             :         /*      Loop over image in designated chunks. */
    1731             :         /* --------------------------------------------------------------------
    1732             :          */
    1733             : 
    1734             :         double *padfAttrValues = static_cast<double *>(
    1735          41 :             VSI_MALLOC_VERBOSE(sizeof(double) * nBandCount));
    1736          41 :         if (padfAttrValues == nullptr)
    1737           0 :             eErr = CE_Failure;
    1738             : 
    1739          82 :         for (int iY = 0; iY < poDS->GetRasterYSize() && eErr == CE_None;
    1740          41 :              iY += nYChunkSize)
    1741             :         {
    1742          41 :             int nThisYChunkSize = nYChunkSize;
    1743          41 :             if (nThisYChunkSize + iY > poDS->GetRasterYSize())
    1744           0 :                 nThisYChunkSize = poDS->GetRasterYSize() - iY;
    1745             : 
    1746             :             // Only re-read image if not a single chunk is being rendered.
    1747          41 :             if (nYChunkSize < poDS->GetRasterYSize())
    1748             :             {
    1749           0 :                 eErr = poDS->RasterIO(
    1750             :                     GF_Read, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
    1751             :                     pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize,
    1752             :                     eType, nBandCount, panBandList, 0, 0, 0, nullptr);
    1753           0 :                 if (eErr != CE_None)
    1754           0 :                     break;
    1755             :             }
    1756             : 
    1757          98 :             for (auto &poFeat : poLayer)
    1758             :             {
    1759          57 :                 OGRGeometry *poGeom = poFeat->GetGeometryRef();
    1760             : 
    1761          57 :                 if (pszBurnAttribute)
    1762             :                 {
    1763             :                     const double dfAttrValue =
    1764           5 :                         poFeat->GetFieldAsDouble(iBurnField);
    1765          20 :                     for (int iBand = 0; iBand < nBandCount; iBand++)
    1766          15 :                         padfAttrValues[iBand] = dfAttrValue;
    1767             : 
    1768           5 :                     padfBurnValues = padfAttrValues;
    1769             :                 }
    1770             : 
    1771          57 :                 gv_rasterize_one_shape(
    1772             :                     pabyChunkBuf, 0, iY, poDS->GetRasterXSize(),
    1773             :                     nThisYChunkSize, nBandCount, eType, 0, 0, 0, bAllTouched,
    1774             :                     poGeom, GDT_Float64, padfBurnValues, nullptr,
    1775             :                     eBurnValueSource, eMergeAlg, pfnTransformer, pTransformArg);
    1776             :             }
    1777             : 
    1778             :             // Only write image if not a single chunk is being rendered.
    1779          41 :             if (nYChunkSize < poDS->GetRasterYSize())
    1780             :             {
    1781           0 :                 eErr = poDS->RasterIO(
    1782             :                     GF_Write, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
    1783             :                     pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize,
    1784             :                     eType, nBandCount, panBandList, 0, 0, 0, nullptr);
    1785             :             }
    1786             : 
    1787          41 :             poLayer->ResetReading();
    1788             : 
    1789          41 :             if (!pfnProgress((iY + nThisYChunkSize) /
    1790          41 :                                  static_cast<double>(poDS->GetRasterYSize()),
    1791             :                              "", pProgressArg))
    1792             :             {
    1793           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1794           0 :                 eErr = CE_Failure;
    1795             :             }
    1796             :         }
    1797             : 
    1798          41 :         VSIFree(padfAttrValues);
    1799             : 
    1800          41 :         if (bNeedToFreeTransformer)
    1801             :         {
    1802          41 :             GDALDestroyTransformer(pTransformArg);
    1803          41 :             pTransformArg = nullptr;
    1804          41 :             pfnTransformer = nullptr;
    1805             :         }
    1806             :     }
    1807             : 
    1808             :     /* -------------------------------------------------------------------- */
    1809             :     /*      Write out the image once for all layers if user requested       */
    1810             :     /*      to render the whole raster in single chunk.                     */
    1811             :     /* -------------------------------------------------------------------- */
    1812          41 :     if (eErr == CE_None && nYChunkSize == poDS->GetRasterYSize())
    1813             :     {
    1814             :         eErr =
    1815          41 :             poDS->RasterIO(GF_Write, 0, 0, poDS->GetRasterXSize(), nYChunkSize,
    1816             :                            pabyChunkBuf, poDS->GetRasterXSize(), nYChunkSize,
    1817             :                            eType, nBandCount, panBandList, 0, 0, 0, nullptr);
    1818             :     }
    1819             : 
    1820             :     /* -------------------------------------------------------------------- */
    1821             :     /*      cleanup                                                         */
    1822             :     /* -------------------------------------------------------------------- */
    1823          41 :     VSIFree(pabyChunkBuf);
    1824             : 
    1825          41 :     return eErr;
    1826             : }
    1827             : 
    1828             : /************************************************************************/
    1829             : /*                        GDALRasterizeLayersBuf()                      */
    1830             : /************************************************************************/
    1831             : 
    1832             : /**
    1833             :  * Burn geometries from the specified list of layer into raster.
    1834             :  *
    1835             :  * Rasterize all the geometric objects from a list of layers into supplied
    1836             :  * raster buffer.  The layers are passed as an array of OGRLayerH handlers.
    1837             :  *
    1838             :  * If the geometries are in the georeferenced coordinates of the raster
    1839             :  * dataset, then the pfnTransform may be passed in NULL and one will be
    1840             :  * derived internally from the geotransform of the dataset.  The transform
    1841             :  * needs to transform the geometry locations into pixel/line coordinates
    1842             :  * of the target raster.
    1843             :  *
    1844             :  * The output raster may be of any GDAL supported datatype(non complex).
    1845             :  *
    1846             :  * @param pData pointer to the output data array.
    1847             :  *
    1848             :  * @param nBufXSize width of the output data array in pixels.
    1849             :  *
    1850             :  * @param nBufYSize height of the output data array in pixels.
    1851             :  *
    1852             :  * @param eBufType data type of the output data array.
    1853             :  *
    1854             :  * @param nPixelSpace The byte offset from the start of one pixel value in
    1855             :  * pData to the start of the next pixel value within a scanline.  If defaulted
    1856             :  * (0) the size of the datatype eBufType is used.
    1857             :  *
    1858             :  * @param nLineSpace The byte offset from the start of one scanline in
    1859             :  * pData to the start of the next.  If defaulted the size of the datatype
    1860             :  * eBufType * nBufXSize is used.
    1861             :  *
    1862             :  * @param nLayerCount the number of layers being passed in pahLayers array.
    1863             :  *
    1864             :  * @param pahLayers the array of layers to burn in.
    1865             :  *
    1866             :  * @param pszDstProjection WKT defining the coordinate system of the target
    1867             :  * raster.
    1868             :  *
    1869             :  * @param padfDstGeoTransform geotransformation matrix of the target raster.
    1870             :  *
    1871             :  * @param pfnTransformer transformation to apply to geometries to put into
    1872             :  * pixel/line coordinates on raster.  If NULL a geotransform based one will
    1873             :  * be created internally.
    1874             :  *
    1875             :  * @param pTransformArg callback data for transformer.
    1876             :  *
    1877             :  * @param dfBurnValue the value to burn into the raster.
    1878             :  *
    1879             :  * @param papszOptions special options controlling rasterization:
    1880             :  * <ul>
    1881             :  * <li>"ATTRIBUTE": Identifies an attribute field on the features to be
    1882             :  * used for a burn in value. The value will be burned into all output
    1883             :  * bands. If specified, padfLayerBurnValues will not be used and can be a NULL
    1884             :  * pointer.</li>
    1885             :  * <li>"ALL_TOUCHED": May be set to TRUE to set all pixels touched
    1886             :  * by the line or polygons, not just those whose center is within the polygon
    1887             :  * (behavior is unspecified when the polygon is just touching the pixel center)
    1888             :  * or that are selected by Brezenham's line algorithm.  Defaults to FALSE.</li>
    1889             :  * <li>"BURN_VALUE_FROM": May be set to "Z" to use
    1890             :  * the Z values of the geometries. dfBurnValue or the attribute field value is
    1891             :  * added to this before burning. In default case dfBurnValue is burned as it
    1892             :  * is. This is implemented properly only for points and lines for now. Polygons
    1893             :  * will be burned using the Z value from the first point. The M value may
    1894             :  * be supported in the future.</li>
    1895             :  * <li>"MERGE_ALG": May be REPLACE (the default) or ADD.  REPLACE
    1896             :  * results in overwriting of value, while ADD adds the new value to the
    1897             :  * existing raster, suitable for heatmaps for instance.</li>
    1898             :  * </ul>
    1899             :  *
    1900             :  * @param pfnProgress the progress function to report completion.
    1901             :  *
    1902             :  * @param pProgressArg callback data for progress function.
    1903             :  *
    1904             :  *
    1905             :  * @return CE_None on success or CE_Failure on error.
    1906             :  */
    1907             : 
    1908           0 : CPLErr GDALRasterizeLayersBuf(
    1909             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1910             :     int nPixelSpace, int nLineSpace, int nLayerCount, OGRLayerH *pahLayers,
    1911             :     const char *pszDstProjection, double *padfDstGeoTransform,
    1912             :     GDALTransformerFunc pfnTransformer, void *pTransformArg, double dfBurnValue,
    1913             :     char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressArg)
    1914             : 
    1915             : {
    1916             :     /* -------------------------------------------------------------------- */
    1917             :     /*           check eType, Avoid not supporting data types               */
    1918             :     /* -------------------------------------------------------------------- */
    1919           0 :     if (GDALDataTypeIsComplex(eBufType) || eBufType <= GDT_Unknown ||
    1920           0 :         eBufType >= GDT_TypeCount)
    1921             :     {
    1922           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1923             :                  "GDALRasterizeLayersBuf(): unsupported data type of eBufType");
    1924           0 :         return CE_Failure;
    1925             :     }
    1926             : 
    1927             :     /* -------------------------------------------------------------------- */
    1928             :     /*      If pixel and line spaceing are defaulted assign reasonable      */
    1929             :     /*      value assuming a packed buffer.                                 */
    1930             :     /* -------------------------------------------------------------------- */
    1931           0 :     int nTypeSizeBytes = GDALGetDataTypeSizeBytes(eBufType);
    1932           0 :     if (nPixelSpace == 0)
    1933             :     {
    1934           0 :         nPixelSpace = nTypeSizeBytes;
    1935             :     }
    1936           0 :     if (nPixelSpace < nTypeSizeBytes)
    1937             :     {
    1938           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1939             :                  "GDALRasterizeLayersBuf(): unsupported value of nPixelSpace");
    1940           0 :         return CE_Failure;
    1941             :     }
    1942             : 
    1943           0 :     if (nLineSpace == 0)
    1944             :     {
    1945           0 :         nLineSpace = nPixelSpace * nBufXSize;
    1946             :     }
    1947           0 :     if (nLineSpace < nPixelSpace * nBufXSize)
    1948             :     {
    1949           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1950             :                  "GDALRasterizeLayersBuf(): unsupported value of nLineSpace");
    1951           0 :         return CE_Failure;
    1952             :     }
    1953             : 
    1954           0 :     if (pfnProgress == nullptr)
    1955           0 :         pfnProgress = GDALDummyProgress;
    1956             : 
    1957             :     /* -------------------------------------------------------------------- */
    1958             :     /*      Do some rudimentary arg checking.                               */
    1959             :     /* -------------------------------------------------------------------- */
    1960           0 :     if (nLayerCount == 0)
    1961           0 :         return CE_None;
    1962             : 
    1963             :     /* -------------------------------------------------------------------- */
    1964             :     /*      Options                                                         */
    1965             :     /* -------------------------------------------------------------------- */
    1966           0 :     int bAllTouched = FALSE;
    1967           0 :     GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
    1968           0 :     GDALRasterMergeAlg eMergeAlg = GRMA_Replace;
    1969           0 :     if (GDALRasterizeOptions(papszOptions, &bAllTouched, &eBurnValueSource,
    1970           0 :                              &eMergeAlg, nullptr) == CE_Failure)
    1971             :     {
    1972           0 :         return CE_Failure;
    1973             :     }
    1974             : 
    1975             :     /* ==================================================================== */
    1976             :     /*      Read the specified layers transforming and rasterizing          */
    1977             :     /*      geometries.                                                     */
    1978             :     /* ==================================================================== */
    1979           0 :     CPLErr eErr = CE_None;
    1980           0 :     const char *pszBurnAttribute = CSLFetchNameValue(papszOptions, "ATTRIBUTE");
    1981             : 
    1982           0 :     pfnProgress(0.0, nullptr, pProgressArg);
    1983             : 
    1984           0 :     for (int iLayer = 0; iLayer < nLayerCount; iLayer++)
    1985             :     {
    1986           0 :         OGRLayer *poLayer = reinterpret_cast<OGRLayer *>(pahLayers[iLayer]);
    1987             : 
    1988           0 :         if (!poLayer)
    1989             :         {
    1990           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1991             :                      "Layer element number %d is NULL, skipping.", iLayer);
    1992           0 :             continue;
    1993             :         }
    1994             : 
    1995             :         /* --------------------------------------------------------------------
    1996             :          */
    1997             :         /*      If the layer does not contain any features just skip it. */
    1998             :         /*      Do not force the feature count, so if driver doesn't know */
    1999             :         /*      exact number of features, go down the normal way. */
    2000             :         /* --------------------------------------------------------------------
    2001             :          */
    2002           0 :         if (poLayer->GetFeatureCount(FALSE) == 0)
    2003           0 :             continue;
    2004             : 
    2005           0 :         int iBurnField = -1;
    2006           0 :         if (pszBurnAttribute)
    2007             :         {
    2008             :             iBurnField =
    2009           0 :                 poLayer->GetLayerDefn()->GetFieldIndex(pszBurnAttribute);
    2010           0 :             if (iBurnField == -1)
    2011             :             {
    2012           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2013             :                          "Failed to find field %s on layer %s, skipping.",
    2014           0 :                          pszBurnAttribute, poLayer->GetLayerDefn()->GetName());
    2015           0 :                 continue;
    2016             :             }
    2017             :         }
    2018             : 
    2019             :         /* --------------------------------------------------------------------
    2020             :          */
    2021             :         /*      If we have no transformer, create the one from input file */
    2022             :         /*      projection. Note that each layer can be georefernced */
    2023             :         /*      separately. */
    2024             :         /* --------------------------------------------------------------------
    2025             :          */
    2026           0 :         bool bNeedToFreeTransformer = false;
    2027             : 
    2028           0 :         if (pfnTransformer == nullptr)
    2029             :         {
    2030           0 :             char *pszProjection = nullptr;
    2031           0 :             bNeedToFreeTransformer = true;
    2032             : 
    2033           0 :             OGRSpatialReference *poSRS = poLayer->GetSpatialRef();
    2034           0 :             if (!poSRS)
    2035             :             {
    2036           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2037             :                          "Failed to fetch spatial reference on layer %s "
    2038             :                          "to build transformer, assuming matching coordinate "
    2039             :                          "systems.",
    2040           0 :                          poLayer->GetLayerDefn()->GetName());
    2041             :             }
    2042             :             else
    2043             :             {
    2044           0 :                 poSRS->exportToWkt(&pszProjection);
    2045             :             }
    2046             : 
    2047           0 :             pTransformArg = GDALCreateGenImgProjTransformer3(
    2048             :                 pszProjection, nullptr, pszDstProjection, padfDstGeoTransform);
    2049           0 :             pfnTransformer = GDALGenImgProjTransform;
    2050             : 
    2051           0 :             CPLFree(pszProjection);
    2052             :         }
    2053             : 
    2054           0 :         for (auto &poFeat : poLayer)
    2055             :         {
    2056           0 :             OGRGeometry *poGeom = poFeat->GetGeometryRef();
    2057             : 
    2058           0 :             if (pszBurnAttribute)
    2059           0 :                 dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
    2060             : 
    2061           0 :             gv_rasterize_one_shape(
    2062             :                 static_cast<unsigned char *>(pData), 0, 0, nBufXSize, nBufYSize,
    2063             :                 1, eBufType, nPixelSpace, nLineSpace, 0, bAllTouched, poGeom,
    2064             :                 GDT_Float64, &dfBurnValue, nullptr, eBurnValueSource, eMergeAlg,
    2065             :                 pfnTransformer, pTransformArg);
    2066             :         }
    2067             : 
    2068           0 :         poLayer->ResetReading();
    2069             : 
    2070           0 :         if (!pfnProgress(1, "", pProgressArg))
    2071             :         {
    2072           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    2073           0 :             eErr = CE_Failure;
    2074             :         }
    2075             : 
    2076           0 :         if (bNeedToFreeTransformer)
    2077             :         {
    2078           0 :             GDALDestroyTransformer(pTransformArg);
    2079           0 :             pTransformArg = nullptr;
    2080           0 :             pfnTransformer = nullptr;
    2081             :         }
    2082             :     }
    2083             : 
    2084           0 :     return eErr;
    2085             : }

Generated by: LCOV version 1.14