Coverage Report

Created: 2025-06-13 06:29

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