LCOV - code coverage report
Current view: top level - alg - gdalrasterize.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 516 758 68.1 %
Date: 2026-05-28 11:29:44 Functions: 18 36 50.0 %

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

Generated by: LCOV version 1.14