Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdalrasterize.cpp
Line
Count
Source
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
template <typename T> static inline T SaturatedAddSigned(T a, T b)
43
0
{
44
0
    if (a > 0 && b > 0 && a > std::numeric_limits<T>::max() - b)
45
0
    {
46
0
        return std::numeric_limits<T>::max();
47
0
    }
48
0
    else if (a < 0 && b < 0 && a < std::numeric_limits<T>::min() - b)
49
0
    {
50
0
        return std::numeric_limits<T>::min();
51
0
    }
52
0
    else
53
0
    {
54
0
        return a + b;
55
0
    }
56
0
}
57
58
/************************************************************************/
59
/*                              MakeKey()                               */
60
/************************************************************************/
61
62
inline uint64_t MakeKey(int y, int x)
63
0
{
64
0
    return (static_cast<uint64_t>(y) << 32) | static_cast<uint64_t>(x);
65
0
}
66
67
/************************************************************************/
68
/*                        gvBurnScanlineBasic()                         */
69
/************************************************************************/
70
template <typename T>
71
static inline void gvBurnScanlineBasic(GDALRasterizeInfo *psInfo, int nY,
72
                                       int nXStart, int nXEnd, double dfVariant)
73
74
0
{
75
0
    for (int iBand = 0; iBand < psInfo->nBands; iBand++)
76
0
    {
77
0
        const double burnValue =
78
0
            (psInfo->burnValues.double_values[iBand] +
79
0
             ((psInfo->eBurnValueSource == GBV_UserBurnValue) ? 0 : dfVariant));
80
81
0
        unsigned char *pabyInsert =
82
0
            psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
83
0
            nY * psInfo->nLineSpace + nXStart * psInfo->nPixelSpace;
84
0
        if (psInfo->eMergeAlg == GRMA_Add)
85
0
        {
86
0
            if (psInfo->poSetVisitedPoints)
87
0
            {
88
0
                CPLAssert(!psInfo->bFillSetVisitedPoints);
89
0
                uint64_t nKey = MakeKey(nY, nXStart);
90
0
                auto &oSetVisitedPoints = *(psInfo->poSetVisitedPoints);
91
0
                for (int nX = nXStart; nX <= nXEnd; ++nX)
92
0
                {
93
0
                    if (oSetVisitedPoints.find(nKey) == oSetVisitedPoints.end())
94
0
                    {
95
0
                        double dfVal = static_cast<double>(
96
0
                                           *reinterpret_cast<T *>(pabyInsert)) +
97
0
                                       burnValue;
98
0
                        GDALCopyWord(dfVal, *reinterpret_cast<T *>(pabyInsert));
99
0
                    }
100
0
                    pabyInsert += psInfo->nPixelSpace;
101
0
                    ++nKey;
102
0
                }
103
0
            }
104
0
            else
105
0
            {
106
0
                for (int nX = nXStart; nX <= nXEnd; ++nX)
107
0
                {
108
0
                    double dfVal = static_cast<double>(
109
0
                                       *reinterpret_cast<T *>(pabyInsert)) +
110
0
                                   burnValue;
111
0
                    GDALCopyWord(dfVal, *reinterpret_cast<T *>(pabyInsert));
112
0
                    pabyInsert += psInfo->nPixelSpace;
113
0
                }
114
0
            }
115
0
        }
116
0
        else
117
0
        {
118
0
            T nVal;
119
0
            GDALCopyWord(burnValue, nVal);
120
0
            for (int nX = nXStart; nX <= nXEnd; ++nX)
121
0
            {
122
0
                *reinterpret_cast<T *>(pabyInsert) = nVal;
123
0
                pabyInsert += psInfo->nPixelSpace;
124
0
            }
125
0
        }
126
0
    }
127
0
}
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<unsigned char>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<signed char>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<short>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<unsigned short>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<int>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<unsigned int>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<long>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<unsigned long>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<cpl::Float16>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<float>(GDALRasterizeInfo*, int, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnScanlineBasic<double>(GDALRasterizeInfo*, int, int, int, double)
128
129
static inline void gvBurnScanlineInt64UserBurnValue(GDALRasterizeInfo *psInfo,
130
                                                    int nY, int nXStart,
131
                                                    int nXEnd)
132
133
0
{
134
0
    for (int iBand = 0; iBand < psInfo->nBands; iBand++)
135
0
    {
136
0
        const std::int64_t burnValue = psInfo->burnValues.int64_values[iBand];
137
138
0
        unsigned char *pabyInsert =
139
0
            psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
140
0
            nY * psInfo->nLineSpace + nXStart * psInfo->nPixelSpace;
141
0
        if (psInfo->eMergeAlg == GRMA_Add)
142
0
        {
143
0
            if (psInfo->poSetVisitedPoints)
144
0
            {
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
0
                {
150
0
                    if (oSetVisitedPoints.find(nKey) == oSetVisitedPoints.end())
151
0
                    {
152
0
                        *reinterpret_cast<std::int64_t *>(pabyInsert) =
153
0
                            SaturatedAddSigned(
154
0
                                *reinterpret_cast<std::int64_t *>(pabyInsert),
155
0
                                burnValue);
156
0
                    }
157
0
                    pabyInsert += psInfo->nPixelSpace;
158
0
                    ++nKey;
159
0
                }
160
0
            }
161
0
            else
162
0
            {
163
0
                for (int nX = nXStart; nX <= nXEnd; ++nX)
164
0
                {
165
0
                    *reinterpret_cast<std::int64_t *>(pabyInsert) =
166
0
                        SaturatedAddSigned(
167
0
                            *reinterpret_cast<std::int64_t *>(pabyInsert),
168
0
                            burnValue);
169
0
                    pabyInsert += psInfo->nPixelSpace;
170
0
                }
171
0
            }
172
0
        }
173
0
        else
174
0
        {
175
0
            for (int nX = nXStart; nX <= nXEnd; ++nX)
176
0
            {
177
0
                *reinterpret_cast<std::int64_t *>(pabyInsert) = burnValue;
178
0
                pabyInsert += psInfo->nPixelSpace;
179
0
            }
180
0
        }
181
0
    }
182
0
}
183
184
/************************************************************************/
185
/*                           gvBurnScanline()                           */
186
/************************************************************************/
187
static void gvBurnScanline(GDALRasterizeInfo *psInfo, int nY, int nXStart,
188
                           int nXEnd, double dfVariant)
189
190
0
{
191
0
    if (nXStart > nXEnd)
192
0
        return;
193
194
0
    CPLAssert(nY >= 0 && nY < psInfo->nYSize);
195
0
    CPLAssert(nXStart < psInfo->nXSize);
196
0
    CPLAssert(nXEnd >= 0);
197
198
0
    if (nXStart < 0)
199
0
        nXStart = 0;
200
0
    if (nXEnd >= psInfo->nXSize)
201
0
        nXEnd = psInfo->nXSize - 1;
202
203
0
    if (psInfo->eBurnValueType == GDT_Int64)
204
0
    {
205
0
        if (psInfo->eType == GDT_Int64 &&
206
0
            psInfo->eBurnValueSource == GBV_UserBurnValue)
207
0
        {
208
0
            gvBurnScanlineInt64UserBurnValue(psInfo, nY, nXStart, nXEnd);
209
0
        }
210
0
        else
211
0
        {
212
0
            CPLAssert(false);
213
0
        }
214
0
        return;
215
0
    }
216
217
0
    switch (psInfo->eType)
218
0
    {
219
0
        case GDT_UInt8:
220
0
            gvBurnScanlineBasic<GByte>(psInfo, nY, nXStart, nXEnd, dfVariant);
221
0
            break;
222
0
        case GDT_Int8:
223
0
            gvBurnScanlineBasic<GInt8>(psInfo, nY, nXStart, nXEnd, dfVariant);
224
0
            break;
225
0
        case GDT_Int16:
226
0
            gvBurnScanlineBasic<GInt16>(psInfo, nY, nXStart, nXEnd, dfVariant);
227
0
            break;
228
0
        case GDT_UInt16:
229
0
            gvBurnScanlineBasic<GUInt16>(psInfo, nY, nXStart, nXEnd, dfVariant);
230
0
            break;
231
0
        case GDT_Int32:
232
0
            gvBurnScanlineBasic<GInt32>(psInfo, nY, nXStart, nXEnd, dfVariant);
233
0
            break;
234
0
        case GDT_UInt32:
235
0
            gvBurnScanlineBasic<GUInt32>(psInfo, nY, nXStart, nXEnd, dfVariant);
236
0
            break;
237
0
        case GDT_Int64:
238
0
            gvBurnScanlineBasic<std::int64_t>(psInfo, nY, nXStart, nXEnd,
239
0
                                              dfVariant);
240
0
            break;
241
0
        case GDT_UInt64:
242
0
            gvBurnScanlineBasic<std::uint64_t>(psInfo, nY, nXStart, nXEnd,
243
0
                                               dfVariant);
244
0
            break;
245
0
        case GDT_Float16:
246
0
            gvBurnScanlineBasic<GFloat16>(psInfo, nY, nXStart, nXEnd,
247
0
                                          dfVariant);
248
0
            break;
249
0
        case GDT_Float32:
250
0
            gvBurnScanlineBasic<float>(psInfo, nY, nXStart, nXEnd, dfVariant);
251
0
            break;
252
0
        case GDT_Float64:
253
0
            gvBurnScanlineBasic<double>(psInfo, nY, nXStart, nXEnd, dfVariant);
254
0
            break;
255
0
        case GDT_CInt16:
256
0
        case GDT_CInt32:
257
0
        case GDT_CFloat16:
258
0
        case GDT_CFloat32:
259
0
        case GDT_CFloat64:
260
0
        case GDT_Unknown:
261
0
        case GDT_TypeCount:
262
0
            CPLAssert(false);
263
0
            break;
264
0
    }
265
0
}
266
267
/************************************************************************/
268
/*                          gvBurnPointBasic()                          */
269
/************************************************************************/
270
template <typename T>
271
static inline void gvBurnPointBasic(GDALRasterizeInfo *psInfo, int nY, int nX,
272
                                    double dfVariant)
273
274
0
{
275
0
    for (int iBand = 0; iBand < psInfo->nBands; iBand++)
276
0
    {
277
0
        double burnValue =
278
0
            (psInfo->burnValues.double_values[iBand] +
279
0
             ((psInfo->eBurnValueSource == GBV_UserBurnValue) ? 0 : dfVariant));
280
0
        unsigned char *pbyInsert =
281
0
            psInfo->pabyChunkBuf + iBand * psInfo->nBandSpace +
282
0
            nY * psInfo->nLineSpace + nX * psInfo->nPixelSpace;
283
284
0
        T *pbyPixel = reinterpret_cast<T *>(pbyInsert);
285
0
        if (psInfo->eMergeAlg == GRMA_Add)
286
0
            burnValue += static_cast<double>(*pbyPixel);
287
0
        GDALCopyWord(burnValue, *pbyPixel);
288
0
    }
289
0
}
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<unsigned char>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<signed char>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<short>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<unsigned short>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<int>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<unsigned int>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<long>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<unsigned long>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<cpl::Float16>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<float>(GDALRasterizeInfo*, int, int, double)
Unexecuted instantiation: gdalrasterize.cpp:void gvBurnPointBasic<double>(GDALRasterizeInfo*, int, int, double)
290
291
static inline void gvBurnPointInt64UserBurnValue(GDALRasterizeInfo *psInfo,
292
                                                 int nY, int nX)
293
294
0
{
295
0
    for (int iBand = 0; iBand < psInfo->nBands; iBand++)
296
0
    {
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
0
        {
305
0
            burnValue = SaturatedAddSigned(burnValue, *pbyPixel);
306
0
        }
307
0
        *pbyPixel = burnValue;
308
0
    }
309
0
}
310
311
/************************************************************************/
312
/*                            gvBurnPoint()                             */
313
/************************************************************************/
314
static void gvBurnPoint(GDALRasterizeInfo *psInfo, int nY, int nX,
315
                        double dfVariant)
316
317
0
{
318
319
0
    CPLAssert(nY >= 0 && nY < psInfo->nYSize);
320
0
    CPLAssert(nX >= 0 && nX < psInfo->nXSize);
321
322
0
    if (psInfo->poSetVisitedPoints)
323
0
    {
324
0
        const uint64_t nKey = MakeKey(nY, nX);
325
0
        if (psInfo->poSetVisitedPoints->find(nKey) ==
326
0
            psInfo->poSetVisitedPoints->end())
327
0
        {
328
0
            if (psInfo->bFillSetVisitedPoints)
329
0
                psInfo->poSetVisitedPoints->insert(nKey);
330
0
        }
331
0
        else
332
0
        {
333
0
            return;
334
0
        }
335
0
    }
336
337
0
    if (psInfo->eBurnValueType == GDT_Int64)
338
0
    {
339
0
        if (psInfo->eType == GDT_Int64 &&
340
0
            psInfo->eBurnValueSource == GBV_UserBurnValue)
341
0
        {
342
0
            gvBurnPointInt64UserBurnValue(psInfo, nY, nX);
343
0
        }
344
0
        else
345
0
        {
346
0
            CPLAssert(false);
347
0
        }
348
0
        return;
349
0
    }
350
351
0
    switch (psInfo->eType)
352
0
    {
353
0
        case GDT_UInt8:
354
0
            gvBurnPointBasic<GByte>(psInfo, nY, nX, dfVariant);
355
0
            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
0
        case GDT_Float32:
381
0
            gvBurnPointBasic<float>(psInfo, nY, nX, dfVariant);
382
0
            break;
383
0
        case GDT_Float64:
384
0
            gvBurnPointBasic<double>(psInfo, nY, nX, dfVariant);
385
0
            break;
386
0
        case GDT_CInt16:
387
0
        case GDT_CInt32:
388
0
        case GDT_CFloat16:
389
0
        case GDT_CFloat32:
390
0
        case GDT_CFloat64:
391
0
        case GDT_Unknown:
392
0
        case GDT_TypeCount:
393
0
            CPLAssert(false);
394
0
    }
395
0
}
396
397
/************************************************************************/
398
/*                    GDALCollectRingsFromGeometry()                    */
399
/************************************************************************/
400
401
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
0
{
409
0
    if (poShape == nullptr || poShape->IsEmpty())
410
0
        return;
411
412
0
    const OGRwkbGeometryType eFlatType = wkbFlatten(poShape->getGeometryType());
413
414
0
    if (eFlatType == wkbPoint)
415
0
    {
416
0
        const auto poPoint = poShape->toPoint();
417
418
0
        aPointX.push_back(poPoint->getX());
419
0
        aPointY.push_back(poPoint->getY());
420
0
        aPartSize.push_back(1);
421
0
        if (eBurnValueSrc != GBV_UserBurnValue)
422
0
        {
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
0
        }
433
0
    }
434
0
    else if (EQUAL(poShape->getGeometryName(), "LINEARRING"))
435
0
    {
436
0
        const auto poRing = poShape->toLinearRing();
437
0
        const int nCount = poRing->getNumPoints();
438
0
        const size_t nNewCount = aPointX.size() + static_cast<size_t>(nCount);
439
440
0
        aPointX.reserve(nNewCount);
441
0
        aPointY.reserve(nNewCount);
442
0
        if (eBurnValueSrc != GBV_UserBurnValue)
443
0
            aPointVariant.reserve(nNewCount);
444
0
        if (poRing->isClockwise())
445
0
        {
446
0
            for (int i = 0; i < nCount; i++)
447
0
            {
448
0
                aPointX.push_back(poRing->getX(i));
449
0
                aPointY.push_back(poRing->getY(i));
450
0
                if (eBurnValueSrc != GBV_UserBurnValue)
451
0
                {
452
                    /*switch( eBurnValueSrc )
453
                    {
454
                    case GBV_Z:*/
455
0
                    aPointVariant.push_back(poRing->getZ(i));
456
                    /*break;
457
                case GBV_M:
458
                    aPointVariant.push_back( poRing->getM(i) );
459
                }*/
460
0
                }
461
0
            }
462
0
        }
463
0
        else
464
0
        {
465
0
            for (int i = nCount - 1; i >= 0; i--)
466
0
            {
467
0
                aPointX.push_back(poRing->getX(i));
468
0
                aPointY.push_back(poRing->getY(i));
469
0
                if (eBurnValueSrc != GBV_UserBurnValue)
470
0
                {
471
                    /*switch( eBurnValueSrc )
472
                    {
473
                    case GBV_Z:*/
474
0
                    aPointVariant.push_back(poRing->getZ(i));
475
                    /*break;
476
                case GBV_M:
477
                    aPointVariant.push_back( poRing->getM(i) );
478
                }*/
479
0
                }
480
0
            }
481
0
        }
482
0
        aPartSize.push_back(nCount);
483
0
    }
484
0
    else if (eFlatType == wkbLineString)
485
0
    {
486
0
        const auto poLine = poShape->toLineString();
487
0
        const int nCount = poLine->getNumPoints();
488
0
        const size_t nNewCount = aPointX.size() + static_cast<size_t>(nCount);
489
490
0
        aPointX.reserve(nNewCount);
491
0
        aPointY.reserve(nNewCount);
492
0
        if (eBurnValueSrc != GBV_UserBurnValue)
493
0
            aPointVariant.reserve(nNewCount);
494
0
        for (int i = nCount - 1; i >= 0; i--)
495
0
        {
496
0
            aPointX.push_back(poLine->getX(i));
497
0
            aPointY.push_back(poLine->getY(i));
498
0
            if (eBurnValueSrc != GBV_UserBurnValue)
499
0
            {
500
                /*switch( eBurnValueSrc )
501
                {
502
                    case GBV_Z:*/
503
0
                aPointVariant.push_back(poLine->getZ(i));
504
                /*break;
505
            case GBV_M:
506
                aPointVariant.push_back( poLine->getM(i) );
507
        }*/
508
0
            }
509
0
        }
510
0
        aPartSize.push_back(nCount);
511
0
    }
512
0
    else if (eFlatType == wkbPolygon)
513
0
    {
514
0
        const auto poPolygon = poShape->toPolygon();
515
516
0
        GDALCollectRingsFromGeometry(poPolygon->getExteriorRing(), aPointX,
517
0
                                     aPointY, aPointVariant, aPartSize,
518
0
                                     eBurnValueSrc);
519
520
0
        for (int i = 0; i < poPolygon->getNumInteriorRings(); i++)
521
0
            GDALCollectRingsFromGeometry(poPolygon->getInteriorRing(i), aPointX,
522
0
                                         aPointY, aPointVariant, aPartSize,
523
0
                                         eBurnValueSrc);
524
0
    }
525
0
    else if (eFlatType == wkbMultiPoint || eFlatType == wkbMultiLineString ||
526
0
             eFlatType == wkbMultiPolygon || eFlatType == wkbGeometryCollection)
527
0
    {
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
0
                                         aPointY, aPointVariant, aPartSize,
532
0
                                         eBurnValueSrc);
533
0
    }
534
0
    else
535
0
    {
536
0
        CPLDebug("GDAL", "Rasterizer ignoring non-polygonal geometry.");
537
0
    }
538
0
}
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
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
0
{
581
0
    std::unique_ptr<OGRGeometry> poLinearizedShape;
582
583
0
    if (poShape == nullptr || poShape->IsEmpty())
584
0
        return;
585
586
0
    if (poShape->hasCurveGeometry())
587
0
    {
588
0
        poLinearizedShape.reset(poShape->getLinearGeometry());
589
0
        if (!poLinearizedShape)
590
0
        {
591
0
            CPLError(CE_Failure, CPLE_AppDefined,
592
0
                     "Failed to linearize geometry");
593
0
            return;
594
0
        }
595
0
        poShape = poLinearizedShape.get();
596
0
    }
597
598
0
    const auto eGeomType = wkbFlatten(poShape->getGeometryType());
599
600
0
    if ((eGeomType == wkbMultiLineString || eGeomType == wkbMultiPolygon ||
601
0
         eGeomType == wkbGeometryCollection) &&
602
0
        eMergeAlg == GRMA_Replace)
603
0
    {
604
        // Speed optimization: in replace mode, we can rasterize each part of
605
        // a geometry collection separately.
606
0
        const auto poGC = poShape->toGeometryCollection();
607
0
        for (const auto poPart : *poGC)
608
0
        {
609
0
            gv_rasterize_one_shape(
610
0
                pabyChunkBuf, nXOff, nYOff, nXSize, nYSize, nBands, eType,
611
0
                nPixelSpace, nLineSpace, nBandSpace, bAllTouched, poPart,
612
0
                eBurnValueType, padfBurnValues, panBurnValues, eBurnValueSrc,
613
0
                eMergeAlg, pfnTransformer, pTransformArg);
614
0
        }
615
0
        return;
616
0
    }
617
618
0
    if (nPixelSpace == 0)
619
0
    {
620
0
        nPixelSpace = GDALGetDataTypeSizeBytes(eType);
621
0
    }
622
0
    if (nLineSpace == 0)
623
0
    {
624
0
        nLineSpace = static_cast<GSpacing>(nXSize) * nPixelSpace;
625
0
    }
626
0
    if (nBandSpace == 0)
627
0
    {
628
0
        nBandSpace = nYSize * nLineSpace;
629
0
    }
630
631
0
    GDALRasterizeInfo sInfo;
632
0
    sInfo.nXSize = nXSize;
633
0
    sInfo.nYSize = nYSize;
634
0
    sInfo.nBands = nBands;
635
0
    sInfo.pabyChunkBuf = pabyChunkBuf;
636
0
    sInfo.eType = eType;
637
0
    sInfo.nPixelSpace = nPixelSpace;
638
0
    sInfo.nLineSpace = nLineSpace;
639
0
    sInfo.nBandSpace = nBandSpace;
640
0
    sInfo.eBurnValueType = eBurnValueType;
641
0
    if (eBurnValueType == GDT_Float64)
642
0
        sInfo.burnValues.double_values = padfBurnValues;
643
0
    else if (eBurnValueType == GDT_Int64)
644
0
        sInfo.burnValues.int64_values = panBurnValues;
645
0
    else
646
0
    {
647
0
        CPLAssert(false);
648
0
    }
649
0
    sInfo.eBurnValueSource = eBurnValueSrc;
650
0
    sInfo.eMergeAlg = eMergeAlg;
651
0
    sInfo.bFillSetVisitedPoints = false;
652
0
    sInfo.poSetVisitedPoints = nullptr;
653
654
    /* -------------------------------------------------------------------- */
655
    /*      Transform polygon geometries into a set of rings and a part     */
656
    /*      size list.                                                      */
657
    /* -------------------------------------------------------------------- */
658
0
    std::vector<double>
659
0
        aPointX;  // coordinate X values from all rings/components
660
0
    std::vector<double>
661
0
        aPointY;  // coordinate Y values from all rings/components
662
0
    std::vector<double> aPointVariant;  // coordinate Z values
663
0
    std::vector<int> aPartSize;  // number of X/Y/(Z) values associated with
664
                                 // each ring/component
665
666
0
    GDALCollectRingsFromGeometry(poShape, aPointX, aPointY, aPointVariant,
667
0
                                 aPartSize, eBurnValueSrc);
668
669
    /* -------------------------------------------------------------------- */
670
    /*      Transform points if needed.                                     */
671
    /* -------------------------------------------------------------------- */
672
0
    if (pfnTransformer != nullptr)
673
0
    {
674
0
        int *panSuccess =
675
0
            static_cast<int *>(CPLCalloc(sizeof(int), aPointX.size()));
676
677
        // TODO: We need to add all appropriate error checking at some point.
678
0
        pfnTransformer(pTransformArg, FALSE, static_cast<int>(aPointX.size()),
679
0
                       aPointX.data(), aPointY.data(), nullptr, panSuccess);
680
0
        CPLFree(panSuccess);
681
0
    }
682
683
    /* -------------------------------------------------------------------- */
684
    /*      Shift to account for the buffer offset of this buffer.          */
685
    /* -------------------------------------------------------------------- */
686
0
    for (unsigned int i = 0; i < aPointX.size(); i++)
687
0
        aPointX[i] -= nXOff;
688
0
    for (unsigned int i = 0; i < aPointY.size(); i++)
689
0
        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
0
    switch (eGeomType)
698
0
    {
699
0
        case wkbPoint:
700
0
        case wkbMultiPoint:
701
0
            GDALdllImagePoint(
702
0
                sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
703
0
                aPartSize.data(), aPointX.data(), aPointY.data(),
704
0
                (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
705
0
                                                     : aPointVariant.data(),
706
0
                gvBurnPoint, &sInfo);
707
0
            break;
708
0
        case wkbLineString:
709
0
        case wkbMultiLineString:
710
0
        {
711
0
            if (eMergeAlg == GRMA_Add)
712
0
            {
713
0
                sInfo.bFillSetVisitedPoints = true;
714
0
                sInfo.poSetVisitedPoints = new std::set<uint64_t>();
715
0
            }
716
0
            if (bAllTouched)
717
0
                GDALdllImageLineAllTouched(
718
0
                    sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
719
0
                    aPartSize.data(), aPointX.data(), aPointY.data(),
720
0
                    (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
721
0
                                                         : aPointVariant.data(),
722
0
                    gvBurnPoint, &sInfo, eMergeAlg == GRMA_Add, false);
723
0
            else
724
0
                GDALdllImageLine(
725
0
                    sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
726
0
                    aPartSize.data(), aPointX.data(), aPointY.data(),
727
0
                    (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
728
0
                                                         : aPointVariant.data(),
729
0
                    gvBurnPoint, &sInfo);
730
0
        }
731
0
        break;
732
733
0
        default:
734
0
        {
735
0
            if (eMergeAlg == GRMA_Add)
736
0
            {
737
0
                sInfo.bFillSetVisitedPoints = true;
738
0
                sInfo.poSetVisitedPoints = new std::set<uint64_t>();
739
0
            }
740
0
            if (bAllTouched)
741
0
            {
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
0
                if (eBurnValueSrc == GBV_UserBurnValue)
747
0
                {
748
0
                    GDALdllImageLineAllTouched(
749
0
                        sInfo.nXSize, nYSize,
750
0
                        static_cast<int>(aPartSize.size()), aPartSize.data(),
751
0
                        aPointX.data(), aPointY.data(), nullptr, gvBurnPoint,
752
0
                        &sInfo, eMergeAlg == GRMA_Add, true);
753
0
                }
754
0
                else
755
0
                {
756
0
                    for (unsigned int i = 0, n = 0;
757
0
                         i < static_cast<unsigned int>(aPartSize.size()); i++)
758
0
                    {
759
0
                        for (int j = 0; j < aPartSize[i]; j++)
760
0
                            aPointVariant[n++] = aPointVariant[0];
761
0
                    }
762
763
0
                    GDALdllImageLineAllTouched(
764
0
                        sInfo.nXSize, nYSize,
765
0
                        static_cast<int>(aPartSize.size()), aPartSize.data(),
766
0
                        aPointX.data(), aPointY.data(), aPointVariant.data(),
767
0
                        gvBurnPoint, &sInfo, eMergeAlg == GRMA_Add, true);
768
0
                }
769
0
            }
770
0
            sInfo.bFillSetVisitedPoints = false;
771
0
            GDALdllImageFilledPolygon(
772
0
                sInfo.nXSize, nYSize, static_cast<int>(aPartSize.size()),
773
0
                aPartSize.data(), aPointX.data(), aPointY.data(),
774
0
                (eBurnValueSrc == GBV_UserBurnValue) ? nullptr
775
0
                                                     : aPointVariant.data(),
776
0
                gvBurnScanline, &sInfo, eMergeAlg == GRMA_Add);
777
0
        }
778
0
        break;
779
0
    }
780
781
0
    delete sInfo.poSetVisitedPoints;
782
0
}
783
784
/************************************************************************/
785
/*                        GDALRasterizeOptions()                        */
786
/*                                                                      */
787
/*      Recognise a few rasterize options used by all three entry       */
788
/*      points.                                                         */
789
/************************************************************************/
790
791
static CPLErr GDALRasterizeOptions(CSLConstList papszOptions, int *pbAllTouched,
792
                                   GDALBurnValueSrc *peBurnValueSource,
793
                                   GDALRasterMergeAlg *peMergeAlg,
794
                                   GDALRasterizeOptim *peOptim)
795
0
{
796
0
    *pbAllTouched = CPLFetchBool(papszOptions, "ALL_TOUCHED", false);
797
798
0
    const char *pszOpt = CSLFetchNameValue(papszOptions, "BURN_VALUE_FROM");
799
0
    *peBurnValueSource = GBV_UserBurnValue;
800
0
    if (pszOpt)
801
0
    {
802
0
        if (EQUAL(pszOpt, "Z"))
803
0
        {
804
0
            *peBurnValueSource = GBV_Z;
805
0
        }
806
        // else if( EQUAL(pszOpt, "M"))
807
        //     eBurnValueSource = GBV_M;
808
0
        else
809
0
        {
810
0
            CPLError(CE_Failure, CPLE_AppDefined,
811
0
                     "Unrecognized value '%s' for BURN_VALUE_FROM.", pszOpt);
812
0
            return CE_Failure;
813
0
        }
814
0
    }
815
816
    /* -------------------------------------------------------------------- */
817
    /*      MERGE_ALG=[REPLACE]/ADD                                         */
818
    /* -------------------------------------------------------------------- */
819
0
    *peMergeAlg = GRMA_Replace;
820
0
    pszOpt = CSLFetchNameValue(papszOptions, "MERGE_ALG");
821
0
    if (pszOpt)
822
0
    {
823
0
        if (EQUAL(pszOpt, "ADD"))
824
0
        {
825
0
            *peMergeAlg = GRMA_Add;
826
0
        }
827
0
        else if (EQUAL(pszOpt, "REPLACE"))
828
0
        {
829
0
            *peMergeAlg = GRMA_Replace;
830
0
        }
831
0
        else
832
0
        {
833
0
            CPLError(CE_Failure, CPLE_AppDefined,
834
0
                     "Unrecognized value '%s' for MERGE_ALG.", pszOpt);
835
0
            return CE_Failure;
836
0
        }
837
0
    }
838
839
    /* -------------------------------------------------------------------- */
840
    /*      OPTIM=[AUTO]/RASTER/VECTOR                                      */
841
    /* -------------------------------------------------------------------- */
842
0
    pszOpt = CSLFetchNameValue(papszOptions, "OPTIM");
843
0
    if (pszOpt)
844
0
    {
845
0
        if (peOptim)
846
0
        {
847
0
            *peOptim = GRO_Auto;
848
0
            if (EQUAL(pszOpt, "RASTER"))
849
0
            {
850
0
                *peOptim = GRO_Raster;
851
0
            }
852
0
            else if (EQUAL(pszOpt, "VECTOR"))
853
0
            {
854
0
                *peOptim = GRO_Vector;
855
0
            }
856
0
            else if (EQUAL(pszOpt, "AUTO"))
857
0
            {
858
0
                *peOptim = GRO_Auto;
859
0
            }
860
0
            else
861
0
            {
862
0
                CPLError(CE_Failure, CPLE_AppDefined,
863
0
                         "Unrecognized value '%s' for OPTIM.", pszOpt);
864
0
                return CE_Failure;
865
0
            }
866
0
        }
867
0
        else
868
0
        {
869
0
            CPLError(CE_Warning, CPLE_NotSupported,
870
0
                     "Option OPTIM is not supported by this function");
871
0
        }
872
0
    }
873
874
0
    return CE_None;
875
0
}
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
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
0
{
1006
0
    VALIDATE_POINTER1(hDS, "GDALRasterizeGeometries", CE_Failure);
1007
1008
0
    return GDALRasterizeGeometriesInternal(
1009
0
        hDS, nBandCount, panBandList, nGeomCount, pahGeometries, pfnTransformer,
1010
0
        pTransformArg, GDT_Float64, padfGeomBurnValues, nullptr, papszOptions,
1011
0
        pfnProgress, pProgressArg);
1012
0
}
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
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
0
{
1029
0
    VALIDATE_POINTER1(hDS, "GDALRasterizeGeometriesInt64", CE_Failure);
1030
1031
0
    return GDALRasterizeGeometriesInternal(
1032
0
        hDS, nBandCount, panBandList, nGeomCount, pahGeometries, pfnTransformer,
1033
0
        pTransformArg, GDT_Int64, nullptr, panGeomBurnValues, papszOptions,
1034
0
        pfnProgress, pProgressArg);
1035
0
}
1036
1037
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
0
{
1045
0
    if (pfnProgress == nullptr)
1046
0
        pfnProgress = GDALDummyProgress;
1047
1048
0
    GDALDataset *poDS = GDALDataset::FromHandle(hDS);
1049
    /* -------------------------------------------------------------------- */
1050
    /*      Do some rudimentary arg checking.                               */
1051
    /* -------------------------------------------------------------------- */
1052
0
    if (nBandCount == 0 || nGeomCount == 0)
1053
0
    {
1054
0
        pfnProgress(1.0, "", pProgressArg);
1055
0
        return CE_None;
1056
0
    }
1057
1058
0
    if (eBurnValueType == GDT_Int64)
1059
0
    {
1060
0
        for (int i = 0; i < nBandCount; i++)
1061
0
        {
1062
0
            GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[i]);
1063
0
            if (poBand == nullptr)
1064
0
                return CE_Failure;
1065
0
            if (poBand->GetRasterDataType() != GDT_Int64)
1066
0
            {
1067
0
                CPLError(CE_Failure, CPLE_NotSupported,
1068
0
                         "GDALRasterizeGeometriesInt64() only supported on "
1069
0
                         "Int64 raster");
1070
0
                return CE_Failure;
1071
0
            }
1072
0
        }
1073
0
    }
1074
1075
    // Prototype band.
1076
0
    GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[0]);
1077
0
    if (poBand == nullptr)
1078
0
        return CE_Failure;
1079
1080
    /* -------------------------------------------------------------------- */
1081
    /*      Options                                                         */
1082
    /* -------------------------------------------------------------------- */
1083
0
    int bAllTouched = FALSE;
1084
0
    GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
1085
0
    GDALRasterMergeAlg eMergeAlg = GRMA_Replace;
1086
0
    GDALRasterizeOptim eOptim = GRO_Auto;
1087
0
    if (GDALRasterizeOptions(papszOptions, &bAllTouched, &eBurnValueSource,
1088
0
                             &eMergeAlg, &eOptim) == CE_Failure)
1089
0
    {
1090
0
        return CE_Failure;
1091
0
    }
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
0
    bool bNeedToFreeTransformer = false;
1102
1103
0
    if (pfnTransformer == nullptr)
1104
0
    {
1105
0
        bNeedToFreeTransformer = true;
1106
1107
0
        CPLStringList aosTransformerOptions;
1108
0
        GDALGeoTransform gt;
1109
0
        if (poDS->GetGeoTransform(gt) != CE_None && poDS->GetGCPCount() == 0 &&
1110
0
            poDS->GetMetadata(GDAL_MDD_RPC) == nullptr)
1111
0
        {
1112
0
            aosTransformerOptions.SetNameValue("DST_METHOD", "NO_GEOTRANSFORM");
1113
0
        }
1114
1115
0
        pTransformArg = GDALCreateGenImgProjTransformer2(
1116
0
            nullptr, hDS, aosTransformerOptions.List());
1117
1118
0
        pfnTransformer = GDALGenImgProjTransform;
1119
0
        if (pTransformArg == nullptr)
1120
0
        {
1121
0
            return CE_Failure;
1122
0
        }
1123
0
    }
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
0
    int nXBlockSize, nYBlockSize;
1132
0
    poBand->GetBlockSize(&nXBlockSize, &nYBlockSize);
1133
1134
0
    if (eOptim == GRO_Auto)
1135
0
    {
1136
0
        eOptim = GRO_Raster;
1137
        // TODO make more tests with various inputs/outputs to adjust the
1138
        // parameters
1139
0
        if (nYBlockSize > 1 && nGeomCount > 10000 &&
1140
0
            (poBand->GetXSize() * static_cast<long long>(poBand->GetYSize()) /
1141
0
                 nGeomCount >
1142
0
             50))
1143
0
        {
1144
0
            eOptim = GRO_Vector;
1145
0
            CPLDebug("GDAL", "The vector optim has been chosen automatically");
1146
0
        }
1147
0
    }
1148
1149
    /* -------------------------------------------------------------------- */
1150
    /*      The original algorithm                                          */
1151
    /*      Optimized for raster writing                                    */
1152
    /*      (optimal on a small number of large vectors)                    */
1153
    /* -------------------------------------------------------------------- */
1154
0
    unsigned char *pabyChunkBuf;
1155
0
    CPLErr eErr = CE_None;
1156
0
    if (eOptim == GRO_Raster)
1157
0
    {
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
0
        const GDALDataType eType =
1166
0
            GDALGetNonComplexDataType(poBand->GetRasterDataType());
1167
1168
0
        const uint64_t nScanlineBytes = static_cast<uint64_t>(nBandCount) *
1169
0
                                        poDS->GetRasterXSize() *
1170
0
                                        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
0
        int nYChunkSize =
1184
0
            atoi(CSLFetchNameValueDef(papszOptions, "CHUNKYSIZE", "0"));
1185
0
        if (nYChunkSize <= 0)
1186
0
        {
1187
0
            const GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes;
1188
0
            const int knIntMax = std::numeric_limits<int>::max();
1189
0
            nYChunkSize = nYChunkSize64 > knIntMax
1190
0
                              ? knIntMax
1191
0
                              : static_cast<int>(nYChunkSize64);
1192
0
        }
1193
1194
0
        if (nYChunkSize < 1)
1195
0
            nYChunkSize = 1;
1196
0
        if (nYChunkSize > poDS->GetRasterYSize())
1197
0
            nYChunkSize = poDS->GetRasterYSize();
1198
1199
0
        CPLDebug("GDAL", "Rasterizer operating on %d swaths of %d scanlines.",
1200
0
                 DIV_ROUND_UP(poDS->GetRasterYSize(), nYChunkSize),
1201
0
                 nYChunkSize);
1202
1203
0
        pabyChunkBuf = static_cast<unsigned char *>(VSI_MALLOC2_VERBOSE(
1204
0
            nYChunkSize, static_cast<size_t>(nScanlineBytes)));
1205
0
        if (pabyChunkBuf == nullptr)
1206
0
        {
1207
0
            if (bNeedToFreeTransformer)
1208
0
                GDALDestroyTransformer(pTransformArg);
1209
0
            return CE_Failure;
1210
0
        }
1211
1212
        /* ====================================================================
1213
         */
1214
        /*      Loop over image in designated chunks. */
1215
        /* ====================================================================
1216
         */
1217
0
        pfnProgress(0.0, nullptr, pProgressArg);
1218
1219
0
        for (int iY = 0; iY < poDS->GetRasterYSize() && eErr == CE_None;
1220
0
             iY += nYChunkSize)
1221
0
        {
1222
0
            int nThisYChunkSize = nYChunkSize;
1223
0
            if (nThisYChunkSize + iY > poDS->GetRasterYSize())
1224
0
                nThisYChunkSize = poDS->GetRasterYSize() - iY;
1225
1226
0
            eErr = poDS->RasterIO(
1227
0
                GF_Read, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
1228
0
                pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize, eType,
1229
0
                nBandCount, panBandList, 0, 0, 0, nullptr);
1230
0
            if (eErr != CE_None)
1231
0
                break;
1232
1233
0
            for (int iShape = 0; iShape < nGeomCount; iShape++)
1234
0
            {
1235
0
                gv_rasterize_one_shape(
1236
0
                    pabyChunkBuf, 0, iY, poDS->GetRasterXSize(),
1237
0
                    nThisYChunkSize, nBandCount, eType, 0, 0, 0, bAllTouched,
1238
0
                    OGRGeometry::FromHandle(pahGeometries[iShape]),
1239
0
                    eBurnValueType,
1240
0
                    padfGeomBurnValues
1241
0
                        ? padfGeomBurnValues +
1242
0
                              static_cast<size_t>(iShape) * nBandCount
1243
0
                        : nullptr,
1244
0
                    panGeomBurnValues
1245
0
                        ? panGeomBurnValues +
1246
0
                              static_cast<size_t>(iShape) * nBandCount
1247
0
                        : nullptr,
1248
0
                    eBurnValueSource, eMergeAlg, pfnTransformer, pTransformArg);
1249
0
            }
1250
1251
0
            eErr = poDS->RasterIO(
1252
0
                GF_Write, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
1253
0
                pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize, eType,
1254
0
                nBandCount, panBandList, 0, 0, 0, nullptr);
1255
1256
0
            if (!pfnProgress((iY + nThisYChunkSize) /
1257
0
                                 static_cast<double>(poDS->GetRasterYSize()),
1258
0
                             "", pProgressArg))
1259
0
            {
1260
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1261
0
                eErr = CE_Failure;
1262
0
            }
1263
0
        }
1264
0
    }
1265
    /* -------------------------------------------------------------------- */
1266
    /*      The new algorithm                                               */
1267
    /*      Optimized to minimize the vector computation                    */
1268
    /*      (optimal on a large number of vectors & tiled raster)           */
1269
    /* -------------------------------------------------------------------- */
1270
0
    else
1271
0
    {
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
0
        const int nXBlocks = DIV_ROUND_UP(poBand->GetXSize(), nXBlockSize);
1279
0
        const int nYBlocks = DIV_ROUND_UP(poBand->GetYSize(), nYBlockSize);
1280
1281
0
        const GDALDataType eType =
1282
0
            poBand->GetRasterDataType() == GDT_UInt8 ? GDT_UInt8 : GDT_Float64;
1283
1284
0
        const int nPixelSize = nBandCount * GDALGetDataTypeSizeBytes(eType);
1285
1286
        // rem: optimized for square blocks
1287
0
        const GIntBig nbMaxBlocks64 =
1288
0
            GDALGetCacheMax64() / nPixelSize / nYBlockSize / nXBlockSize;
1289
0
        const int knIntMax = std::numeric_limits<int>::max();
1290
0
        const int nbMaxBlocks = static_cast<int>(
1291
0
            std::min(static_cast<GIntBig>(knIntMax / nPixelSize / nYBlockSize /
1292
0
                                          nXBlockSize),
1293
0
                     nbMaxBlocks64));
1294
0
        const int nbBlocksX = std::max(
1295
0
            1,
1296
0
            std::min(static_cast<int>(sqrt(static_cast<double>(nbMaxBlocks))),
1297
0
                     nXBlocks));
1298
0
        const int nbBlocksY =
1299
0
            std::max(1, std::min(nbMaxBlocks / nbBlocksX, nYBlocks));
1300
1301
0
        const uint64_t nChunkSize = static_cast<uint64_t>(nXBlockSize) *
1302
0
                                    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
0
        pabyChunkBuf = static_cast<unsigned char *>(
1316
0
            VSI_MALLOC2_VERBOSE(nPixelSize, static_cast<size_t>(nChunkSize)));
1317
0
        if (pabyChunkBuf == nullptr)
1318
0
        {
1319
0
            if (bNeedToFreeTransformer)
1320
0
                GDALDestroyTransformer(pTransformArg);
1321
0
            return CE_Failure;
1322
0
        }
1323
1324
0
        OGREnvelope sRasterEnvelope;
1325
0
        sRasterEnvelope.MinX = 0;
1326
0
        sRasterEnvelope.MinY = 0;
1327
0
        sRasterEnvelope.MaxX = poDS->GetRasterXSize();
1328
0
        sRasterEnvelope.MaxY = poDS->GetRasterYSize();
1329
1330
        /* --------------------------------------------------------------------
1331
         */
1332
        /*      loop over the vectorial geometries */
1333
        /* --------------------------------------------------------------------
1334
         */
1335
0
        pfnProgress(0.0, nullptr, pProgressArg);
1336
0
        for (int iShape = 0; iShape < nGeomCount; iShape++)
1337
0
        {
1338
1339
0
            const OGRGeometry *poGeometry =
1340
0
                OGRGeometry::FromHandle(pahGeometries[iShape]);
1341
0
            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
0
            OGREnvelope sGeomEnvelope;
1348
0
            poGeometry->getEnvelope(&sGeomEnvelope);
1349
0
            if (pfnTransformer != nullptr)
1350
0
            {
1351
0
                int anSuccessTransform[2] = {0};
1352
0
                double apCorners[4];
1353
0
                apCorners[0] = sGeomEnvelope.MinX;
1354
0
                apCorners[1] = sGeomEnvelope.MaxX;
1355
0
                apCorners[2] = sGeomEnvelope.MinY;
1356
0
                apCorners[3] = sGeomEnvelope.MaxY;
1357
1358
0
                if (!pfnTransformer(pTransformArg, FALSE, 2, &(apCorners[0]),
1359
0
                                    &(apCorners[2]), nullptr,
1360
0
                                    anSuccessTransform) ||
1361
0
                    !anSuccessTransform[0] || !anSuccessTransform[1])
1362
0
                {
1363
0
                    continue;
1364
0
                }
1365
0
                sGeomEnvelope.MinX = std::min(apCorners[0], apCorners[1]);
1366
0
                sGeomEnvelope.MaxX = std::max(apCorners[0], apCorners[1]);
1367
0
                sGeomEnvelope.MinY = std::min(apCorners[2], apCorners[3]);
1368
0
                sGeomEnvelope.MaxY = std::max(apCorners[2], apCorners[3]);
1369
0
            }
1370
0
            if (!sGeomEnvelope.Intersects(sRasterEnvelope))
1371
0
                continue;
1372
0
            sGeomEnvelope.Intersect(sRasterEnvelope);
1373
0
            CPLAssert(sGeomEnvelope.MinX >= 0 &&
1374
0
                      sGeomEnvelope.MinX <= poDS->GetRasterXSize());
1375
0
            CPLAssert(sGeomEnvelope.MinY >= 0 &&
1376
0
                      sGeomEnvelope.MinY <= poDS->GetRasterYSize());
1377
0
            CPLAssert(sGeomEnvelope.MaxX >= 0 &&
1378
0
                      sGeomEnvelope.MaxX <= poDS->GetRasterXSize());
1379
0
            CPLAssert(sGeomEnvelope.MaxY >= 0 &&
1380
0
                      sGeomEnvelope.MaxY <= poDS->GetRasterYSize());
1381
0
            const int minBlockX = int(sGeomEnvelope.MinX) / nXBlockSize;
1382
0
            const int minBlockY = int(sGeomEnvelope.MinY) / nYBlockSize;
1383
0
            const int maxBlockX = int(sGeomEnvelope.MaxX + 1) / nXBlockSize;
1384
0
            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
0
            for (int xB = minBlockX; xB <= maxBlockX; xB += nbBlocksX)
1392
0
            {
1393
0
                for (int yB = minBlockY; yB <= maxBlockY; yB += nbBlocksY)
1394
0
                {
1395
1396
                    /* --------------------------------------------------------------------
1397
                     */
1398
                    /*      ensure to stay in the image */
1399
                    /* --------------------------------------------------------------------
1400
                     */
1401
0
                    int remSBX = std::min(maxBlockX - xB + 1, nbBlocksX);
1402
0
                    int remSBY = std::min(maxBlockY - yB + 1, nbBlocksY);
1403
0
                    int nThisXChunkSize = nXBlockSize * remSBX;
1404
0
                    int nThisYChunkSize = nYBlockSize * remSBY;
1405
0
                    if (xB * nXBlockSize + nThisXChunkSize >
1406
0
                        poDS->GetRasterXSize())
1407
0
                        nThisXChunkSize =
1408
0
                            poDS->GetRasterXSize() - xB * nXBlockSize;
1409
0
                    if (yB * nYBlockSize + nThisYChunkSize >
1410
0
                        poDS->GetRasterYSize())
1411
0
                        nThisYChunkSize =
1412
0
                            poDS->GetRasterYSize() - yB * nYBlockSize;
1413
1414
                    /* --------------------------------------------------------------------
1415
                     */
1416
                    /*      read image / process buffer / write buffer */
1417
                    /* --------------------------------------------------------------------
1418
                     */
1419
0
                    eErr = poDS->RasterIO(
1420
0
                        GF_Read, xB * nXBlockSize, yB * nYBlockSize,
1421
0
                        nThisXChunkSize, nThisYChunkSize, pabyChunkBuf,
1422
0
                        nThisXChunkSize, nThisYChunkSize, eType, nBandCount,
1423
0
                        panBandList, 0, 0, 0, nullptr);
1424
0
                    if (eErr != CE_None)
1425
0
                        break;
1426
1427
0
                    gv_rasterize_one_shape(
1428
0
                        pabyChunkBuf, xB * nXBlockSize, yB * nYBlockSize,
1429
0
                        nThisXChunkSize, nThisYChunkSize, nBandCount, eType, 0,
1430
0
                        0, 0, bAllTouched,
1431
0
                        OGRGeometry::FromHandle(pahGeometries[iShape]),
1432
0
                        eBurnValueType,
1433
0
                        padfGeomBurnValues
1434
0
                            ? padfGeomBurnValues +
1435
0
                                  static_cast<size_t>(iShape) * nBandCount
1436
0
                            : nullptr,
1437
0
                        panGeomBurnValues
1438
0
                            ? panGeomBurnValues +
1439
0
                                  static_cast<size_t>(iShape) * nBandCount
1440
0
                            : nullptr,
1441
0
                        eBurnValueSource, eMergeAlg, pfnTransformer,
1442
0
                        pTransformArg);
1443
1444
0
                    eErr = poDS->RasterIO(
1445
0
                        GF_Write, xB * nXBlockSize, yB * nYBlockSize,
1446
0
                        nThisXChunkSize, nThisYChunkSize, pabyChunkBuf,
1447
0
                        nThisXChunkSize, nThisYChunkSize, eType, nBandCount,
1448
0
                        panBandList, 0, 0, 0, nullptr);
1449
0
                    if (eErr != CE_None)
1450
0
                        break;
1451
0
                }
1452
0
            }
1453
1454
0
            if (!pfnProgress(iShape / static_cast<double>(nGeomCount), "",
1455
0
                             pProgressArg))
1456
0
            {
1457
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1458
0
                eErr = CE_Failure;
1459
0
            }
1460
0
        }
1461
1462
0
        if (!pfnProgress(1., "", pProgressArg))
1463
0
        {
1464
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1465
0
            eErr = CE_Failure;
1466
0
        }
1467
0
    }
1468
1469
    /* -------------------------------------------------------------------- */
1470
    /*      cleanup                                                         */
1471
    /* -------------------------------------------------------------------- */
1472
0
    VSIFree(pabyChunkBuf);
1473
1474
0
    if (bNeedToFreeTransformer)
1475
0
        GDALDestroyTransformer(pTransformArg);
1476
1477
0
    return eErr;
1478
0
}
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
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
0
{
1550
0
    VALIDATE_POINTER1(hDS, "GDALRasterizeLayers", CE_Failure);
1551
1552
0
    if (pfnProgress == nullptr)
1553
0
        pfnProgress = GDALDummyProgress;
1554
1555
    /* -------------------------------------------------------------------- */
1556
    /*      Do some rudimentary arg checking.                               */
1557
    /* -------------------------------------------------------------------- */
1558
0
    if (nBandCount == 0 || nLayerCount == 0)
1559
0
        return CE_None;
1560
1561
0
    GDALDataset *poDS = GDALDataset::FromHandle(hDS);
1562
1563
    // Prototype band.
1564
0
    GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[0]);
1565
0
    if (poBand == nullptr)
1566
0
        return CE_Failure;
1567
1568
    /* -------------------------------------------------------------------- */
1569
    /*      Options                                                         */
1570
    /* -------------------------------------------------------------------- */
1571
0
    int bAllTouched = FALSE;
1572
0
    GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
1573
0
    GDALRasterMergeAlg eMergeAlg = GRMA_Replace;
1574
0
    if (GDALRasterizeOptions(papszOptions, &bAllTouched, &eBurnValueSource,
1575
0
                             &eMergeAlg, nullptr) == CE_Failure)
1576
0
    {
1577
0
        return CE_Failure;
1578
0
    }
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
0
    const char *pszYChunkSize = CSLFetchNameValue(papszOptions, "CHUNKYSIZE");
1586
1587
0
    const GDALDataType eType = poBand->GetRasterDataType();
1588
1589
0
    const int nScanlineBytes =
1590
0
        nBandCount * poDS->GetRasterXSize() * GDALGetDataTypeSizeBytes(eType);
1591
1592
0
    int nYChunkSize = 0;
1593
0
    if (!(pszYChunkSize && ((nYChunkSize = atoi(pszYChunkSize))) != 0))
1594
0
    {
1595
0
        const GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes;
1596
0
        nYChunkSize = static_cast<int>(
1597
0
            std::min<GIntBig>(nYChunkSize64, std::numeric_limits<int>::max()));
1598
0
    }
1599
1600
0
    if (nYChunkSize < 1)
1601
0
        nYChunkSize = 1;
1602
0
    if (nYChunkSize > poDS->GetRasterYSize())
1603
0
        nYChunkSize = poDS->GetRasterYSize();
1604
1605
0
    CPLDebug("GDAL", "Rasterizer operating on %d swaths of %d scanlines.",
1606
0
             DIV_ROUND_UP(poDS->GetRasterYSize(), nYChunkSize), nYChunkSize);
1607
0
    unsigned char *pabyChunkBuf = static_cast<unsigned char *>(
1608
0
        VSI_MALLOC2_VERBOSE(nYChunkSize, nScanlineBytes));
1609
0
    if (pabyChunkBuf == nullptr)
1610
0
    {
1611
0
        return CE_Failure;
1612
0
    }
1613
1614
    /* -------------------------------------------------------------------- */
1615
    /*      Read the image once for all layers if user requested to render  */
1616
    /*      the whole raster in single chunk.                               */
1617
    /* -------------------------------------------------------------------- */
1618
0
    if (nYChunkSize == poDS->GetRasterYSize())
1619
0
    {
1620
0
        if (poDS->RasterIO(GF_Read, 0, 0, poDS->GetRasterXSize(), nYChunkSize,
1621
0
                           pabyChunkBuf, poDS->GetRasterXSize(), nYChunkSize,
1622
0
                           eType, nBandCount, panBandList, 0, 0, 0,
1623
0
                           nullptr) != CE_None)
1624
0
        {
1625
0
            CPLFree(pabyChunkBuf);
1626
0
            return CE_Failure;
1627
0
        }
1628
0
    }
1629
1630
    /* ==================================================================== */
1631
    /*      Read the specified layers transforming and rasterizing          */
1632
    /*      geometries.                                                     */
1633
    /* ==================================================================== */
1634
0
    CPLErr eErr = CE_None;
1635
0
    const char *pszBurnAttribute = CSLFetchNameValue(papszOptions, "ATTRIBUTE");
1636
1637
0
    pfnProgress(0.0, nullptr, pProgressArg);
1638
1639
0
    for (int iLayer = 0; iLayer < nLayerCount; iLayer++)
1640
0
    {
1641
0
        OGRLayer *poLayer = reinterpret_cast<OGRLayer *>(pahLayers[iLayer]);
1642
1643
0
        if (!poLayer)
1644
0
        {
1645
0
            CPLError(CE_Warning, CPLE_AppDefined,
1646
0
                     "Layer element number %d is NULL, skipping.", iLayer);
1647
0
            continue;
1648
0
        }
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
0
        if (poLayer->GetFeatureCount(FALSE) == 0)
1658
0
            continue;
1659
1660
0
        int iBurnField = -1;
1661
0
        double *padfBurnValues = nullptr;
1662
1663
0
        if (pszBurnAttribute)
1664
0
        {
1665
0
            iBurnField =
1666
0
                poLayer->GetLayerDefn()->GetFieldIndex(pszBurnAttribute);
1667
0
            if (iBurnField == -1)
1668
0
            {
1669
0
                CPLError(CE_Warning, CPLE_AppDefined,
1670
0
                         "Failed to find field %s on layer %s, skipping.",
1671
0
                         pszBurnAttribute, poLayer->GetLayerDefn()->GetName());
1672
0
                continue;
1673
0
            }
1674
0
        }
1675
0
        else
1676
0
        {
1677
0
            padfBurnValues = padfLayerBurnValues + iLayer * nBandCount;
1678
0
        }
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
0
        bool bNeedToFreeTransformer = false;
1688
1689
0
        if (pfnTransformer == nullptr)
1690
0
        {
1691
0
            char *pszProjection = nullptr;
1692
0
            bNeedToFreeTransformer = true;
1693
1694
0
            const OGRSpatialReference *poSRS = poLayer->GetSpatialRef();
1695
0
            if (!poSRS)
1696
0
            {
1697
0
                if (poDS->GetSpatialRef() != nullptr ||
1698
0
                    poDS->GetGCPSpatialRef() != nullptr ||
1699
0
                    poDS->GetMetadata(GDAL_MDD_RPC) != nullptr)
1700
0
                {
1701
0
                    CPLError(
1702
0
                        CE_Warning, CPLE_AppDefined,
1703
0
                        "Failed to fetch spatial reference on layer %s "
1704
0
                        "to build transformer, assuming matching coordinate "
1705
0
                        "systems.",
1706
0
                        poLayer->GetLayerDefn()->GetName());
1707
0
                }
1708
0
            }
1709
0
            else
1710
0
            {
1711
0
                poSRS->exportToWkt(&pszProjection);
1712
0
            }
1713
1714
0
            CPLStringList aosTransformerOptions;
1715
0
            if (pszProjection != nullptr)
1716
0
                aosTransformerOptions.SetNameValue("SRC_SRS", pszProjection);
1717
0
            GDALGeoTransform gt;
1718
0
            if (poDS->GetGeoTransform(gt) != CE_None &&
1719
0
                poDS->GetGCPCount() == 0 &&
1720
0
                poDS->GetMetadata(GDAL_MDD_RPC) == nullptr)
1721
0
            {
1722
0
                aosTransformerOptions.SetNameValue("DST_METHOD",
1723
0
                                                   "NO_GEOTRANSFORM");
1724
0
            }
1725
1726
0
            pTransformArg = GDALCreateGenImgProjTransformer2(
1727
0
                nullptr, hDS, aosTransformerOptions.List());
1728
0
            pfnTransformer = GDALGenImgProjTransform;
1729
1730
0
            CPLFree(pszProjection);
1731
0
            if (pTransformArg == nullptr)
1732
0
            {
1733
0
                CPLFree(pabyChunkBuf);
1734
0
                return CE_Failure;
1735
0
            }
1736
0
        }
1737
1738
0
        poLayer->ResetReading();
1739
1740
        /* --------------------------------------------------------------------
1741
         */
1742
        /*      Loop over image in designated chunks. */
1743
        /* --------------------------------------------------------------------
1744
         */
1745
1746
0
        double *padfAttrValues = static_cast<double *>(
1747
0
            VSI_MALLOC_VERBOSE(sizeof(double) * nBandCount));
1748
0
        if (padfAttrValues == nullptr)
1749
0
            eErr = CE_Failure;
1750
1751
0
        for (int iY = 0; iY < poDS->GetRasterYSize() && eErr == CE_None;
1752
0
             iY += nYChunkSize)
1753
0
        {
1754
0
            int nThisYChunkSize = nYChunkSize;
1755
0
            if (nThisYChunkSize + iY > poDS->GetRasterYSize())
1756
0
                nThisYChunkSize = poDS->GetRasterYSize() - iY;
1757
1758
            // Only re-read image if not a single chunk is being rendered.
1759
0
            if (nYChunkSize < poDS->GetRasterYSize())
1760
0
            {
1761
0
                eErr = poDS->RasterIO(
1762
0
                    GF_Read, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
1763
0
                    pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize,
1764
0
                    eType, nBandCount, panBandList, 0, 0, 0, nullptr);
1765
0
                if (eErr != CE_None)
1766
0
                    break;
1767
0
            }
1768
1769
0
            for (auto &poFeat : poLayer)
1770
0
            {
1771
0
                OGRGeometry *poGeom = poFeat->GetGeometryRef();
1772
1773
0
                if (pszBurnAttribute)
1774
0
                {
1775
0
                    const double dfAttrValue =
1776
0
                        poFeat->GetFieldAsDouble(iBurnField);
1777
0
                    for (int iBand = 0; iBand < nBandCount; iBand++)
1778
0
                        padfAttrValues[iBand] = dfAttrValue;
1779
1780
0
                    padfBurnValues = padfAttrValues;
1781
0
                }
1782
1783
0
                gv_rasterize_one_shape(
1784
0
                    pabyChunkBuf, 0, iY, poDS->GetRasterXSize(),
1785
0
                    nThisYChunkSize, nBandCount, eType, 0, 0, 0, bAllTouched,
1786
0
                    poGeom, GDT_Float64, padfBurnValues, nullptr,
1787
0
                    eBurnValueSource, eMergeAlg, pfnTransformer, pTransformArg);
1788
0
            }
1789
1790
            // Only write image if not a single chunk is being rendered.
1791
0
            if (nYChunkSize < poDS->GetRasterYSize())
1792
0
            {
1793
0
                eErr = poDS->RasterIO(
1794
0
                    GF_Write, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize,
1795
0
                    pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize,
1796
0
                    eType, nBandCount, panBandList, 0, 0, 0, nullptr);
1797
0
            }
1798
1799
0
            poLayer->ResetReading();
1800
1801
0
            if (!pfnProgress((iY + nThisYChunkSize) /
1802
0
                                 static_cast<double>(poDS->GetRasterYSize()),
1803
0
                             "", pProgressArg))
1804
0
            {
1805
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1806
0
                eErr = CE_Failure;
1807
0
            }
1808
0
        }
1809
1810
0
        VSIFree(padfAttrValues);
1811
1812
0
        if (bNeedToFreeTransformer)
1813
0
        {
1814
0
            GDALDestroyTransformer(pTransformArg);
1815
0
            pTransformArg = nullptr;
1816
0
            pfnTransformer = nullptr;
1817
0
        }
1818
0
    }
1819
1820
    /* -------------------------------------------------------------------- */
1821
    /*      Write out the image once for all layers if user requested       */
1822
    /*      to render the whole raster in single chunk.                     */
1823
    /* -------------------------------------------------------------------- */
1824
0
    if (eErr == CE_None && nYChunkSize == poDS->GetRasterYSize())
1825
0
    {
1826
0
        eErr =
1827
0
            poDS->RasterIO(GF_Write, 0, 0, poDS->GetRasterXSize(), nYChunkSize,
1828
0
                           pabyChunkBuf, poDS->GetRasterXSize(), nYChunkSize,
1829
0
                           eType, nBandCount, panBandList, 0, 0, 0, nullptr);
1830
0
    }
1831
1832
    /* -------------------------------------------------------------------- */
1833
    /*      cleanup                                                         */
1834
    /* -------------------------------------------------------------------- */
1835
0
    VSIFree(pabyChunkBuf);
1836
1837
0
    return eErr;
1838
0
}
1839
1840
/************************************************************************/
1841
/*                       GDALRasterizeLayersBuf()                       */
1842
/************************************************************************/
1843
1844
/**
1845
 * Burn geometries from the specified list of layer into raster.
1846
 *
1847
 * Rasterize all the geometric objects from a list of layers into supplied
1848
 * raster buffer.  The layers are passed as an array of OGRLayerH handlers.
1849
 *
1850
 * If the geometries are in the georeferenced coordinates of the raster
1851
 * dataset, then the pfnTransform may be passed in NULL and one will be
1852
 * derived internally from the geotransform of the dataset.  The transform
1853
 * needs to transform the geometry locations into pixel/line coordinates
1854
 * of the target raster.
1855
 *
1856
 * The output raster may be of any GDAL supported datatype(non complex).
1857
 *
1858
 * @param pData pointer to the output data array.
1859
 *
1860
 * @param nBufXSize width of the output data array in pixels.
1861
 *
1862
 * @param nBufYSize height of the output data array in pixels.
1863
 *
1864
 * @param eBufType data type of the output data array.
1865
 *
1866
 * @param nPixelSpace The byte offset from the start of one pixel value in
1867
 * pData to the start of the next pixel value within a scanline.  If defaulted
1868
 * (0) the size of the datatype eBufType is used.
1869
 *
1870
 * @param nLineSpace The byte offset from the start of one scanline in
1871
 * pData to the start of the next.  If defaulted the size of the datatype
1872
 * eBufType * nBufXSize is used.
1873
 *
1874
 * @param nLayerCount the number of layers being passed in pahLayers array.
1875
 *
1876
 * @param pahLayers the array of layers to burn in.
1877
 *
1878
 * @param pszDstProjection WKT defining the coordinate system of the target
1879
 * raster.
1880
 *
1881
 * @param padfDstGeoTransform geotransformation matrix of the target raster.
1882
 *
1883
 * @param pfnTransformer transformation to apply to geometries to put into
1884
 * pixel/line coordinates on raster.  If NULL a geotransform based one will
1885
 * be created internally.
1886
 *
1887
 * @param pTransformArg callback data for transformer.
1888
 *
1889
 * @param dfBurnValue the value to burn into the raster.
1890
 *
1891
 * @param papszOptions special options controlling rasterization:
1892
 * <ul>
1893
 * <li>"ATTRIBUTE": Identifies an attribute field on the features to be
1894
 * used for a burn in value. The value will be burned into all output
1895
 * bands. If specified, padfLayerBurnValues will not be used and can be a NULL
1896
 * pointer.</li>
1897
 * <li>"ALL_TOUCHED": May be set to TRUE to set all pixels touched
1898
 * by the line or polygons, not just those whose center is within the polygon
1899
 * (behavior is unspecified when the polygon is just touching the pixel center)
1900
 * or that are selected by Brezenham's line algorithm.  Defaults to FALSE.</li>
1901
 * <li>"BURN_VALUE_FROM": May be set to "Z" to use
1902
 * the Z values of the geometries. dfBurnValue or the attribute field value is
1903
 * added to this before burning. In default case dfBurnValue is burned as it
1904
 * is. This is implemented properly only for points and lines for now. Polygons
1905
 * will be burned using the Z value from the first point. The M value may
1906
 * be supported in the future.</li>
1907
 * <li>"MERGE_ALG": May be REPLACE (the default) or ADD.  REPLACE
1908
 * results in overwriting of value, while ADD adds the new value to the
1909
 * existing raster, suitable for heatmaps for instance.</li>
1910
 * </ul>
1911
 *
1912
 * @param pfnProgress the progress function to report completion.
1913
 *
1914
 * @param pProgressArg callback data for progress function.
1915
 *
1916
 *
1917
 * @return CE_None on success or CE_Failure on error.
1918
 */
1919
1920
CPLErr GDALRasterizeLayersBuf(
1921
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1922
    int nPixelSpace, int nLineSpace, int nLayerCount, OGRLayerH *pahLayers,
1923
    const char *pszDstProjection, double *padfDstGeoTransform,
1924
    GDALTransformerFunc pfnTransformer, void *pTransformArg, double dfBurnValue,
1925
    char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressArg)
1926
1927
0
{
1928
    /* -------------------------------------------------------------------- */
1929
    /*           check eType, Avoid not supporting data types               */
1930
    /* -------------------------------------------------------------------- */
1931
0
    if (GDALDataTypeIsComplex(eBufType) || eBufType <= GDT_Unknown ||
1932
0
        eBufType >= GDT_TypeCount)
1933
0
    {
1934
0
        CPLError(CE_Failure, CPLE_NotSupported,
1935
0
                 "GDALRasterizeLayersBuf(): unsupported data type of eBufType");
1936
0
        return CE_Failure;
1937
0
    }
1938
1939
    /* -------------------------------------------------------------------- */
1940
    /*      If pixel and line spaceing are defaulted assign reasonable      */
1941
    /*      value assuming a packed buffer.                                 */
1942
    /* -------------------------------------------------------------------- */
1943
0
    int nTypeSizeBytes = GDALGetDataTypeSizeBytes(eBufType);
1944
0
    if (nPixelSpace == 0)
1945
0
    {
1946
0
        nPixelSpace = nTypeSizeBytes;
1947
0
    }
1948
0
    if (nPixelSpace < nTypeSizeBytes)
1949
0
    {
1950
0
        CPLError(CE_Failure, CPLE_NotSupported,
1951
0
                 "GDALRasterizeLayersBuf(): unsupported value of nPixelSpace");
1952
0
        return CE_Failure;
1953
0
    }
1954
1955
0
    if (nLineSpace == 0)
1956
0
    {
1957
0
        nLineSpace = nPixelSpace * nBufXSize;
1958
0
    }
1959
0
    if (nLineSpace < nPixelSpace * nBufXSize)
1960
0
    {
1961
0
        CPLError(CE_Failure, CPLE_NotSupported,
1962
0
                 "GDALRasterizeLayersBuf(): unsupported value of nLineSpace");
1963
0
        return CE_Failure;
1964
0
    }
1965
1966
0
    if (pfnProgress == nullptr)
1967
0
        pfnProgress = GDALDummyProgress;
1968
1969
    /* -------------------------------------------------------------------- */
1970
    /*      Do some rudimentary arg checking.                               */
1971
    /* -------------------------------------------------------------------- */
1972
0
    if (nLayerCount == 0)
1973
0
        return CE_None;
1974
1975
    /* -------------------------------------------------------------------- */
1976
    /*      Options                                                         */
1977
    /* -------------------------------------------------------------------- */
1978
0
    int bAllTouched = FALSE;
1979
0
    GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue;
1980
0
    GDALRasterMergeAlg eMergeAlg = GRMA_Replace;
1981
0
    if (GDALRasterizeOptions(papszOptions, &bAllTouched, &eBurnValueSource,
1982
0
                             &eMergeAlg, nullptr) == CE_Failure)
1983
0
    {
1984
0
        return CE_Failure;
1985
0
    }
1986
1987
    /* ==================================================================== */
1988
    /*      Read the specified layers transforming and rasterizing          */
1989
    /*      geometries.                                                     */
1990
    /* ==================================================================== */
1991
0
    CPLErr eErr = CE_None;
1992
0
    const char *pszBurnAttribute = CSLFetchNameValue(papszOptions, "ATTRIBUTE");
1993
1994
0
    pfnProgress(0.0, nullptr, pProgressArg);
1995
1996
0
    for (int iLayer = 0; iLayer < nLayerCount; iLayer++)
1997
0
    {
1998
0
        OGRLayer *poLayer = reinterpret_cast<OGRLayer *>(pahLayers[iLayer]);
1999
2000
0
        if (!poLayer)
2001
0
        {
2002
0
            CPLError(CE_Warning, CPLE_AppDefined,
2003
0
                     "Layer element number %d is NULL, skipping.", iLayer);
2004
0
            continue;
2005
0
        }
2006
2007
        /* --------------------------------------------------------------------
2008
         */
2009
        /*      If the layer does not contain any features just skip it. */
2010
        /*      Do not force the feature count, so if driver doesn't know */
2011
        /*      exact number of features, go down the normal way. */
2012
        /* --------------------------------------------------------------------
2013
         */
2014
0
        if (poLayer->GetFeatureCount(FALSE) == 0)
2015
0
            continue;
2016
2017
0
        int iBurnField = -1;
2018
0
        if (pszBurnAttribute)
2019
0
        {
2020
0
            iBurnField =
2021
0
                poLayer->GetLayerDefn()->GetFieldIndex(pszBurnAttribute);
2022
0
            if (iBurnField == -1)
2023
0
            {
2024
0
                CPLError(CE_Warning, CPLE_AppDefined,
2025
0
                         "Failed to find field %s on layer %s, skipping.",
2026
0
                         pszBurnAttribute, poLayer->GetLayerDefn()->GetName());
2027
0
                continue;
2028
0
            }
2029
0
        }
2030
2031
        /* --------------------------------------------------------------------
2032
         */
2033
        /*      If we have no transformer, create the one from input file */
2034
        /*      projection. Note that each layer can be georefernced */
2035
        /*      separately. */
2036
        /* --------------------------------------------------------------------
2037
         */
2038
0
        bool bNeedToFreeTransformer = false;
2039
2040
0
        if (pfnTransformer == nullptr)
2041
0
        {
2042
0
            char *pszProjection = nullptr;
2043
0
            bNeedToFreeTransformer = true;
2044
2045
0
            const OGRSpatialReference *poSRS = poLayer->GetSpatialRef();
2046
0
            if (!poSRS)
2047
0
            {
2048
0
                CPLError(CE_Warning, CPLE_AppDefined,
2049
0
                         "Failed to fetch spatial reference on layer %s "
2050
0
                         "to build transformer, assuming matching coordinate "
2051
0
                         "systems.",
2052
0
                         poLayer->GetLayerDefn()->GetName());
2053
0
            }
2054
0
            else
2055
0
            {
2056
0
                poSRS->exportToWkt(&pszProjection);
2057
0
            }
2058
2059
0
            pTransformArg = GDALCreateGenImgProjTransformer3(
2060
0
                pszProjection, nullptr, pszDstProjection, padfDstGeoTransform);
2061
0
            pfnTransformer = GDALGenImgProjTransform;
2062
2063
0
            CPLFree(pszProjection);
2064
0
        }
2065
2066
0
        for (auto &poFeat : poLayer)
2067
0
        {
2068
0
            OGRGeometry *poGeom = poFeat->GetGeometryRef();
2069
2070
0
            if (pszBurnAttribute)
2071
0
                dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
2072
2073
0
            gv_rasterize_one_shape(
2074
0
                static_cast<unsigned char *>(pData), 0, 0, nBufXSize, nBufYSize,
2075
0
                1, eBufType, nPixelSpace, nLineSpace, 0, bAllTouched, poGeom,
2076
0
                GDT_Float64, &dfBurnValue, nullptr, eBurnValueSource, eMergeAlg,
2077
0
                pfnTransformer, pTransformArg);
2078
0
        }
2079
2080
0
        poLayer->ResetReading();
2081
2082
0
        if (!pfnProgress(1, "", pProgressArg))
2083
0
        {
2084
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
2085
0
            eErr = CE_Failure;
2086
0
        }
2087
2088
0
        if (bNeedToFreeTransformer)
2089
0
        {
2090
0
            GDALDestroyTransformer(pTransformArg);
2091
0
            pTransformArg = nullptr;
2092
0
            pfnTransformer = nullptr;
2093
0
        }
2094
0
    }
2095
2096
0
    return eErr;
2097
0
}