Coverage Report

Created: 2025-12-31 08:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/hfa/hfadataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Name:     hfadataset.cpp
4
 * Project:  Erdas Imagine Driver
5
 * Purpose:  Main driver for Erdas Imagine format.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1999, Frank Warmerdam
10
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "cpl_port.h"
16
#include "hfadataset.h"
17
#include "hfa_p.h"
18
19
#include <cassert>
20
#include <climits>
21
#include <cmath>
22
#include <cstddef>
23
#include <cstdio>
24
#include <cstdlib>
25
#include <cstring>
26
#include <algorithm>
27
#include <limits>
28
#include <memory>
29
#include <string>
30
#include <vector>
31
32
#include "cpl_conv.h"
33
#include "cpl_error.h"
34
#include "cpl_minixml.h"
35
#include "cpl_progress.h"
36
#include "cpl_string.h"
37
#include "cpl_vsi.h"
38
#include "gdal.h"
39
#include "gdal_frmts.h"
40
#include "gdal_pam.h"
41
#include "gdal_priv.h"
42
#include "gdal_rat.h"
43
#include "hfa.h"
44
#include "ogr_core.h"
45
#include "ogr_spatialref.h"
46
#include "ogr_srs_api.h"
47
48
constexpr double D2R = M_PI / 180.0;
49
50
constexpr double ARCSEC2RAD = M_PI / 648000.0;
51
52
int WritePeStringIfNeeded(const OGRSpatialReference *poSRS, HFAHandle hHFA);
53
void ClearSR(HFAHandle hHFA);
54
55
/************************************************************************/
56
/*                     HFARasterAttributeTable()                        */
57
/************************************************************************/
58
59
HFARasterAttributeTable::HFARasterAttributeTable(HFARasterBand *poBand,
60
                                                 const char *pszName)
61
28.9k
    : hHFA(poBand->hHFA),
62
28.9k
      poDT(poBand->hHFA->papoBand[poBand->nBand - 1]->poNode->GetNamedChild(
63
28.9k
          pszName)),
64
28.9k
      osName(pszName), nBand(poBand->nBand), eAccess(poBand->GetAccess()),
65
28.9k
      nRows(0), bLinearBinning(false), dfRow0Min(0.0), dfBinSize(0.0),
66
28.9k
      eTableType(GRTT_THEMATIC)
67
28.9k
{
68
28.9k
    if (poDT != nullptr)
69
1.16k
    {
70
1.16k
        nRows = std::max(0, poDT->GetIntField("numRows"));
71
72
        // Scan under table for columns.
73
5.64k
        for (HFAEntry *poDTChild = poDT->GetChild(); poDTChild != nullptr;
74
4.47k
             poDTChild = poDTChild->GetNext())
75
4.47k
        {
76
4.47k
            if (EQUAL(poDTChild->GetType(), "Edsc_BinFunction"))
77
428
            {
78
428
                const double dfMax = poDTChild->GetDoubleField("maxLimit");
79
428
                const double dfMin = poDTChild->GetDoubleField("minLimit");
80
428
                const int nBinCount = poDTChild->GetIntField("numBins");
81
82
428
                if (nBinCount == nRows && dfMax != dfMin && nBinCount > 1)
83
331
                {
84
                    // Can't call SetLinearBinning since it will re-write
85
                    // which we might not have permission to do.
86
331
                    bLinearBinning = true;
87
331
                    dfRow0Min = dfMin;
88
331
                    dfBinSize = (dfMax - dfMin) / (nBinCount - 1);
89
331
                }
90
428
            }
91
92
4.47k
            if (EQUAL(poDTChild->GetType(), "Edsc_BinFunction840"))
93
124
            {
94
124
                const char *pszValue =
95
124
                    poDTChild->GetStringField("binFunction.type.string");
96
124
                if (pszValue && EQUAL(pszValue, "BFUnique"))
97
95
                {
98
95
                    AddColumn("BinValues", GFT_Real, GFU_MinMax, 0, 0,
99
95
                              poDTChild, true);
100
95
                }
101
124
            }
102
103
4.47k
            if (!EQUAL(poDTChild->GetType(), "Edsc_Column"))
104
3.24k
                continue;
105
106
1.23k
            const int nOffset = poDTChild->GetIntField("columnDataPtr");
107
1.23k
            const char *pszType = poDTChild->GetStringField("dataType");
108
1.23k
            GDALRATFieldUsage eUsage = GFU_Generic;
109
1.23k
            bool bConvertColors = false;
110
111
1.23k
            if (pszType == nullptr || nOffset == 0)
112
226
                continue;
113
114
1.00k
            GDALRATFieldType eType;
115
1.00k
            if (EQUAL(pszType, "real"))
116
701
                eType = GFT_Real;
117
305
            else if (EQUAL(pszType, "string"))
118
33
                eType = GFT_String;
119
272
            else if (STARTS_WITH_CI(pszType, "int"))
120
96
                eType = GFT_Integer;
121
176
            else
122
176
                continue;
123
124
830
            if (EQUAL(poDTChild->GetName(), "Histogram"))
125
465
                eUsage = GFU_PixelCount;
126
365
            else if (EQUAL(poDTChild->GetName(), "Red"))
127
104
            {
128
104
                eUsage = GFU_Red;
129
                // Treat color columns as ints regardless
130
                // of how they are stored.
131
104
                bConvertColors = eType == GFT_Real;
132
104
                eType = GFT_Integer;
133
104
            }
134
261
            else if (EQUAL(poDTChild->GetName(), "Green"))
135
54
            {
136
54
                eUsage = GFU_Green;
137
54
                bConvertColors = eType == GFT_Real;
138
54
                eType = GFT_Integer;
139
54
            }
140
207
            else if (EQUAL(poDTChild->GetName(), "Blue"))
141
39
            {
142
39
                eUsage = GFU_Blue;
143
39
                bConvertColors = eType == GFT_Real;
144
39
                eType = GFT_Integer;
145
39
            }
146
168
            else if (EQUAL(poDTChild->GetName(), "Opacity"))
147
45
            {
148
45
                eUsage = GFU_Alpha;
149
45
                bConvertColors = eType == GFT_Real;
150
45
                eType = GFT_Integer;
151
45
            }
152
123
            else if (EQUAL(poDTChild->GetName(), "Class_Names"))
153
0
                eUsage = GFU_Name;
154
155
830
            if (eType == GFT_Real)
156
485
            {
157
485
                AddColumn(poDTChild->GetName(), GFT_Real, eUsage, nOffset,
158
485
                          sizeof(double), poDTChild);
159
485
            }
160
345
            else if (eType == GFT_String)
161
28
            {
162
28
                int nMaxNumChars = poDTChild->GetIntField("maxNumChars");
163
28
                if (nMaxNumChars <= 0)
164
6
                {
165
6
                    CPLError(CE_Failure, CPLE_AppDefined,
166
6
                             "Invalid nMaxNumChars = %d for column %s",
167
6
                             nMaxNumChars, poDTChild->GetName());
168
6
                    nMaxNumChars = 1;
169
6
                }
170
28
                AddColumn(poDTChild->GetName(), GFT_String, eUsage, nOffset,
171
28
                          nMaxNumChars, poDTChild);
172
28
            }
173
317
            else if (eType == GFT_Integer)
174
317
            {
175
317
                int nSize = sizeof(GInt32);
176
317
                if (bConvertColors)
177
216
                    nSize = sizeof(double);
178
317
                AddColumn(poDTChild->GetName(), GFT_Integer, eUsage, nOffset,
179
317
                          nSize, poDTChild, false, bConvertColors);
180
317
            }
181
830
        }
182
1.16k
    }
183
28.9k
}
184
185
/************************************************************************/
186
/*                    ~HFARasterAttributeTable()                        */
187
/************************************************************************/
188
189
HFARasterAttributeTable::~HFARasterAttributeTable()
190
28.9k
{
191
28.9k
}
192
193
/************************************************************************/
194
/*                              Clone()                                 */
195
/************************************************************************/
196
197
GDALRasterAttributeTable *HFARasterAttributeTable::Clone() const
198
0
{
199
0
    if (nRows > 0 && GetColumnCount() > RAT_MAX_ELEM_FOR_CLONE / nRows)
200
0
        return nullptr;
201
202
0
    GDALDefaultRasterAttributeTable *poRAT =
203
0
        new GDALDefaultRasterAttributeTable();
204
205
0
    for (int iCol = 0; iCol < static_cast<int>(aoFields.size()); iCol++)
206
0
    {
207
0
        poRAT->CreateColumn(aoFields[iCol].sName, aoFields[iCol].eType,
208
0
                            aoFields[iCol].eUsage);
209
0
        poRAT->SetRowCount(nRows);
210
211
0
        if (aoFields[iCol].eType == GFT_Integer)
212
0
        {
213
0
            int *panColData =
214
0
                static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nRows));
215
0
            if (panColData == nullptr)
216
0
            {
217
0
                delete poRAT;
218
0
                return nullptr;
219
0
            }
220
221
0
            if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
222
0
                    GF_Read, iCol, 0, nRows, panColData) != CE_None)
223
0
            {
224
0
                CPLFree(panColData);
225
0
                delete poRAT;
226
0
                return nullptr;
227
0
            }
228
229
0
            for (int iRow = 0; iRow < nRows; iRow++)
230
0
            {
231
0
                poRAT->SetValue(iRow, iCol, panColData[iRow]);
232
0
            }
233
0
            CPLFree(panColData);
234
0
        }
235
0
        if (aoFields[iCol].eType == GFT_Real)
236
0
        {
237
0
            double *padfColData = static_cast<double *>(
238
0
                VSI_MALLOC2_VERBOSE(sizeof(double), nRows));
239
0
            if (padfColData == nullptr)
240
0
            {
241
0
                delete poRAT;
242
0
                return nullptr;
243
0
            }
244
245
0
            if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
246
0
                    GF_Read, iCol, 0, nRows, padfColData) != CE_None)
247
0
            {
248
0
                CPLFree(padfColData);
249
0
                delete poRAT;
250
0
                return nullptr;
251
0
            }
252
253
0
            for (int iRow = 0; iRow < nRows; iRow++)
254
0
            {
255
0
                poRAT->SetValue(iRow, iCol, padfColData[iRow]);
256
0
            }
257
0
            CPLFree(padfColData);
258
0
        }
259
0
        if (aoFields[iCol].eType == GFT_String)
260
0
        {
261
0
            char **papszColData = static_cast<char **>(
262
0
                VSI_MALLOC2_VERBOSE(sizeof(char *), nRows));
263
0
            if (papszColData == nullptr)
264
0
            {
265
0
                delete poRAT;
266
0
                return nullptr;
267
0
            }
268
269
0
            if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
270
0
                    GF_Read, iCol, 0, nRows, papszColData) != CE_None)
271
0
            {
272
0
                CPLFree(papszColData);
273
0
                delete poRAT;
274
0
                return nullptr;
275
0
            }
276
277
0
            for (int iRow = 0; iRow < nRows; iRow++)
278
0
            {
279
0
                poRAT->SetValue(iRow, iCol, papszColData[iRow]);
280
0
                CPLFree(papszColData[iRow]);
281
0
            }
282
0
            CPLFree(papszColData);
283
0
        }
284
0
    }
285
286
0
    if (bLinearBinning)
287
0
        poRAT->SetLinearBinning(dfRow0Min, dfBinSize);
288
289
0
    poRAT->SetTableType(this->GetTableType());
290
291
0
    return poRAT;
292
0
}
293
294
/************************************************************************/
295
/*                          GetColumnCount()                            */
296
/************************************************************************/
297
298
int HFARasterAttributeTable::GetColumnCount() const
299
0
{
300
0
    return static_cast<int>(aoFields.size());
301
0
}
302
303
/************************************************************************/
304
/*                          GetNameOfCol()                              */
305
/************************************************************************/
306
307
const char *HFARasterAttributeTable::GetNameOfCol(int nCol) const
308
0
{
309
0
    if (nCol < 0 || nCol >= static_cast<int>(aoFields.size()))
310
0
        return nullptr;
311
312
0
    return aoFields[nCol].sName;
313
0
}
314
315
/************************************************************************/
316
/*                          GetUsageOfCol()                             */
317
/************************************************************************/
318
319
GDALRATFieldUsage HFARasterAttributeTable::GetUsageOfCol(int nCol) const
320
0
{
321
0
    if (nCol < 0 || nCol >= static_cast<int>(aoFields.size()))
322
0
        return GFU_Generic;
323
324
0
    return aoFields[nCol].eUsage;
325
0
}
326
327
/************************************************************************/
328
/*                          GetTypeOfCol()                              */
329
/************************************************************************/
330
331
GDALRATFieldType HFARasterAttributeTable::GetTypeOfCol(int nCol) const
332
0
{
333
0
    if (nCol < 0 || nCol >= static_cast<int>(aoFields.size()))
334
0
        return GFT_Integer;
335
336
0
    return aoFields[nCol].eType;
337
0
}
338
339
/************************************************************************/
340
/*                          GetColOfUsage()                             */
341
/************************************************************************/
342
343
int HFARasterAttributeTable::GetColOfUsage(GDALRATFieldUsage eUsage) const
344
0
{
345
0
    for (unsigned int i = 0; i < aoFields.size(); i++)
346
0
    {
347
0
        if (aoFields[i].eUsage == eUsage)
348
0
            return i;
349
0
    }
350
351
0
    return -1;
352
0
}
353
354
/************************************************************************/
355
/*                          GetRowCount()                               */
356
/************************************************************************/
357
358
int HFARasterAttributeTable::GetRowCount() const
359
19
{
360
19
    return nRows;
361
19
}
362
363
/************************************************************************/
364
/*                      GetValueAsString()                              */
365
/************************************************************************/
366
367
const char *HFARasterAttributeTable::GetValueAsString(int iRow,
368
                                                      int iField) const
369
0
{
370
    // Let ValuesIO do the work.
371
0
    char *apszStrList[1] = {nullptr};
372
0
    if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
373
0
            GF_Read, iField, iRow, 1, apszStrList) != CE_None)
374
0
    {
375
0
        return "";
376
0
    }
377
378
0
    const_cast<HFARasterAttributeTable *>(this)->osWorkingResult =
379
0
        apszStrList[0];
380
0
    CPLFree(apszStrList[0]);
381
382
0
    return osWorkingResult;
383
0
}
384
385
/************************************************************************/
386
/*                        GetValueAsInt()                               */
387
/************************************************************************/
388
389
int HFARasterAttributeTable::GetValueAsInt(int iRow, int iField) const
390
0
{
391
    // Let ValuesIO do the work.
392
0
    int nValue = 0;
393
0
    if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
394
0
            GF_Read, iField, iRow, 1, &nValue) != CE_None)
395
0
    {
396
0
        return 0;
397
0
    }
398
399
0
    return nValue;
400
0
}
401
402
/************************************************************************/
403
/*                      GetValueAsDouble()                              */
404
/************************************************************************/
405
406
double HFARasterAttributeTable::GetValueAsDouble(int iRow, int iField) const
407
0
{
408
    // Let ValuesIO do the work.
409
0
    double dfValue = 0.0;
410
0
    if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
411
0
            GF_Read, iField, iRow, 1, &dfValue) != CE_None)
412
0
    {
413
0
        return 0.0;
414
0
    }
415
416
0
    return dfValue;
417
0
}
418
419
/************************************************************************/
420
/*                        GetValueAsBoolean()                           */
421
/************************************************************************/
422
423
bool HFARasterAttributeTable::GetValueAsBoolean(int iRow, int iField) const
424
0
{
425
    // Let ValuesIO do the work.
426
0
    bool bValue = false;
427
0
    if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
428
0
            GF_Read, iField, iRow, 1, &bValue) != CE_None)
429
0
    {
430
0
        return false;
431
0
    }
432
433
0
    return bValue;
434
0
}
435
436
/************************************************************************/
437
/*                        GetValueAsDateTime()                          */
438
/************************************************************************/
439
440
GDALRATDateTime HFARasterAttributeTable::GetValueAsDateTime(int iRow,
441
                                                            int iField) const
442
0
{
443
    // Let ValuesIO do the work.
444
0
    GDALRATDateTime dt;
445
0
    const_cast<HFARasterAttributeTable *>(this)->ValuesIO(GF_Read, iField, iRow,
446
0
                                                          1, &dt);
447
0
    return dt;
448
0
}
449
450
/************************************************************************/
451
/*                        GetValueAsWKBGeometry()                       */
452
/************************************************************************/
453
454
const GByte *
455
HFARasterAttributeTable::GetValueAsWKBGeometry(int iRow, int iField,
456
                                               size_t &nWKBSize) const
457
0
{
458
    // Let ValuesIO do the work.
459
0
    GByte *pabyWKB = nullptr;
460
0
    nWKBSize = 0;
461
0
    const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
462
0
        GF_Read, iField, iRow, 1, &pabyWKB, &nWKBSize);
463
0
    if (pabyWKB)
464
0
        m_abyWKB.assign(pabyWKB, pabyWKB + nWKBSize);
465
0
    CPLFree(pabyWKB);
466
0
    return pabyWKB ? m_abyWKB.data() : nullptr;
467
0
}
468
469
/************************************************************************/
470
/*                          SetValue()                                  */
471
/************************************************************************/
472
473
CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField,
474
                                         const char *pszValue)
475
0
{
476
    // Let ValuesIO do the work.
477
0
    char *apszValues[1] = {const_cast<char *>(pszValue)};
478
0
    return ValuesIO(GF_Write, iField, iRow, 1, apszValues);
479
0
}
480
481
/************************************************************************/
482
/*                          SetValue()                                  */
483
/************************************************************************/
484
485
CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField, double dfValue)
486
0
{
487
    // Let ValuesIO do the work.
488
0
    return ValuesIO(GF_Write, iField, iRow, 1, &dfValue);
489
0
}
490
491
/************************************************************************/
492
/*                          SetValue()                                  */
493
/************************************************************************/
494
495
CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField, int nValue)
496
0
{
497
    // Let ValuesIO do the work.
498
0
    return ValuesIO(GF_Write, iField, iRow, 1, &nValue);
499
0
}
500
501
/************************************************************************/
502
/*                          SetValue()                                  */
503
/************************************************************************/
504
505
CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField, bool bValue)
506
0
{
507
    // Let ValuesIO do the work.
508
0
    return ValuesIO(GF_Write, iField, iRow, 1, &bValue);
509
0
}
510
511
/************************************************************************/
512
/*                          SetValue()                                  */
513
/************************************************************************/
514
515
CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField,
516
                                         const GDALRATDateTime &sDateTime)
517
0
{
518
    // Let ValuesIO do the work.
519
0
    return ValuesIO(GF_Write, iField, iRow, 1,
520
0
                    const_cast<GDALRATDateTime *>(&sDateTime));
521
0
}
522
523
/************************************************************************/
524
/*                          SetValue()                                  */
525
/************************************************************************/
526
527
CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField,
528
                                         const void *pabyWKB, size_t nWKBSize)
529
0
{
530
    // Let ValuesIO do the work.
531
0
    const GByte **ppabyWKB = reinterpret_cast<const GByte **>(&pabyWKB);
532
0
    return ValuesIO(GF_Write, iField, iRow, 1, const_cast<GByte **>(ppabyWKB),
533
0
                    &nWKBSize);
534
0
}
535
536
/************************************************************************/
537
/*                          ValuesIO()                                  */
538
/************************************************************************/
539
540
CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
541
                                         int iStartRow, int iLength,
542
                                         double *pdfData)
543
0
{
544
0
    if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
545
0
    {
546
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
547
0
                 "Dataset not open in update mode");
548
0
        return CE_Failure;
549
0
    }
550
551
0
    if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
552
0
    {
553
0
        CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
554
0
                 iField);
555
556
0
        return CE_Failure;
557
0
    }
558
559
0
    if (iStartRow < 0 || iLength >= INT_MAX - iStartRow ||
560
0
        (iStartRow + iLength) > nRows)
561
0
    {
562
0
        CPLError(CE_Failure, CPLE_AppDefined,
563
0
                 "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
564
0
                 iLength);
565
566
0
        return CE_Failure;
567
0
    }
568
569
0
    if (aoFields[iField].bConvertColors)
570
0
    {
571
        // Convert to/from float color field.
572
0
        int *panColData =
573
0
            static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
574
0
        if (panColData == nullptr)
575
0
        {
576
0
            CPLFree(panColData);
577
0
            return CE_Failure;
578
0
        }
579
580
0
        if (eRWFlag == GF_Write)
581
0
        {
582
0
            for (int i = 0; i < iLength; i++)
583
0
                panColData[i] = static_cast<int>(pdfData[i]);
584
0
        }
585
586
0
        const CPLErr ret =
587
0
            ColorsIO(eRWFlag, iField, iStartRow, iLength, panColData);
588
589
0
        if (eRWFlag == GF_Read)
590
0
        {
591
            // Copy them back to doubles.
592
0
            for (int i = 0; i < iLength; i++)
593
0
                pdfData[i] = panColData[i];
594
0
        }
595
596
0
        CPLFree(panColData);
597
0
        return ret;
598
0
    }
599
600
0
    switch (aoFields[iField].eType)
601
0
    {
602
0
        case GFT_Integer:
603
0
        {
604
            // Allocate space for ints.
605
0
            int *panColData =
606
0
                static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
607
0
            if (panColData == nullptr)
608
0
            {
609
0
                CPLFree(panColData);
610
0
                return CE_Failure;
611
0
            }
612
613
0
            if (eRWFlag == GF_Write)
614
0
            {
615
                // Copy the application supplied doubles to ints.
616
0
                for (int i = 0; i < iLength; i++)
617
0
                    panColData[i] = static_cast<int>(pdfData[i]);
618
0
            }
619
620
            // Do the ValuesIO as ints.
621
0
            const CPLErr eVal =
622
0
                ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData);
623
0
            if (eVal != CE_None)
624
0
            {
625
0
                CPLFree(panColData);
626
0
                return eVal;
627
0
            }
628
629
0
            if (eRWFlag == GF_Read)
630
0
            {
631
                // Copy them back to doubles.
632
0
                for (int i = 0; i < iLength; i++)
633
0
                    pdfData[i] = panColData[i];
634
0
            }
635
636
0
            CPLFree(panColData);
637
0
        }
638
0
        break;
639
0
        case GFT_Real:
640
0
        {
641
0
            if ((eRWFlag == GF_Read) && aoFields[iField].bIsBinValues)
642
0
            {
643
                // Probably could change HFAReadBFUniqueBins to only read needed
644
                // rows.
645
0
                double *padfBinValues = HFAReadBFUniqueBins(
646
0
                    aoFields[iField].poColumn, iStartRow + iLength);
647
0
                if (padfBinValues == nullptr)
648
0
                    return CE_Failure;
649
0
                memcpy(pdfData, &padfBinValues[iStartRow],
650
0
                       sizeof(double) * iLength);
651
0
                CPLFree(padfBinValues);
652
0
            }
653
0
            else
654
0
            {
655
0
                if (VSIFSeekL(hHFA->fp,
656
0
                              aoFields[iField].nDataOffset +
657
0
                                  (static_cast<vsi_l_offset>(iStartRow) *
658
0
                                   aoFields[iField].nElementSize),
659
0
                              SEEK_SET) != 0)
660
0
                {
661
0
                    return CE_Failure;
662
0
                }
663
664
0
                if (eRWFlag == GF_Read)
665
0
                {
666
0
                    if (static_cast<int>(VSIFReadL(pdfData, sizeof(double),
667
0
                                                   iLength, hHFA->fp)) !=
668
0
                        iLength)
669
0
                    {
670
0
                        CPLError(CE_Failure, CPLE_AppDefined,
671
0
                                 "HFARasterAttributeTable::ValuesIO: "
672
0
                                 "Cannot read values");
673
0
                        return CE_Failure;
674
0
                    }
675
#ifdef CPL_MSB
676
                    GDALSwapWords(pdfData, 8, iLength, 8);
677
#endif
678
0
                }
679
0
                else
680
0
                {
681
#ifdef CPL_MSB
682
                    GDALSwapWords(pdfData, 8, iLength, 8);
683
#endif
684
                    // Note: HFAAllocateSpace now called by CreateColumn so
685
                    // space should exist.
686
0
                    if (static_cast<int>(VSIFWriteL(pdfData, sizeof(double),
687
0
                                                    iLength, hHFA->fp)) !=
688
0
                        iLength)
689
0
                    {
690
0
                        CPLError(CE_Failure, CPLE_AppDefined,
691
0
                                 "HFARasterAttributeTable::ValuesIO: "
692
0
                                 "Cannot write values");
693
0
                        return CE_Failure;
694
0
                    }
695
#ifdef CPL_MSB
696
                    // Swap back.
697
                    GDALSwapWords(pdfData, 8, iLength, 8);
698
#endif
699
0
                }
700
0
            }
701
0
        }
702
0
        break;
703
0
        case GFT_String:
704
0
        {
705
            // Allocate space for string pointers.
706
0
            char **papszColData = static_cast<char **>(
707
0
                VSI_MALLOC2_VERBOSE(iLength, sizeof(char *)));
708
0
            if (papszColData == nullptr)
709
0
            {
710
0
                return CE_Failure;
711
0
            }
712
713
0
            if (eRWFlag == GF_Write)
714
0
            {
715
                // Copy the application supplied doubles to strings.
716
0
                for (int i = 0; i < iLength; i++)
717
0
                {
718
0
                    osWorkingResult.Printf("%.16g", pdfData[i]);
719
0
                    papszColData[i] = CPLStrdup(osWorkingResult);
720
0
                }
721
0
            }
722
723
            // Do the ValuesIO as strings.
724
0
            const CPLErr eVal =
725
0
                ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
726
0
            if (eVal != CE_None)
727
0
            {
728
0
                if (eRWFlag == GF_Write)
729
0
                {
730
0
                    for (int i = 0; i < iLength; i++)
731
0
                        CPLFree(papszColData[i]);
732
0
                }
733
0
                CPLFree(papszColData);
734
0
                return eVal;
735
0
            }
736
737
0
            if (eRWFlag == GF_Read)
738
0
            {
739
                // Copy them back to doubles.
740
0
                for (int i = 0; i < iLength; i++)
741
0
                    pdfData[i] = CPLAtof(papszColData[i]);
742
0
            }
743
744
            // Either we allocated them for write, or they were allocated
745
            // by ValuesIO on read.
746
0
            for (int i = 0; i < iLength; i++)
747
0
                CPLFree(papszColData[i]);
748
749
0
            CPLFree(papszColData);
750
0
        }
751
0
        break;
752
0
        case GFT_Boolean:
753
0
        case GFT_DateTime:
754
0
        case GFT_WKBGeometry:
755
0
            CPLAssert(false);
756
0
            break;
757
0
    }
758
759
0
    return CE_None;
760
0
}
761
762
/************************************************************************/
763
/*                          ValuesIO()                                  */
764
/************************************************************************/
765
766
CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
767
                                         int iStartRow, int iLength,
768
                                         int *pnData)
769
0
{
770
0
    if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
771
0
    {
772
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
773
0
                 "Dataset not open in update mode");
774
0
        return CE_Failure;
775
0
    }
776
777
0
    if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
778
0
    {
779
0
        CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
780
0
                 iField);
781
782
0
        return CE_Failure;
783
0
    }
784
785
0
    if (iStartRow < 0 || iLength >= INT_MAX - iStartRow ||
786
0
        (iStartRow + iLength) > nRows)
787
0
    {
788
0
        CPLError(CE_Failure, CPLE_AppDefined,
789
0
                 "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
790
0
                 iLength);
791
792
0
        return CE_Failure;
793
0
    }
794
795
0
    if (aoFields[iField].bConvertColors)
796
0
    {
797
        // Convert to/from float color field.
798
0
        return ColorsIO(eRWFlag, iField, iStartRow, iLength, pnData);
799
0
    }
800
801
0
    switch (aoFields[iField].eType)
802
0
    {
803
0
        case GFT_Integer:
804
0
        {
805
0
            if (VSIFSeekL(hHFA->fp,
806
0
                          aoFields[iField].nDataOffset +
807
0
                              (static_cast<vsi_l_offset>(iStartRow) *
808
0
                               aoFields[iField].nElementSize),
809
0
                          SEEK_SET) != 0)
810
0
            {
811
0
                return CE_Failure;
812
0
            }
813
0
            GInt32 *panColData = static_cast<GInt32 *>(
814
0
                VSI_MALLOC2_VERBOSE(iLength, sizeof(GInt32)));
815
0
            if (panColData == nullptr)
816
0
            {
817
0
                return CE_Failure;
818
0
            }
819
820
0
            if (eRWFlag == GF_Read)
821
0
            {
822
0
                if (static_cast<int>(VSIFReadL(panColData, sizeof(GInt32),
823
0
                                               iLength, hHFA->fp)) != iLength)
824
0
                {
825
0
                    CPLError(CE_Failure, CPLE_AppDefined,
826
0
                             "HFARasterAttributeTable::ValuesIO: "
827
0
                             "Cannot read values");
828
0
                    CPLFree(panColData);
829
0
                    return CE_Failure;
830
0
                }
831
#ifdef CPL_MSB
832
                GDALSwapWords(panColData, 4, iLength, 4);
833
#endif
834
                // Now copy into application buffer. This extra step
835
                // may not be necessary if sizeof(int) == sizeof(GInt32).
836
0
                for (int i = 0; i < iLength; i++)
837
0
                    pnData[i] = panColData[i];
838
0
            }
839
0
            else
840
0
            {
841
                // Copy from application buffer.
842
0
                for (int i = 0; i < iLength; i++)
843
0
                    panColData[i] = pnData[i];
844
845
#ifdef CPL_MSB
846
                GDALSwapWords(panColData, 4, iLength, 4);
847
#endif
848
                // Note: HFAAllocateSpace now called by CreateColumn so space
849
                // should exist.
850
0
                if (static_cast<int>(VSIFWriteL(panColData, sizeof(GInt32),
851
0
                                                iLength, hHFA->fp)) != iLength)
852
0
                {
853
0
                    CPLError(CE_Failure, CPLE_AppDefined,
854
0
                             "HFARasterAttributeTable::ValuesIO: "
855
0
                             "Cannot write values");
856
0
                    CPLFree(panColData);
857
0
                    return CE_Failure;
858
0
                }
859
0
            }
860
0
            CPLFree(panColData);
861
0
        }
862
0
        break;
863
0
        case GFT_Real:
864
0
        {
865
            // Allocate space for doubles.
866
0
            double *padfColData = static_cast<double *>(
867
0
                VSI_MALLOC2_VERBOSE(iLength, sizeof(double)));
868
0
            if (padfColData == nullptr)
869
0
            {
870
0
                return CE_Failure;
871
0
            }
872
873
0
            if (eRWFlag == GF_Write)
874
0
            {
875
                // Copy the application supplied ints to doubles.
876
0
                for (int i = 0; i < iLength; i++)
877
0
                    padfColData[i] = pnData[i];
878
0
            }
879
880
            // Do the ValuesIO as doubles.
881
0
            const CPLErr eVal =
882
0
                ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
883
0
            if (eVal != CE_None)
884
0
            {
885
0
                CPLFree(padfColData);
886
0
                return eVal;
887
0
            }
888
889
0
            if (eRWFlag == GF_Read)
890
0
            {
891
                // Copy them back to ints.
892
0
                for (int i = 0; i < iLength; i++)
893
0
                    pnData[i] = static_cast<int>(padfColData[i]);
894
0
            }
895
896
0
            CPLFree(padfColData);
897
0
        }
898
0
        break;
899
0
        case GFT_String:
900
0
        {
901
            // Allocate space for string pointers.
902
0
            char **papszColData = static_cast<char **>(
903
0
                VSI_MALLOC2_VERBOSE(iLength, sizeof(char *)));
904
0
            if (papszColData == nullptr)
905
0
            {
906
0
                return CE_Failure;
907
0
            }
908
909
0
            if (eRWFlag == GF_Write)
910
0
            {
911
                // Copy the application supplied ints to strings.
912
0
                for (int i = 0; i < iLength; i++)
913
0
                {
914
0
                    osWorkingResult.Printf("%d", pnData[i]);
915
0
                    papszColData[i] = CPLStrdup(osWorkingResult);
916
0
                }
917
0
            }
918
919
            // Do the ValuesIO as strings.
920
0
            const CPLErr eVal =
921
0
                ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
922
0
            if (eVal != CE_None)
923
0
            {
924
0
                if (eRWFlag == GF_Write)
925
0
                {
926
0
                    for (int i = 0; i < iLength; i++)
927
0
                        CPLFree(papszColData[i]);
928
0
                }
929
0
                CPLFree(papszColData);
930
0
                return eVal;
931
0
            }
932
933
0
            if (eRWFlag == GF_Read)
934
0
            {
935
                // Copy them back to ints.
936
0
                for (int i = 0; i < iLength; i++)
937
0
                    pnData[i] = atoi(papszColData[i]);
938
0
            }
939
940
            // Either we allocated them for write, or they were allocated
941
            // by ValuesIO on read.
942
0
            for (int i = 0; i < iLength; i++)
943
0
                CPLFree(papszColData[i]);
944
945
0
            CPLFree(papszColData);
946
0
        }
947
0
        break;
948
0
        case GFT_Boolean:
949
0
        case GFT_DateTime:
950
0
        case GFT_WKBGeometry:
951
0
            CPLAssert(false);
952
0
            break;
953
0
    }
954
955
0
    return CE_None;
956
0
}
957
958
/************************************************************************/
959
/*                          ValuesIO()                                  */
960
/************************************************************************/
961
962
CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
963
                                         int iStartRow, int iLength,
964
                                         char **papszStrList)
965
0
{
966
0
    if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
967
0
    {
968
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
969
0
                 "Dataset not open in update mode");
970
0
        return CE_Failure;
971
0
    }
972
973
0
    if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
974
0
    {
975
0
        CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
976
0
                 iField);
977
978
0
        return CE_Failure;
979
0
    }
980
981
0
    if (iStartRow < 0 || iLength >= INT_MAX - iStartRow ||
982
0
        (iStartRow + iLength) > nRows)
983
0
    {
984
0
        CPLError(CE_Failure, CPLE_AppDefined,
985
0
                 "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
986
0
                 iLength);
987
988
0
        return CE_Failure;
989
0
    }
990
991
0
    if (aoFields[iField].bConvertColors)
992
0
    {
993
        // Convert to/from float color field.
994
0
        int *panColData =
995
0
            static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
996
0
        if (panColData == nullptr)
997
0
        {
998
0
            CPLFree(panColData);
999
0
            return CE_Failure;
1000
0
        }
1001
1002
0
        if (eRWFlag == GF_Write)
1003
0
        {
1004
0
            for (int i = 0; i < iLength; i++)
1005
0
                panColData[i] = atoi(papszStrList[i]);
1006
0
        }
1007
1008
0
        const CPLErr ret =
1009
0
            ColorsIO(eRWFlag, iField, iStartRow, iLength, panColData);
1010
1011
0
        if (eRWFlag == GF_Read)
1012
0
        {
1013
            // Copy them back to strings.
1014
0
            for (int i = 0; i < iLength; i++)
1015
0
            {
1016
0
                osWorkingResult.Printf("%d", panColData[i]);
1017
0
                papszStrList[i] = CPLStrdup(osWorkingResult);
1018
0
            }
1019
0
        }
1020
1021
0
        CPLFree(panColData);
1022
0
        return ret;
1023
0
    }
1024
1025
0
    switch (aoFields[iField].eType)
1026
0
    {
1027
0
        case GFT_Integer:
1028
0
        {
1029
            // Allocate space for ints.
1030
0
            int *panColData =
1031
0
                static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
1032
0
            if (panColData == nullptr)
1033
0
            {
1034
0
                return CE_Failure;
1035
0
            }
1036
1037
0
            if (eRWFlag == GF_Write)
1038
0
            {
1039
                // Convert user supplied strings to ints.
1040
0
                for (int i = 0; i < iLength; i++)
1041
0
                    panColData[i] = atoi(papszStrList[i]);
1042
0
            }
1043
1044
            // Call values IO to read/write ints.
1045
0
            const CPLErr eVal =
1046
0
                ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData);
1047
0
            if (eVal != CE_None)
1048
0
            {
1049
0
                CPLFree(panColData);
1050
0
                return eVal;
1051
0
            }
1052
1053
0
            if (eRWFlag == GF_Read)
1054
0
            {
1055
                // Convert ints back to strings.
1056
0
                for (int i = 0; i < iLength; i++)
1057
0
                {
1058
0
                    osWorkingResult.Printf("%d", panColData[i]);
1059
0
                    papszStrList[i] = CPLStrdup(osWorkingResult);
1060
0
                }
1061
0
            }
1062
0
            CPLFree(panColData);
1063
0
        }
1064
0
        break;
1065
0
        case GFT_Real:
1066
0
        {
1067
            // Allocate space for doubles.
1068
0
            double *padfColData = static_cast<double *>(
1069
0
                VSI_MALLOC2_VERBOSE(iLength, sizeof(double)));
1070
0
            if (padfColData == nullptr)
1071
0
            {
1072
0
                return CE_Failure;
1073
0
            }
1074
1075
0
            if (eRWFlag == GF_Write)
1076
0
            {
1077
                // Convert user supplied strings to doubles.
1078
0
                for (int i = 0; i < iLength; i++)
1079
0
                    padfColData[i] = CPLAtof(papszStrList[i]);
1080
0
            }
1081
1082
            // Call value IO to read/write doubles.
1083
0
            const CPLErr eVal =
1084
0
                ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
1085
0
            if (eVal != CE_None)
1086
0
            {
1087
0
                CPLFree(padfColData);
1088
0
                return eVal;
1089
0
            }
1090
1091
0
            if (eRWFlag == GF_Read)
1092
0
            {
1093
                // Convert doubles back to strings.
1094
0
                for (int i = 0; i < iLength; i++)
1095
0
                {
1096
0
                    osWorkingResult.Printf("%.16g", padfColData[i]);
1097
0
                    papszStrList[i] = CPLStrdup(osWorkingResult);
1098
0
                }
1099
0
            }
1100
0
            CPLFree(padfColData);
1101
0
        }
1102
0
        break;
1103
0
        case GFT_String:
1104
0
        {
1105
0
            if (VSIFSeekL(hHFA->fp,
1106
0
                          aoFields[iField].nDataOffset +
1107
0
                              (static_cast<vsi_l_offset>(iStartRow) *
1108
0
                               aoFields[iField].nElementSize),
1109
0
                          SEEK_SET) != 0)
1110
0
            {
1111
0
                return CE_Failure;
1112
0
            }
1113
0
            char *pachColData = static_cast<char *>(
1114
0
                VSI_MALLOC2_VERBOSE(iLength, aoFields[iField].nElementSize));
1115
0
            if (pachColData == nullptr)
1116
0
            {
1117
0
                return CE_Failure;
1118
0
            }
1119
1120
0
            if (eRWFlag == GF_Read)
1121
0
            {
1122
0
                if (static_cast<int>(VSIFReadL(pachColData,
1123
0
                                               aoFields[iField].nElementSize,
1124
0
                                               iLength, hHFA->fp)) != iLength)
1125
0
                {
1126
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1127
0
                             "HFARasterAttributeTable::ValuesIO: "
1128
0
                             "Cannot read values");
1129
0
                    CPLFree(pachColData);
1130
0
                    return CE_Failure;
1131
0
                }
1132
1133
                // Now copy into application buffer.
1134
0
                for (int i = 0; i < iLength; i++)
1135
0
                {
1136
0
                    osWorkingResult.assign(
1137
0
                        pachColData + aoFields[iField].nElementSize * i,
1138
0
                        aoFields[iField].nElementSize);
1139
0
                    papszStrList[i] = CPLStrdup(osWorkingResult);
1140
0
                }
1141
0
            }
1142
0
            else
1143
0
            {
1144
                // We need to check that these strings will fit in the allocated
1145
                // space.
1146
0
                int nNewMaxChars = aoFields[iField].nElementSize;
1147
0
                for (int i = 0; i < iLength; i++)
1148
0
                {
1149
0
                    const int nStringSize = static_cast<int>(
1150
0
                        std::min<size_t>(std::numeric_limits<int>::max(),
1151
0
                                         strlen(papszStrList[i]) + 1));
1152
0
                    if (nStringSize > nNewMaxChars)
1153
0
                        nNewMaxChars = nStringSize;
1154
0
                }
1155
1156
0
                if (nNewMaxChars > aoFields[iField].nElementSize)
1157
0
                {
1158
0
                    if (static_cast<unsigned>(nRows) >
1159
0
                        std::numeric_limits<unsigned>::max() / nNewMaxChars)
1160
0
                    {
1161
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1162
0
                                 "ValuesIO(): too much content");
1163
0
                        CPLFree(pachColData);
1164
0
                        return CE_Failure;
1165
0
                    }
1166
1167
                    // OK we have a problem: The allocated space is not big
1168
                    // enough we need to re-allocate the space and update the
1169
                    // pointers and copy across the old data.
1170
0
                    const int nNewOffset = HFAAllocateSpace(
1171
0
                        hHFA->papoBand[nBand - 1]->psInfo,
1172
0
                        static_cast<unsigned>(nRows) * nNewMaxChars);
1173
0
                    char *pszBuffer = static_cast<char *>(
1174
0
                        VSI_CALLOC_VERBOSE(1, nNewMaxChars));
1175
0
                    if (!pszBuffer)
1176
0
                    {
1177
0
                        CPLFree(pachColData);
1178
0
                        return CE_Failure;
1179
0
                    }
1180
0
                    for (int i = 0; i < nRows; i++)
1181
0
                    {
1182
                        // Seek to the old place.
1183
0
                        CPL_IGNORE_RET_VAL(
1184
0
                            VSIFSeekL(hHFA->fp,
1185
0
                                      aoFields[iField].nDataOffset +
1186
0
                                          (static_cast<vsi_l_offset>(i) *
1187
0
                                           aoFields[iField].nElementSize),
1188
0
                                      SEEK_SET));
1189
                        // Read in old data.
1190
0
                        CPL_IGNORE_RET_VAL(
1191
0
                            VSIFReadL(pszBuffer, aoFields[iField].nElementSize,
1192
0
                                      1, hHFA->fp));
1193
                        // Seek to new place.
1194
0
                        bool bOK = VSIFSeekL(hHFA->fp,
1195
0
                                             nNewOffset +
1196
0
                                                 (static_cast<vsi_l_offset>(i) *
1197
0
                                                  nNewMaxChars),
1198
0
                                             SEEK_SET) == 0;
1199
1200
                        // Write data to new place.
1201
0
                        bOK &= VSIFWriteL(pszBuffer, nNewMaxChars, 1,
1202
0
                                          hHFA->fp) == 1;
1203
0
                        if (!bOK)
1204
0
                        {
1205
0
                            CPLFree(pszBuffer);
1206
0
                            CPLFree(pachColData);
1207
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1208
0
                                     "HFARasterAttributeTable::ValuesIO: "
1209
0
                                     "Cannot write values");
1210
0
                            return CE_Failure;
1211
0
                        }
1212
0
                    }
1213
                    // Update our data structures.
1214
0
                    aoFields[iField].nElementSize = nNewMaxChars;
1215
0
                    aoFields[iField].nDataOffset = nNewOffset;
1216
                    // Update file.
1217
0
                    aoFields[iField].poColumn->SetIntField("columnDataPtr",
1218
0
                                                           nNewOffset);
1219
0
                    aoFields[iField].poColumn->SetIntField("maxNumChars",
1220
0
                                                           nNewMaxChars);
1221
1222
                    // Note: There isn't an HFAFreeSpace so we can't un-allocate
1223
                    // the old space in the file.
1224
0
                    CPLFree(pszBuffer);
1225
1226
                    // Re-allocate our buffer.
1227
0
                    CPLFree(pachColData);
1228
0
                    pachColData = static_cast<char *>(
1229
0
                        VSI_MALLOC2_VERBOSE(iLength, nNewMaxChars));
1230
0
                    if (pachColData == nullptr)
1231
0
                    {
1232
0
                        return CE_Failure;
1233
0
                    }
1234
1235
                    // Lastly seek to the right place in the new space ready to
1236
                    // write.
1237
0
                    if (VSIFSeekL(hHFA->fp,
1238
0
                                  nNewOffset +
1239
0
                                      (static_cast<vsi_l_offset>(iStartRow) *
1240
0
                                       nNewMaxChars),
1241
0
                                  SEEK_SET) != 0)
1242
0
                    {
1243
0
                        VSIFree(pachColData);
1244
0
                        return CE_Failure;
1245
0
                    }
1246
0
                }
1247
1248
                // Copy from application buffer.
1249
0
                for (int i = 0; i < iLength; i++)
1250
0
                {
1251
0
                    const int nStringSize = static_cast<int>(
1252
0
                        std::min<size_t>(std::numeric_limits<int>::max(),
1253
0
                                         strlen(papszStrList[i]) + 1));
1254
0
                    memcpy(&pachColData[nNewMaxChars * i], papszStrList[i],
1255
0
                           nStringSize);
1256
0
                    if (nStringSize < nNewMaxChars)
1257
0
                        memset(&pachColData[nNewMaxChars * i] + nStringSize, 0,
1258
0
                               nNewMaxChars - nStringSize);
1259
0
                }
1260
1261
                // Note: HFAAllocateSpace now called by CreateColumn so space
1262
                // should exist.
1263
0
                if (static_cast<int>(VSIFWriteL(pachColData,
1264
0
                                                aoFields[iField].nElementSize,
1265
0
                                                iLength, hHFA->fp)) != iLength)
1266
0
                {
1267
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1268
0
                             "HFARasterAttributeTable::ValuesIO: "
1269
0
                             "Cannot write values");
1270
0
                    CPLFree(pachColData);
1271
0
                    return CE_Failure;
1272
0
                }
1273
0
            }
1274
0
            CPLFree(pachColData);
1275
0
        }
1276
0
        break;
1277
0
        case GFT_Boolean:
1278
0
        case GFT_DateTime:
1279
0
        case GFT_WKBGeometry:
1280
0
            CPLAssert(false);
1281
0
            break;
1282
0
    }
1283
1284
0
    return CE_None;
1285
0
}
1286
1287
/************************************************************************/
1288
/*                          ValuesIO()                                  */
1289
/************************************************************************/
1290
1291
CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
1292
                                         int iStartRow, int iLength,
1293
                                         bool *pbData)
1294
0
{
1295
0
    return ValuesIOBooleanFromIntoInt(eRWFlag, iField, iStartRow, iLength,
1296
0
                                      pbData);
1297
0
}
1298
1299
/************************************************************************/
1300
/*                          ValuesIO()                                  */
1301
/************************************************************************/
1302
1303
CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
1304
                                         int iStartRow, int iLength,
1305
                                         GDALRATDateTime *psDateTime)
1306
0
{
1307
0
    return ValuesIODateTimeFromIntoString(eRWFlag, iField, iStartRow, iLength,
1308
0
                                          psDateTime);
1309
0
}
1310
1311
/************************************************************************/
1312
/*                          ValuesIO()                                  */
1313
/************************************************************************/
1314
1315
CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
1316
                                         int iStartRow, int iLength,
1317
                                         GByte **ppabyWKB, size_t *pnWKBSize)
1318
0
{
1319
0
    return ValuesIOWKBGeometryFromIntoString(eRWFlag, iField, iStartRow,
1320
0
                                             iLength, ppabyWKB, pnWKBSize);
1321
0
}
1322
1323
/************************************************************************/
1324
/*                               ColorsIO()                              */
1325
/************************************************************************/
1326
1327
// Handle the fact that HFA stores colours as floats, but we need to
1328
// read them in as ints 0...255.
1329
CPLErr HFARasterAttributeTable::ColorsIO(GDALRWFlag eRWFlag, int iField,
1330
                                         int iStartRow, int iLength,
1331
                                         int *pnData)
1332
0
{
1333
    // Allocate space for doubles.
1334
0
    double *padfData =
1335
0
        static_cast<double *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(double)));
1336
0
    if (padfData == nullptr)
1337
0
    {
1338
0
        return CE_Failure;
1339
0
    }
1340
1341
0
    if (eRWFlag == GF_Write)
1342
0
    {
1343
        // Copy the application supplied ints to doubles
1344
        // and convert 0..255 to 0..1 in the same manner
1345
        // as the color table.
1346
0
        for (int i = 0; i < iLength; i++)
1347
0
            padfData[i] = pnData[i] / 255.0;
1348
0
    }
1349
1350
0
    if (VSIFSeekL(hHFA->fp,
1351
0
                  aoFields[iField].nDataOffset +
1352
0
                      (static_cast<vsi_l_offset>(iStartRow) *
1353
0
                       aoFields[iField].nElementSize),
1354
0
                  SEEK_SET) != 0)
1355
0
    {
1356
0
        CPLFree(padfData);
1357
0
        return CE_Failure;
1358
0
    }
1359
1360
0
    if (eRWFlag == GF_Read)
1361
0
    {
1362
0
        if (static_cast<int>(VSIFReadL(padfData, sizeof(double), iLength,
1363
0
                                       hHFA->fp)) != iLength)
1364
0
        {
1365
0
            CPLError(CE_Failure, CPLE_AppDefined,
1366
0
                     "HFARasterAttributeTable::ColorsIO: Cannot read values");
1367
0
            CPLFree(padfData);
1368
0
            return CE_Failure;
1369
0
        }
1370
#ifdef CPL_MSB
1371
        GDALSwapWords(padfData, 8, iLength, 8);
1372
#endif
1373
0
    }
1374
0
    else
1375
0
    {
1376
#ifdef CPL_MSB
1377
        GDALSwapWords(padfData, 8, iLength, 8);
1378
#endif
1379
        // Note: HFAAllocateSpace now called by CreateColumn so space should
1380
        // exist.
1381
0
        if (static_cast<int>(VSIFWriteL(padfData, sizeof(double), iLength,
1382
0
                                        hHFA->fp)) != iLength)
1383
0
        {
1384
0
            CPLError(CE_Failure, CPLE_AppDefined,
1385
0
                     "HFARasterAttributeTable::ColorsIO: Cannot write values");
1386
0
            CPLFree(padfData);
1387
0
            return CE_Failure;
1388
0
        }
1389
0
    }
1390
1391
0
    if (eRWFlag == GF_Read)
1392
0
    {
1393
        // Copy them back to ints converting 0..1 to 0..255 in
1394
        // the same manner as the color table.
1395
        // TODO(schwehr): Symbolic constants for 255 and 256.
1396
0
        for (int i = 0; i < iLength; i++)
1397
0
            pnData[i] = std::min(255, static_cast<int>(padfData[i] * 256));
1398
0
    }
1399
1400
0
    CPLFree(padfData);
1401
1402
0
    return CE_None;
1403
0
}
1404
1405
/************************************************************************/
1406
/*                       ChangesAreWrittenToFile()                      */
1407
/************************************************************************/
1408
1409
int HFARasterAttributeTable::ChangesAreWrittenToFile()
1410
0
{
1411
0
    return TRUE;
1412
0
}
1413
1414
/************************************************************************/
1415
/*                          SetRowCount()                               */
1416
/************************************************************************/
1417
1418
void HFARasterAttributeTable::SetRowCount(int iCount)
1419
0
{
1420
0
    if (eAccess == GA_ReadOnly)
1421
0
    {
1422
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1423
0
                 "Dataset not open in update mode");
1424
0
        return;
1425
0
    }
1426
1427
0
    if (iCount > nRows)
1428
0
    {
1429
        // Making the RAT larger - a bit hard.
1430
        // We need to re-allocate space on disc.
1431
0
        for (int iCol = 0; iCol < static_cast<int>(aoFields.size()); iCol++)
1432
0
        {
1433
            // New space.
1434
0
            const int nNewOffset =
1435
0
                HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
1436
0
                                 iCount * aoFields[iCol].nElementSize);
1437
1438
            // Only need to bother if there are actually rows.
1439
0
            if (nRows > 0)
1440
0
            {
1441
                // Temp buffer for this column.
1442
0
                void *pData =
1443
0
                    VSI_MALLOC2_VERBOSE(nRows, aoFields[iCol].nElementSize);
1444
0
                if (pData == nullptr)
1445
0
                {
1446
0
                    return;
1447
0
                }
1448
                // Read old data.
1449
0
                if (VSIFSeekL(hHFA->fp, aoFields[iCol].nDataOffset, SEEK_SET) !=
1450
0
                        0 ||
1451
0
                    static_cast<int>(VSIFReadL(pData,
1452
0
                                               aoFields[iCol].nElementSize,
1453
0
                                               nRows, hHFA->fp)) != nRows)
1454
0
                {
1455
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1456
0
                             "HFARasterAttributeTable::SetRowCount: "
1457
0
                             "Cannot read values");
1458
0
                    CPLFree(pData);
1459
0
                    return;
1460
0
                }
1461
1462
                // Write data - new space will be uninitialised.
1463
0
                if (VSIFSeekL(hHFA->fp, nNewOffset, SEEK_SET) != 0 ||
1464
0
                    static_cast<int>(VSIFWriteL(pData,
1465
0
                                                aoFields[iCol].nElementSize,
1466
0
                                                nRows, hHFA->fp)) != nRows)
1467
0
                {
1468
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1469
0
                             "HFARasterAttributeTable::SetRowCount: "
1470
0
                             "Cannot write values");
1471
0
                    CPLFree(pData);
1472
0
                    return;
1473
0
                }
1474
0
                CPLFree(pData);
1475
0
            }
1476
1477
            // Update our data structures.
1478
0
            aoFields[iCol].nDataOffset = nNewOffset;
1479
            // Update file.
1480
0
            aoFields[iCol].poColumn->SetIntField("columnDataPtr", nNewOffset);
1481
0
            aoFields[iCol].poColumn->SetIntField("numRows", iCount);
1482
0
        }
1483
0
    }
1484
0
    else if (iCount < nRows)
1485
0
    {
1486
        // Update the numRows.
1487
0
        for (int iCol = 0; iCol < static_cast<int>(aoFields.size()); iCol++)
1488
0
        {
1489
0
            aoFields[iCol].poColumn->SetIntField("numRows", iCount);
1490
0
        }
1491
0
    }
1492
1493
0
    nRows = iCount;
1494
1495
0
    if (poDT != nullptr && EQUAL(poDT->GetType(), "Edsc_Table"))
1496
0
    {
1497
0
        poDT->SetIntField("numrows", iCount);
1498
0
    }
1499
0
}
1500
1501
/************************************************************************/
1502
/*                          GetRowOfValue()                             */
1503
/************************************************************************/
1504
int HFARasterAttributeTable::GetRowOfValue(double dfValue) const
1505
0
{
1506
    // Handle case of regular binning.
1507
0
    if (bLinearBinning)
1508
0
    {
1509
0
        const int iBin =
1510
0
            static_cast<int>(floor((dfValue - dfRow0Min) / dfBinSize));
1511
0
        if (iBin < 0 || iBin >= nRows)
1512
0
            return -1;
1513
0
        return iBin;
1514
0
    }
1515
    // Do we have any information?
1516
0
    int nMinCol = GetColOfUsage(GFU_Min);
1517
0
    if (nMinCol == -1)
1518
0
        nMinCol = GetColOfUsage(GFU_MinMax);
1519
0
    int nMaxCol = GetColOfUsage(GFU_Max);
1520
0
    if (nMaxCol == -1)
1521
0
        nMaxCol = GetColOfUsage(GFU_MinMax);
1522
0
    if (nMinCol == -1 && nMaxCol == -1)
1523
0
        return -1;
1524
    // Search through rows for match.
1525
0
    for (int iRow = 0; iRow < nRows; iRow++)
1526
0
    {
1527
0
        if (nMinCol != -1)
1528
0
        {
1529
0
            while (iRow < nRows && dfValue < GetValueAsDouble(iRow, nMinCol))
1530
0
                iRow++;
1531
0
            if (iRow == nRows)
1532
0
                break;
1533
0
        }
1534
0
        if (nMaxCol != -1)
1535
0
        {
1536
0
            if (dfValue > GetValueAsDouble(iRow, nMaxCol))
1537
0
                continue;
1538
0
        }
1539
0
        return iRow;
1540
0
    }
1541
0
    return -1;
1542
0
}
1543
1544
/************************************************************************/
1545
/*                          GetRowOfValue()                             */
1546
/*                                                                      */
1547
/*      Int arg for now just converted to double.  Perhaps we will      */
1548
/*      handle this in a special way some day?                          */
1549
/************************************************************************/
1550
int HFARasterAttributeTable::GetRowOfValue(int nValue) const
1551
0
{
1552
0
    return GetRowOfValue(static_cast<double>(nValue));
1553
0
}
1554
1555
/************************************************************************/
1556
/*                          CreateColumn()                              */
1557
/************************************************************************/
1558
1559
CPLErr HFARasterAttributeTable::CreateColumn(const char *pszFieldName,
1560
                                             GDALRATFieldType eFieldType,
1561
                                             GDALRATFieldUsage eFieldUsage)
1562
0
{
1563
0
    if (eAccess == GA_ReadOnly)
1564
0
    {
1565
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1566
0
                 "Dataset not open in update mode");
1567
0
        return CE_Failure;
1568
0
    }
1569
1570
0
    switch (eFieldType)
1571
0
    {
1572
0
        case GFT_Integer:
1573
0
        case GFT_Real:
1574
0
        case GFT_String:
1575
0
        case GFT_Boolean:
1576
0
        case GFT_DateTime:
1577
0
            break;
1578
1579
0
        case GFT_WKBGeometry:
1580
            // Cannot deal with any of the others yet.
1581
0
            CPLError(CE_Failure, CPLE_NotSupported,
1582
0
                     "Data type %s is not supported "
1583
0
                     "for this Raster Attribute Table.",
1584
0
                     GDALGetRATFieldTypeName(eFieldType));
1585
0
            return CE_Failure;
1586
0
    }
1587
1588
    // Do we have a descriptor table already?
1589
0
    if (poDT == nullptr || !EQUAL(poDT->GetType(), "Edsc_Table"))
1590
0
        CreateDT();
1591
1592
0
    bool bConvertColors = false;
1593
1594
    // Imagine doesn't have a concept of usage - works of the names instead.
1595
    // Must make sure name matches use.
1596
0
    if (eFieldUsage == GFU_Red)
1597
0
    {
1598
0
        pszFieldName = "Red";
1599
        // Create a real column in the file, but make it
1600
        // available as int to GDAL.
1601
0
        bConvertColors = true;
1602
0
        eFieldType = GFT_Real;
1603
0
    }
1604
0
    else if (eFieldUsage == GFU_Green)
1605
0
    {
1606
0
        pszFieldName = "Green";
1607
0
        bConvertColors = true;
1608
0
        eFieldType = GFT_Real;
1609
0
    }
1610
0
    else if (eFieldUsage == GFU_Blue)
1611
0
    {
1612
0
        pszFieldName = "Blue";
1613
0
        bConvertColors = true;
1614
0
        eFieldType = GFT_Real;
1615
0
    }
1616
0
    else if (eFieldUsage == GFU_Alpha)
1617
0
    {
1618
0
        pszFieldName = "Opacity";
1619
0
        bConvertColors = true;
1620
0
        eFieldType = GFT_Real;
1621
0
    }
1622
0
    else if (eFieldUsage == GFU_PixelCount)
1623
0
    {
1624
0
        pszFieldName = "Histogram";
1625
        // Histogram is always float in HFA.
1626
0
        eFieldType = GFT_Real;
1627
0
    }
1628
0
    else if (eFieldUsage == GFU_Name)
1629
0
    {
1630
0
        pszFieldName = "Class_Names";
1631
0
    }
1632
1633
    // Check to see if a column with pszFieldName exists and create it
1634
    // if necessary.
1635
0
    HFAEntry *poColumn = poDT->GetNamedChild(pszFieldName);
1636
1637
0
    if (poColumn == nullptr || !EQUAL(poColumn->GetType(), "Edsc_Column"))
1638
0
        poColumn = HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo,
1639
0
                                 pszFieldName, "Edsc_Column", poDT);
1640
1641
0
    poColumn->SetIntField("numRows", nRows);
1642
0
    int nElementSize = 0;
1643
1644
0
    switch (eFieldType)
1645
0
    {
1646
0
        case GFT_Integer:
1647
0
            nElementSize = sizeof(GInt32);
1648
0
            poColumn->SetStringField("dataType", "integer");
1649
0
            break;
1650
1651
0
        case GFT_Real:
1652
0
            nElementSize = sizeof(double);
1653
0
            poColumn->SetStringField("dataType", "real");
1654
0
            break;
1655
1656
0
        case GFT_String:
1657
            // Just have to guess here since we don't have any strings to check.
1658
0
            nElementSize = 10;
1659
0
            poColumn->SetStringField("dataType", "string");
1660
0
            poColumn->SetIntField("maxNumChars", nElementSize);
1661
0
            break;
1662
1663
0
        case GFT_Boolean:
1664
0
            CPLError(CE_Warning, CPLE_AppDefined,
1665
0
                     "RAT field type Boolean is not natively supported by the "
1666
0
                     "HFA driver. Dealing with it as Integer");
1667
0
            nElementSize = sizeof(GInt32);
1668
0
            poColumn->SetStringField("dataType", "integer");
1669
0
            eFieldType = GFT_Integer;
1670
0
            break;
1671
1672
0
        case GFT_DateTime:
1673
0
            CPLError(CE_Warning, CPLE_AppDefined,
1674
0
                     "RAT field type DateTime is not natively supported by the "
1675
0
                     "HFA driver. Dealing with it as String");
1676
0
            nElementSize =
1677
0
                static_cast<int>(strlen("YYYY-MM-DDTHH:MM:SS.sss+mm:ss"));
1678
0
            poColumn->SetStringField("dataType", "string");
1679
0
            poColumn->SetIntField("maxNumChars", nElementSize);
1680
0
            eFieldType = GFT_String;
1681
0
            break;
1682
1683
0
#ifndef __COVERITY__
1684
0
        case GFT_WKBGeometry:
1685
0
            CPLAssert(false);
1686
0
            break;
1687
0
#endif
1688
0
    }
1689
1690
0
    const int nOffset = HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
1691
0
                                         nRows * nElementSize);
1692
0
    poColumn->SetIntField("columnDataPtr", nOffset);
1693
1694
0
    if (bConvertColors)
1695
0
    {
1696
        // GDAL Int column
1697
0
        eFieldType = GFT_Integer;
1698
0
    }
1699
1700
0
    AddColumn(pszFieldName, eFieldType, eFieldUsage, nOffset, nElementSize,
1701
0
              poColumn, false, bConvertColors);
1702
1703
0
    return CE_None;
1704
0
}
1705
1706
/************************************************************************/
1707
/*                          SetLinearBinning()                          */
1708
/************************************************************************/
1709
1710
CPLErr HFARasterAttributeTable::SetLinearBinning(double dfRow0MinIn,
1711
                                                 double dfBinSizeIn)
1712
0
{
1713
0
    if (eAccess == GA_ReadOnly)
1714
0
    {
1715
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1716
0
                 "Dataset not open in update mode");
1717
0
        return CE_Failure;
1718
0
    }
1719
1720
0
    bLinearBinning = true;
1721
0
    dfRow0Min = dfRow0MinIn;
1722
0
    dfBinSize = dfBinSizeIn;
1723
1724
    // Do we have a descriptor table already?
1725
0
    if (poDT == nullptr || !EQUAL(poDT->GetType(), "Edsc_Table"))
1726
0
        CreateDT();
1727
1728
    // We should have an Edsc_BinFunction.
1729
0
    HFAEntry *poBinFunction = poDT->GetNamedChild("#Bin_Function#");
1730
0
    if (poBinFunction == nullptr ||
1731
0
        !EQUAL(poBinFunction->GetType(), "Edsc_BinFunction"))
1732
0
    {
1733
0
        poBinFunction =
1734
0
            HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo, "#Bin_Function#",
1735
0
                          "Edsc_BinFunction", poDT);
1736
0
    }
1737
1738
    // Because of the BaseData we have to hardcode the size.
1739
0
    poBinFunction->MakeData(30);
1740
1741
0
    poBinFunction->SetStringField("binFunction", "direct");
1742
0
    poBinFunction->SetDoubleField("minLimit", dfRow0Min);
1743
0
    poBinFunction->SetDoubleField("maxLimit",
1744
0
                                  (nRows - 1) * dfBinSize + dfRow0Min);
1745
0
    poBinFunction->SetIntField("numBins", nRows);
1746
1747
0
    return CE_None;
1748
0
}
1749
1750
/************************************************************************/
1751
/*                          GetLinearBinning()                          */
1752
/************************************************************************/
1753
1754
int HFARasterAttributeTable::GetLinearBinning(double *pdfRow0Min,
1755
                                              double *pdfBinSize) const
1756
0
{
1757
0
    if (!bLinearBinning)
1758
0
        return FALSE;
1759
1760
0
    *pdfRow0Min = dfRow0Min;
1761
0
    *pdfBinSize = dfBinSize;
1762
1763
0
    return TRUE;
1764
0
}
1765
1766
/************************************************************************/
1767
/*                              Serialize()                             */
1768
/************************************************************************/
1769
1770
CPLXMLNode *HFARasterAttributeTable::Serialize() const
1771
0
{
1772
0
    if (GetRowCount() != 0 &&
1773
0
        GetColumnCount() > RAT_MAX_ELEM_FOR_CLONE / GetRowCount())
1774
0
        return nullptr;
1775
1776
0
    return GDALRasterAttributeTable::Serialize();
1777
0
}
1778
1779
/************************************************************************/
1780
/*                              SetTableType()                             */
1781
/************************************************************************/
1782
1783
CPLErr
1784
HFARasterAttributeTable::SetTableType(const GDALRATTableType eInTableType)
1785
22.3k
{
1786
22.3k
    eTableType = eInTableType;
1787
22.3k
    return CE_None;
1788
22.3k
}
1789
1790
/************************************************************************/
1791
/*                              GetTableType()                             */
1792
/************************************************************************/
1793
1794
GDALRATTableType HFARasterAttributeTable::GetTableType() const
1795
0
{
1796
0
    return eTableType;
1797
0
}
1798
1799
void HFARasterAttributeTable::RemoveStatistics()
1800
0
{
1801
    // since we are storing the fields in a vector it will generally
1802
    // be faster to create a new vector and replace the old one
1803
    // rather than actually erasing columns.
1804
0
    std::vector<HFAAttributeField> aoNewFields;
1805
0
    for (const auto &field : aoFields)
1806
0
    {
1807
0
        switch (field.eUsage)
1808
0
        {
1809
0
            case GFU_PixelCount:
1810
0
            case GFU_Min:
1811
0
            case GFU_Max:
1812
0
            case GFU_RedMin:
1813
0
            case GFU_GreenMin:
1814
0
            case GFU_BlueMin:
1815
0
            case GFU_AlphaMin:
1816
0
            case GFU_RedMax:
1817
0
            case GFU_GreenMax:
1818
0
            case GFU_BlueMax:
1819
0
            case GFU_AlphaMax:
1820
0
            {
1821
0
                break;
1822
0
            }
1823
1824
0
            default:
1825
0
                if (field.sName != "Histogram")
1826
0
                {
1827
0
                    aoNewFields.push_back(field);
1828
0
                }
1829
0
        }
1830
0
    }
1831
0
    aoFields = std::move(aoNewFields);
1832
0
}
1833
1834
/************************************************************************/
1835
/*                           HFARasterBand()                            */
1836
/************************************************************************/
1837
1838
namespace
1839
{
1840
1841
// Convert 0..1 input color range to 0..255.
1842
// Clamp overflow and underflow.
1843
short ColorToShort(double val)
1844
364k
{
1845
364k
    const double dfScaled = val * 256.0;
1846
    // Clamp to [0..255].
1847
364k
    const double dfClamped = std::max(0.0, std::min(255.0, dfScaled));
1848
364k
    return static_cast<short>(dfClamped);
1849
364k
}
1850
1851
}  // namespace
1852
1853
HFARasterBand::HFARasterBand(HFADataset *poDSIn, int nBandIn, int iOverview)
1854
35.0k
    : poCT(nullptr),
1855
      // eHFADataType
1856
35.0k
      nOverviews(-1), nThisOverview(iOverview), papoOverviewBands(nullptr),
1857
35.0k
      hHFA(poDSIn->hHFA), bMetadataDirty(false), poDefaultRAT(nullptr)
1858
35.0k
{
1859
35.0k
    if (iOverview == -1)
1860
28.9k
        poDS = poDSIn;
1861
6.02k
    else
1862
6.02k
        poDS = nullptr;
1863
1864
35.0k
    nBand = nBandIn;
1865
35.0k
    eAccess = poDSIn->GetAccess();
1866
1867
35.0k
    int nCompression = 0;
1868
35.0k
    HFAGetBandInfo(hHFA, nBand, &eHFADataType, &nBlockXSize, &nBlockYSize,
1869
35.0k
                   &nCompression);
1870
1871
    // If this is an overview, we need to fetch the actual size,
1872
    // and block size.
1873
35.0k
    if (iOverview > -1)
1874
6.02k
    {
1875
6.02k
        EPTType eHFADataTypeO;
1876
1877
6.02k
        nOverviews = 0;
1878
6.02k
        if (HFAGetOverviewInfo(hHFA, nBand, iOverview, &nRasterXSize,
1879
6.02k
                               &nRasterYSize, &nBlockXSize, &nBlockYSize,
1880
6.02k
                               &eHFADataTypeO) != CE_None)
1881
17
        {
1882
17
            nRasterXSize = 0;
1883
17
            nRasterYSize = 0;
1884
17
            return;
1885
17
        }
1886
1887
        // If we are an 8bit overview of a 1bit layer, we need to mark
1888
        // ourselves as being "resample: average_bit2grayscale".
1889
6.01k
        if (eHFADataType == EPT_u1 && eHFADataTypeO == EPT_u8)
1890
1
        {
1891
1
            GDALMajorObject::SetMetadataItem("RESAMPLING",
1892
1
                                             "AVERAGE_BIT2GRAYSCALE");
1893
1
            GDALMajorObject::SetMetadataItem("NBITS", "8");
1894
1
        }
1895
6.01k
        eHFADataType = eHFADataTypeO;
1896
6.01k
    }
1897
1898
    // Set some other information.
1899
34.9k
    if (nCompression != 0)
1900
992
        GDALMajorObject::SetMetadataItem("COMPRESSION", "RLE",
1901
992
                                         "IMAGE_STRUCTURE");
1902
1903
34.9k
    switch (eHFADataType)
1904
34.9k
    {
1905
13.4k
        case EPT_u1:
1906
13.5k
        case EPT_u2:
1907
13.6k
        case EPT_u4:
1908
20.2k
        case EPT_u8:
1909
20.2k
            eDataType = GDT_UInt8;
1910
20.2k
            break;
1911
1912
609
        case EPT_s8:
1913
609
            eDataType = GDT_Int8;
1914
609
            break;
1915
1916
586
        case EPT_u16:
1917
586
            eDataType = GDT_UInt16;
1918
586
            break;
1919
1920
2.87k
        case EPT_s16:
1921
2.87k
            eDataType = GDT_Int16;
1922
2.87k
            break;
1923
1924
5.48k
        case EPT_u32:
1925
5.48k
            eDataType = GDT_UInt32;
1926
5.48k
            break;
1927
1928
2.58k
        case EPT_s32:
1929
2.58k
            eDataType = GDT_Int32;
1930
2.58k
            break;
1931
1932
324
        case EPT_f32:
1933
324
            eDataType = GDT_Float32;
1934
324
            break;
1935
1936
2.15k
        case EPT_f64:
1937
2.15k
            eDataType = GDT_Float64;
1938
2.15k
            break;
1939
1940
36
        case EPT_c64:
1941
36
            eDataType = GDT_CFloat32;
1942
36
            break;
1943
1944
84
        case EPT_c128:
1945
84
            eDataType = GDT_CFloat64;
1946
84
            break;
1947
1948
0
        default:
1949
0
            eDataType = GDT_UInt8;
1950
            // This should really report an error, but this isn't
1951
            // so easy from within constructors.
1952
0
            CPLDebug("GDAL", "Unsupported pixel type in HFARasterBand: %d.",
1953
0
                     eHFADataType);
1954
0
            break;
1955
34.9k
    }
1956
1957
34.9k
    if (HFAGetDataTypeBits(eHFADataType) < 8)
1958
13.6k
    {
1959
13.6k
        GDALMajorObject::SetMetadataItem(
1960
13.6k
            "NBITS", CPLString().Printf("%d", HFAGetDataTypeBits(eHFADataType)),
1961
13.6k
            "IMAGE_STRUCTURE");
1962
13.6k
    }
1963
1964
    // Collect color table if present.
1965
34.9k
    double *padfRed = nullptr;
1966
34.9k
    double *padfGreen = nullptr;
1967
34.9k
    double *padfBlue = nullptr;
1968
34.9k
    double *padfAlpha = nullptr;
1969
34.9k
    double *padfBins = nullptr;
1970
34.9k
    int nColors = 0;
1971
1972
34.9k
    if (iOverview == -1 &&
1973
28.9k
        HFAGetPCT(hHFA, nBand, &nColors, &padfRed, &padfGreen, &padfBlue,
1974
28.9k
                  &padfAlpha, &padfBins) == CE_None &&
1975
150
        nColors > 0)
1976
150
    {
1977
150
        poCT = new GDALColorTable();
1978
91.3k
        for (int iColor = 0; iColor < nColors; iColor++)
1979
91.2k
        {
1980
            // The following mapping assigns "equal sized" section of
1981
            // the [0...1] range to each possible output value and avoid
1982
            // rounding issues for the "normal" values generated using n/255.
1983
            // See bug #1732 for some discussion.
1984
91.2k
            GDALColorEntry sEntry = {ColorToShort(padfRed[iColor]),
1985
91.2k
                                     ColorToShort(padfGreen[iColor]),
1986
91.2k
                                     ColorToShort(padfBlue[iColor]),
1987
91.2k
                                     ColorToShort(padfAlpha[iColor])};
1988
1989
91.2k
            if (padfBins != nullptr)
1990
1.21k
            {
1991
1.21k
                const double dfIdx = padfBins[iColor];
1992
1.21k
                if (!(dfIdx >= 0.0 && dfIdx <= 65535.0))
1993
17
                {
1994
17
                    CPLError(CE_Failure, CPLE_NotSupported,
1995
17
                             "Invalid index padfBins[%d] = %g", iColor, dfIdx);
1996
17
                    break;
1997
17
                }
1998
1.19k
                else
1999
1.19k
                {
2000
1.19k
                    poCT->SetColorEntry(static_cast<int>(dfIdx), &sEntry);
2001
1.19k
                }
2002
1.21k
            }
2003
90.0k
            else
2004
90.0k
            {
2005
90.0k
                poCT->SetColorEntry(iColor, &sEntry);
2006
90.0k
            }
2007
91.2k
        }
2008
150
    }
2009
34.9k
}
2010
2011
/************************************************************************/
2012
/*                           ~HFARasterBand()                           */
2013
/************************************************************************/
2014
2015
HFARasterBand::~HFARasterBand()
2016
2017
35.0k
{
2018
35.0k
    FlushCache(true);
2019
2020
41.0k
    for (int iOvIndex = 0; iOvIndex < nOverviews; iOvIndex++)
2021
6.02k
    {
2022
6.02k
        delete papoOverviewBands[iOvIndex];
2023
6.02k
    }
2024
35.0k
    CPLFree(papoOverviewBands);
2025
2026
35.0k
    if (poCT != nullptr)
2027
169
        delete poCT;
2028
2029
35.0k
    if (poDefaultRAT)
2030
28.9k
        delete poDefaultRAT;
2031
35.0k
}
2032
2033
/************************************************************************/
2034
/*                          ReadAuxMetadata()                           */
2035
/************************************************************************/
2036
2037
void HFARasterBand::ReadAuxMetadata()
2038
2039
28.9k
{
2040
    // Only load metadata for full resolution layer.
2041
28.9k
    if (nThisOverview != -1)
2042
0
        return;
2043
2044
28.9k
    HFABand *poBand = hHFA->papoBand[nBand - 1];
2045
2046
28.9k
    const char *const *pszAuxMetaData = GetHFAAuxMetaDataList();
2047
434k
    for (int i = 0; pszAuxMetaData[i] != nullptr; i += 4)
2048
405k
    {
2049
405k
        HFAEntry *poEntry;
2050
405k
        if (strlen(pszAuxMetaData[i]) > 0)
2051
376k
        {
2052
376k
            poEntry = poBand->poNode->GetNamedChild(pszAuxMetaData[i]);
2053
376k
            if (poEntry == nullptr)
2054
368k
                continue;
2055
376k
        }
2056
28.9k
        else
2057
28.9k
        {
2058
28.9k
            poEntry = poBand->poNode;
2059
28.9k
            assert(poEntry);
2060
28.9k
        }
2061
2062
37.6k
        const char *pszFieldName = pszAuxMetaData[i + 1] + 1;
2063
2064
37.6k
        switch (pszAuxMetaData[i + 1][0])
2065
37.6k
        {
2066
7.05k
            case 'd':
2067
7.05k
            {
2068
7.05k
                CPLString osValueList;
2069
2070
7.05k
                CPLErr eErr = CE_None;
2071
7.05k
                int nCount = poEntry->GetFieldCount(pszFieldName, &eErr);
2072
7.05k
                if (nCount > 65536)
2073
152
                {
2074
152
                    nCount = 65536;
2075
152
                    CPLDebug("HFA", "Limiting %s to %d entries",
2076
152
                             pszAuxMetaData[i + 2], nCount);
2077
152
                }
2078
743k
                for (int iValue = 0; eErr == CE_None && iValue < nCount;
2079
736k
                     iValue++)
2080
736k
                {
2081
736k
                    CPLString osSubFieldName;
2082
736k
                    osSubFieldName.Printf("%s[%d]", pszFieldName, iValue);
2083
736k
                    const double dfValue =
2084
736k
                        poEntry->GetDoubleField(osSubFieldName, &eErr);
2085
736k
                    if (eErr != CE_None)
2086
418
                        break;
2087
2088
736k
                    char szValueAsString[100] = {};
2089
736k
                    CPLsnprintf(szValueAsString, sizeof(szValueAsString),
2090
736k
                                "%.14g", dfValue);
2091
2092
736k
                    if (iValue > 0)
2093
732k
                        osValueList += ",";
2094
736k
                    osValueList += szValueAsString;
2095
736k
                }
2096
7.05k
                if (eErr == CE_None)
2097
6.63k
                    SetMetadataItem(pszAuxMetaData[i + 2], osValueList);
2098
7.05k
            }
2099
7.05k
            break;
2100
0
            case 'i':
2101
1.49k
            case 'l':
2102
1.49k
            {
2103
1.49k
                CPLString osValueList;
2104
2105
1.49k
                CPLErr eErr = CE_None;
2106
1.49k
                int nCount = poEntry->GetFieldCount(pszFieldName, &eErr);
2107
1.49k
                if (nCount > 65536)
2108
12
                {
2109
12
                    nCount = 65536;
2110
12
                    CPLDebug("HFA", "Limiting %s to %d entries",
2111
12
                             pszAuxMetaData[i + 2], nCount);
2112
12
                }
2113
2.72k
                for (int iValue = 0; eErr == CE_None && iValue < nCount;
2114
1.49k
                     iValue++)
2115
1.33k
                {
2116
1.33k
                    CPLString osSubFieldName;
2117
1.33k
                    osSubFieldName.Printf("%s[%d]", pszFieldName, iValue);
2118
1.33k
                    int nValue = poEntry->GetIntField(osSubFieldName, &eErr);
2119
1.33k
                    if (eErr != CE_None)
2120
100
                        break;
2121
2122
1.23k
                    char szValueAsString[100] = {};
2123
1.23k
                    snprintf(szValueAsString, sizeof(szValueAsString), "%d",
2124
1.23k
                             nValue);
2125
2126
1.23k
                    if (iValue > 0)
2127
520
                        osValueList += ",";
2128
1.23k
                    osValueList += szValueAsString;
2129
1.23k
                }
2130
1.49k
                if (eErr == CE_None)
2131
1.39k
                    SetMetadataItem(pszAuxMetaData[i + 2], osValueList);
2132
1.49k
            }
2133
1.49k
            break;
2134
85
            case 's':
2135
29.0k
            case 'e':
2136
29.0k
            {
2137
29.0k
                CPLErr eErr = CE_None;
2138
29.0k
                const char *pszValue =
2139
29.0k
                    poEntry->GetStringField(pszFieldName, &eErr);
2140
29.0k
                if (eErr == CE_None)
2141
22.4k
                    SetMetadataItem(pszAuxMetaData[i + 2], pszValue);
2142
29.0k
            }
2143
29.0k
            break;
2144
0
            default:
2145
0
                CPLAssert(false);
2146
37.6k
        }
2147
37.6k
    }
2148
2149
    /* if we have a default RAT we can now set its thematic/athematic state
2150
       from the metadata we just read in */
2151
28.9k
    if (GetDefaultRAT())
2152
28.9k
    {
2153
28.9k
        const char *psLayerType = GetMetadataItem("LAYER_TYPE", "");
2154
28.9k
        if (psLayerType)
2155
22.3k
        {
2156
22.3k
            GetDefaultRAT()->SetTableType(EQUALN(psLayerType, "athematic", 9)
2157
22.3k
                                              ? GRTT_ATHEMATIC
2158
22.3k
                                              : GRTT_THEMATIC);
2159
22.3k
        }
2160
28.9k
    }
2161
28.9k
}
2162
2163
/************************************************************************/
2164
/*                       ReadHistogramMetadata()                        */
2165
/************************************************************************/
2166
2167
void HFARasterBand::ReadHistogramMetadata()
2168
2169
28.9k
{
2170
    // Only load metadata for full resolution layer.
2171
28.9k
    if (nThisOverview != -1)
2172
0
        return;
2173
2174
28.9k
    HFABand *poBand = hHFA->papoBand[nBand - 1];
2175
2176
28.9k
    HFAEntry *poEntry =
2177
28.9k
        poBand->poNode->GetNamedChild("Descriptor_Table.Histogram");
2178
28.9k
    if (poEntry == nullptr)
2179
28.3k
        return;
2180
2181
646
    int nNumBins = poEntry->GetIntField("numRows");
2182
646
    if (nNumBins < 0)
2183
21
        return;
2184
    // TODO(schwehr): Can we do a better/tighter check?
2185
625
    if (nNumBins > 1000000)
2186
32
    {
2187
32
        CPLError(CE_Failure, CPLE_FileIO, "Unreasonably large histogram: %d",
2188
32
                 nNumBins);
2189
32
        return;
2190
32
    }
2191
2192
    // Fetch the histogram values.
2193
593
    const int nOffset = poEntry->GetIntField("columnDataPtr");
2194
593
    const char *pszType = poEntry->GetStringField("dataType");
2195
593
    int nBinSize = 4;
2196
2197
593
    if (pszType != nullptr && STARTS_WITH_CI(pszType, "real"))
2198
433
        nBinSize = 8;
2199
2200
593
    GUIntBig *panHistValues = static_cast<GUIntBig *>(
2201
593
        VSI_MALLOC2_VERBOSE(sizeof(GUIntBig), nNumBins));
2202
593
    GByte *pabyWorkBuf =
2203
593
        static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBinSize, nNumBins));
2204
2205
593
    if (panHistValues == nullptr || pabyWorkBuf == nullptr)
2206
35
    {
2207
35
        VSIFree(panHistValues);
2208
35
        VSIFree(pabyWorkBuf);
2209
35
        return;
2210
35
    }
2211
2212
558
    if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
2213
558
        static_cast<int>(
2214
558
            VSIFReadL(pabyWorkBuf, nBinSize, nNumBins, hHFA->fp)) != nNumBins)
2215
70
    {
2216
70
        CPLError(CE_Failure, CPLE_FileIO, "Cannot read histogram values.");
2217
70
        CPLFree(panHistValues);
2218
70
        CPLFree(pabyWorkBuf);
2219
70
        return;
2220
70
    }
2221
2222
    // Swap into local order.
2223
92.8k
    for (int i = 0; i < nNumBins; i++)
2224
92.3k
        HFAStandard(nBinSize, pabyWorkBuf + i * nBinSize);
2225
2226
488
    if (nBinSize == 8)  // Source is doubles.
2227
429
    {
2228
429
        const double *padfWorkBuf = reinterpret_cast<double *>(pabyWorkBuf);
2229
58.6k
        for (int i = 0; i < nNumBins; i++)
2230
58.3k
        {
2231
58.3k
            const double dfNumber = padfWorkBuf[i];
2232
58.3k
            if (dfNumber >=
2233
58.3k
                    static_cast<double>(std::numeric_limits<GUIntBig>::max()) ||
2234
58.2k
                dfNumber <
2235
58.2k
                    static_cast<double>(std::numeric_limits<GUIntBig>::min()) ||
2236
58.2k
                std::isnan(dfNumber))
2237
174
            {
2238
174
                CPLError(CE_Failure, CPLE_FileIO, "Out of range hist vals.");
2239
174
                CPLFree(panHistValues);
2240
174
                CPLFree(pabyWorkBuf);
2241
174
                return;
2242
174
            }
2243
58.2k
            panHistValues[i] = static_cast<GUIntBig>(dfNumber);
2244
58.2k
        }
2245
429
    }
2246
59
    else  // Source is 32bit integers.
2247
59
    {
2248
59
        const int *panWorkBuf = reinterpret_cast<int *>(pabyWorkBuf);
2249
5.61k
        for (int i = 0; i < nNumBins; i++)
2250
5.58k
        {
2251
5.58k
            const int nNumber = panWorkBuf[i];
2252
            // Positive int should always fit.
2253
5.58k
            if (nNumber < 0)
2254
29
            {
2255
29
                CPLError(CE_Failure, CPLE_FileIO, "Out of range hist vals.");
2256
29
                CPLFree(panHistValues);
2257
29
                CPLFree(pabyWorkBuf);
2258
29
                return;
2259
29
            }
2260
5.55k
            panHistValues[i] = static_cast<GUIntBig>(nNumber);
2261
5.55k
        }
2262
59
    }
2263
2264
285
    CPLFree(pabyWorkBuf);
2265
285
    pabyWorkBuf = nullptr;
2266
2267
    // Do we have unique values for the bins?
2268
285
    double *padfBinValues = nullptr;
2269
285
    HFAEntry *poBinEntry =
2270
285
        poBand->poNode->GetNamedChild("Descriptor_Table.#Bin_Function840#");
2271
2272
285
    if (poBinEntry != nullptr &&
2273
82
        EQUAL(poBinEntry->GetType(), "Edsc_BinFunction840"))
2274
79
    {
2275
79
        const char *pszValue =
2276
79
            poBinEntry->GetStringField("binFunction.type.string");
2277
79
        if (pszValue && EQUAL(pszValue, "BFUnique"))
2278
67
            padfBinValues = HFAReadBFUniqueBins(poBinEntry, nNumBins);
2279
79
    }
2280
2281
285
    if (padfBinValues)
2282
43
    {
2283
43
        int nMaxValue = 0;
2284
43
        int nMinValue = 1000000;
2285
2286
2.97k
        for (int i = 0; i < nNumBins; i++)
2287
2.94k
        {
2288
2.94k
            const double dfCurrent = padfBinValues[i];
2289
2290
2.94k
            if (dfCurrent != floor(dfCurrent) || /* not an integer value */
2291
2.93k
                dfCurrent < 0.0 || dfCurrent > 1000.0)
2292
16
            {
2293
16
                CPLFree(padfBinValues);
2294
16
                CPLFree(panHistValues);
2295
16
                CPLDebug("HFA",
2296
16
                         "Unable to offer histogram because unique values "
2297
16
                         "list is not convenient to reform as HISTOBINVALUES.");
2298
16
                return;
2299
16
            }
2300
2301
2.92k
            nMaxValue = std::max(nMaxValue, static_cast<int>(dfCurrent));
2302
2.92k
            nMinValue = std::min(nMinValue, static_cast<int>(dfCurrent));
2303
2.92k
        }
2304
2305
27
        const int nNewBins = nMaxValue + 1;
2306
27
        GUIntBig *panNewHistValues =
2307
27
            static_cast<GUIntBig *>(CPLCalloc(sizeof(GUIntBig), nNewBins));
2308
2309
2.64k
        for (int i = 0; i < nNumBins; i++)
2310
2.61k
            panNewHistValues[static_cast<int>(padfBinValues[i])] =
2311
2.61k
                panHistValues[i];
2312
2313
27
        CPLFree(panHistValues);
2314
27
        panHistValues = panNewHistValues;
2315
27
        nNumBins = nNewBins;
2316
2317
27
        SetMetadataItem("STATISTICS_HISTOMIN", "0");
2318
27
        SetMetadataItem("STATISTICS_HISTOMAX",
2319
27
                        CPLString().Printf("%d", nMaxValue));
2320
27
        SetMetadataItem("STATISTICS_HISTONUMBINS",
2321
27
                        CPLString().Printf("%d", nMaxValue + 1));
2322
2323
27
        CPLFree(padfBinValues);
2324
27
        padfBinValues = nullptr;
2325
27
    }
2326
2327
    // Format into HISTOBINVALUES text format.
2328
269
    unsigned int nBufSize = 1024;
2329
269
    char *pszBinValues = static_cast<char *>(CPLMalloc(nBufSize));
2330
269
    pszBinValues[0] = 0;
2331
269
    int nBinValuesLen = 0;
2332
2333
52.8k
    for (int nBin = 0; nBin < nNumBins; ++nBin)
2334
52.5k
    {
2335
52.5k
        char szBuf[32] = {};
2336
52.5k
        snprintf(szBuf, 31, CPL_FRMT_GUIB, panHistValues[nBin]);
2337
52.5k
        if ((nBinValuesLen + strlen(szBuf) + 2) > nBufSize)
2338
45
        {
2339
45
            nBufSize *= 2;
2340
45
            char *pszNewBinValues = static_cast<char *>(
2341
45
                VSI_REALLOC_VERBOSE(pszBinValues, nBufSize));
2342
45
            if (pszNewBinValues == nullptr)
2343
0
            {
2344
0
                break;
2345
0
            }
2346
45
            pszBinValues = pszNewBinValues;
2347
45
        }
2348
52.5k
        strcat(pszBinValues + nBinValuesLen, szBuf);
2349
52.5k
        strcat(pszBinValues + nBinValuesLen, "|");
2350
52.5k
        nBinValuesLen += static_cast<int>(strlen(pszBinValues + nBinValuesLen));
2351
52.5k
    }
2352
2353
269
    SetMetadataItem("STATISTICS_HISTOBINVALUES", pszBinValues);
2354
269
    CPLFree(panHistValues);
2355
269
    CPLFree(pszBinValues);
2356
269
}
2357
2358
/************************************************************************/
2359
/*                             GetNoDataValue()                         */
2360
/************************************************************************/
2361
2362
double HFARasterBand::GetNoDataValue(int *pbSuccess)
2363
2364
32.0k
{
2365
32.0k
    double dfNoData = 0.0;
2366
2367
32.0k
    if (HFAGetBandNoData(hHFA, nBand, &dfNoData))
2368
6.75k
    {
2369
6.75k
        if (pbSuccess)
2370
6.61k
            *pbSuccess = TRUE;
2371
6.75k
        return dfNoData;
2372
6.75k
    }
2373
2374
25.2k
    return GDALPamRasterBand::GetNoDataValue(pbSuccess);
2375
32.0k
}
2376
2377
/************************************************************************/
2378
/*                             SetNoDataValue()                         */
2379
/************************************************************************/
2380
2381
CPLErr HFARasterBand::SetNoDataValue(double dfValue)
2382
6.25k
{
2383
6.25k
    return HFASetBandNoData(hHFA, nBand, dfValue);
2384
6.25k
}
2385
2386
/************************************************************************/
2387
/*                             GetMinimum()                             */
2388
/************************************************************************/
2389
2390
double HFARasterBand::GetMinimum(int *pbSuccess)
2391
2392
0
{
2393
0
    const char *pszValue = GetMetadataItem("STATISTICS_MINIMUM");
2394
2395
0
    if (pszValue != nullptr)
2396
0
    {
2397
0
        if (pbSuccess)
2398
0
            *pbSuccess = TRUE;
2399
0
        return CPLAtofM(pszValue);
2400
0
    }
2401
2402
0
    return GDALRasterBand::GetMinimum(pbSuccess);
2403
0
}
2404
2405
/************************************************************************/
2406
/*                             GetMaximum()                             */
2407
/************************************************************************/
2408
2409
double HFARasterBand::GetMaximum(int *pbSuccess)
2410
2411
0
{
2412
0
    const char *pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
2413
2414
0
    if (pszValue != nullptr)
2415
0
    {
2416
0
        if (pbSuccess)
2417
0
            *pbSuccess = TRUE;
2418
0
        return CPLAtofM(pszValue);
2419
0
    }
2420
2421
0
    return GDALRasterBand::GetMaximum(pbSuccess);
2422
0
}
2423
2424
/************************************************************************/
2425
/*                         EstablishOverviews()                         */
2426
/*                                                                      */
2427
/*      Delayed population of overview information.                     */
2428
/************************************************************************/
2429
2430
void HFARasterBand::EstablishOverviews()
2431
2432
32.6k
{
2433
32.6k
    if (nOverviews != -1)
2434
23.8k
        return;
2435
2436
8.73k
    nOverviews = HFAGetOverviewCount(hHFA, nBand);
2437
8.73k
    if (nOverviews > 0)
2438
183
    {
2439
183
        papoOverviewBands = static_cast<HFARasterBand **>(
2440
183
            CPLMalloc(sizeof(void *) * nOverviews));
2441
2442
6.21k
        for (int iOvIndex = 0; iOvIndex < nOverviews; iOvIndex++)
2443
6.02k
        {
2444
6.02k
            papoOverviewBands[iOvIndex] = new HFARasterBand(
2445
6.02k
                cpl::down_cast<HFADataset *>(poDS), nBand, iOvIndex);
2446
6.02k
            if (papoOverviewBands[iOvIndex]->GetXSize() == 0)
2447
17
            {
2448
17
                delete papoOverviewBands[iOvIndex];
2449
17
                papoOverviewBands[iOvIndex] = nullptr;
2450
17
            }
2451
6.02k
        }
2452
183
    }
2453
8.73k
}
2454
2455
/************************************************************************/
2456
/*                          GetOverviewCount()                          */
2457
/************************************************************************/
2458
2459
int HFARasterBand::GetOverviewCount()
2460
2461
26.2k
{
2462
26.2k
    EstablishOverviews();
2463
2464
26.2k
    if (nOverviews == 0)
2465
25.6k
        return GDALRasterBand::GetOverviewCount();
2466
2467
549
    return nOverviews;
2468
26.2k
}
2469
2470
/************************************************************************/
2471
/*                            GetOverview()                             */
2472
/************************************************************************/
2473
2474
GDALRasterBand *HFARasterBand::GetOverview(int i)
2475
2476
6.39k
{
2477
6.39k
    EstablishOverviews();
2478
2479
6.39k
    if (nOverviews == 0)
2480
0
        return GDALRasterBand::GetOverview(i);
2481
6.39k
    else if (i < 0 || i >= nOverviews)
2482
0
        return nullptr;
2483
6.39k
    else
2484
6.39k
        return papoOverviewBands[i];
2485
6.39k
}
2486
2487
/************************************************************************/
2488
/*                             IReadBlock()                             */
2489
/************************************************************************/
2490
2491
CPLErr HFARasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
2492
2493
12.0k
{
2494
12.0k
    CPLErr eErr = CE_None;
2495
2496
12.0k
    if (nThisOverview == -1)
2497
12.0k
        eErr = HFAGetRasterBlockEx(hHFA, nBand, nBlockXOff, nBlockYOff, pImage,
2498
12.0k
                                   nBlockXSize * nBlockYSize *
2499
12.0k
                                       GDALGetDataTypeSizeBytes(eDataType));
2500
0
    else
2501
0
        eErr = HFAGetOverviewRasterBlockEx(
2502
0
            hHFA, nBand, nThisOverview, nBlockXOff, nBlockYOff, pImage,
2503
0
            nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType));
2504
2505
12.0k
    if (eErr == CE_None && eHFADataType == EPT_u4)
2506
203
    {
2507
203
        GByte *pabyData = static_cast<GByte *>(pImage);
2508
2509
22.7M
        for (int ii = nBlockXSize * nBlockYSize - 2; ii >= 0; ii -= 2)
2510
22.7M
        {
2511
22.7M
            int k = ii >> 1;
2512
22.7M
            pabyData[ii + 1] = (pabyData[k] >> 4) & 0xf;
2513
22.7M
            pabyData[ii] = (pabyData[k]) & 0xf;
2514
22.7M
        }
2515
203
    }
2516
12.0k
    if (eErr == CE_None && eHFADataType == EPT_u2)
2517
221
    {
2518
221
        GByte *pabyData = static_cast<GByte *>(pImage);
2519
2520
17.1M
        for (int ii = nBlockXSize * nBlockYSize - 4; ii >= 0; ii -= 4)
2521
17.1M
        {
2522
17.1M
            int k = ii >> 2;
2523
17.1M
            pabyData[ii + 3] = (pabyData[k] >> 6) & 0x3;
2524
17.1M
            pabyData[ii + 2] = (pabyData[k] >> 4) & 0x3;
2525
17.1M
            pabyData[ii + 1] = (pabyData[k] >> 2) & 0x3;
2526
17.1M
            pabyData[ii] = (pabyData[k]) & 0x3;
2527
17.1M
        }
2528
221
    }
2529
12.0k
    if (eErr == CE_None && eHFADataType == EPT_u1)
2530
1.55k
    {
2531
1.55k
        GByte *pabyData = static_cast<GByte *>(pImage);
2532
2533
52.7M
        for (int ii = nBlockXSize * nBlockYSize - 1; ii >= 0; ii--)
2534
52.7M
        {
2535
52.7M
            if ((pabyData[ii >> 3] & (1 << (ii & 0x7))))
2536
12.3M
                pabyData[ii] = 1;
2537
40.4M
            else
2538
40.4M
                pabyData[ii] = 0;
2539
52.7M
        }
2540
1.55k
    }
2541
2542
12.0k
    return eErr;
2543
12.0k
}
2544
2545
/************************************************************************/
2546
/*                            IWriteBlock()                             */
2547
/************************************************************************/
2548
2549
CPLErr HFARasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
2550
2551
26.2k
{
2552
26.2k
    GByte *pabyOutBuf = static_cast<GByte *>(pImage);
2553
2554
    // Do we need to pack 1/2/4 bit data?
2555
    // TODO(schwehr): Make symbolic constants with explanations.
2556
26.2k
    if (eHFADataType == EPT_u1 || eHFADataType == EPT_u2 ||
2557
26.2k
        eHFADataType == EPT_u4)
2558
0
    {
2559
0
        const int nPixCount = nBlockXSize * nBlockYSize;
2560
0
        pabyOutBuf = static_cast<GByte *>(VSIMalloc2(nBlockXSize, nBlockYSize));
2561
0
        if (pabyOutBuf == nullptr)
2562
0
            return CE_Failure;
2563
2564
0
        if (eHFADataType == EPT_u1)
2565
0
        {
2566
0
            for (int ii = 0; ii < nPixCount - 7; ii += 8)
2567
0
            {
2568
0
                const int k = ii >> 3;
2569
                // TODO(schwehr): Create a temp for (GByte *)pImage.
2570
0
                pabyOutBuf[k] = (((GByte *)pImage)[ii] & 0x1) |
2571
0
                                ((((GByte *)pImage)[ii + 1] & 0x1) << 1) |
2572
0
                                ((((GByte *)pImage)[ii + 2] & 0x1) << 2) |
2573
0
                                ((((GByte *)pImage)[ii + 3] & 0x1) << 3) |
2574
0
                                ((((GByte *)pImage)[ii + 4] & 0x1) << 4) |
2575
0
                                ((((GByte *)pImage)[ii + 5] & 0x1) << 5) |
2576
0
                                ((((GByte *)pImage)[ii + 6] & 0x1) << 6) |
2577
0
                                ((((GByte *)pImage)[ii + 7] & 0x1) << 7);
2578
0
            }
2579
0
        }
2580
0
        else if (eHFADataType == EPT_u2)
2581
0
        {
2582
0
            for (int ii = 0; ii < nPixCount - 3; ii += 4)
2583
0
            {
2584
0
                const int k = ii >> 2;
2585
0
                pabyOutBuf[k] = (((GByte *)pImage)[ii] & 0x3) |
2586
0
                                ((((GByte *)pImage)[ii + 1] & 0x3) << 2) |
2587
0
                                ((((GByte *)pImage)[ii + 2] & 0x3) << 4) |
2588
0
                                ((((GByte *)pImage)[ii + 3] & 0x3) << 6);
2589
0
            }
2590
0
        }
2591
0
        else if (eHFADataType == EPT_u4)
2592
0
        {
2593
0
            for (int ii = 0; ii < nPixCount - 1; ii += 2)
2594
0
            {
2595
0
                const int k = ii >> 1;
2596
0
                pabyOutBuf[k] = (((GByte *)pImage)[ii] & 0xf) |
2597
0
                                ((((GByte *)pImage)[ii + 1] & 0xf) << 4);
2598
0
            }
2599
0
        }
2600
0
    }
2601
2602
    // Actually write out.
2603
26.2k
    const CPLErr nRetCode =
2604
26.2k
        nThisOverview == -1
2605
26.2k
            ? HFASetRasterBlock(hHFA, nBand, nBlockXOff, nBlockYOff, pabyOutBuf)
2606
26.2k
            : HFASetOverviewRasterBlock(hHFA, nBand, nThisOverview, nBlockXOff,
2607
0
                                        nBlockYOff, pabyOutBuf);
2608
2609
26.2k
    if (pabyOutBuf != pImage)
2610
0
        CPLFree(pabyOutBuf);
2611
2612
26.2k
    return nRetCode;
2613
26.2k
}
2614
2615
/************************************************************************/
2616
/*                         GetDescription()                             */
2617
/************************************************************************/
2618
2619
const char *HFARasterBand::GetDescription() const
2620
39.5k
{
2621
39.5k
    const char *pszName = HFAGetBandName(hHFA, nBand);
2622
2623
39.5k
    if (pszName == nullptr)
2624
0
        return GDALPamRasterBand::GetDescription();
2625
2626
39.5k
    return pszName;
2627
39.5k
}
2628
2629
/************************************************************************/
2630
/*                         SetDescription()                             */
2631
/************************************************************************/
2632
void HFARasterBand::SetDescription(const char *pszName)
2633
1.20k
{
2634
1.20k
    if (strlen(pszName) > 0)
2635
1.20k
        HFASetBandName(hHFA, nBand, pszName);
2636
1.20k
}
2637
2638
/************************************************************************/
2639
/*                       GetColorInterpretation()                       */
2640
/************************************************************************/
2641
2642
GDALColorInterp HFARasterBand::GetColorInterpretation()
2643
2644
12.4k
{
2645
12.4k
    if (poCT != nullptr)
2646
30
        return GCI_PaletteIndex;
2647
2648
12.3k
    return GCI_Undefined;
2649
12.4k
}
2650
2651
/************************************************************************/
2652
/*                           GetColorTable()                            */
2653
/************************************************************************/
2654
2655
GDALColorTable *HFARasterBand::GetColorTable()
2656
19
{
2657
19
    return poCT;
2658
19
}
2659
2660
/************************************************************************/
2661
/*                           SetColorTable()                            */
2662
/************************************************************************/
2663
2664
CPLErr HFARasterBand::SetColorTable(GDALColorTable *poCTable)
2665
2666
19
{
2667
19
    if (GetAccess() == GA_ReadOnly)
2668
0
    {
2669
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
2670
0
                 "Unable to set color table on read-only file.");
2671
0
        return CE_Failure;
2672
0
    }
2673
2674
    // Special case if we are clearing the color table.
2675
19
    if (poCTable == nullptr)
2676
0
    {
2677
0
        delete poCT;
2678
0
        poCT = nullptr;
2679
2680
0
        HFASetPCT(hHFA, nBand, 0, nullptr, nullptr, nullptr, nullptr);
2681
2682
0
        return CE_None;
2683
0
    }
2684
2685
    // Write out the colortable, and update the configuration.
2686
19
    int nColors = poCTable->GetColorEntryCount();
2687
2688
    /* -------------------------------------------------------------------- */
2689
    /*      If we already have a non-empty RAT set and it's smaller than    */
2690
    /*      the colour table, and all the trailing CT entries are the same, */
2691
    /*      truncate the colour table. Helps when RATs travel via GTiff.    */
2692
    /* -------------------------------------------------------------------- */
2693
19
    const GDALRasterAttributeTable *poRAT = GetDefaultRAT();
2694
19
    if (poRAT != nullptr && poRAT->GetRowCount() > 0 &&
2695
0
        poRAT->GetRowCount() < nColors)
2696
0
    {
2697
0
        bool match = true;
2698
0
        const GDALColorEntry *color1 =
2699
0
            poCTable->GetColorEntry(poRAT->GetRowCount());
2700
0
        for (int i = poRAT->GetRowCount() + 1; match && i < nColors; i++)
2701
0
        {
2702
0
            const GDALColorEntry *color2 = poCTable->GetColorEntry(i);
2703
0
            match = (color1->c1 == color2->c1 && color1->c2 == color2->c2 &&
2704
0
                     color1->c3 == color2->c3 && color1->c4 == color2->c4);
2705
0
        }
2706
0
        if (match)
2707
0
        {
2708
0
            CPLDebug("HFA",
2709
0
                     "SetColorTable: Truncating PCT size (%d) to RAT size (%d)",
2710
0
                     nColors, poRAT->GetRowCount());
2711
0
            nColors = poRAT->GetRowCount();
2712
0
        }
2713
0
    }
2714
2715
19
    double *padfRed =
2716
19
        static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
2717
19
    double *padfGreen =
2718
19
        static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
2719
19
    double *padfBlue =
2720
19
        static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
2721
19
    double *padfAlpha =
2722
19
        static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
2723
2724
311
    for (int iColor = 0; iColor < nColors; iColor++)
2725
292
    {
2726
292
        GDALColorEntry sRGB;
2727
2728
292
        poCTable->GetColorEntryAsRGB(iColor, &sRGB);
2729
2730
292
        padfRed[iColor] = sRGB.c1 / 255.0;
2731
292
        padfGreen[iColor] = sRGB.c2 / 255.0;
2732
292
        padfBlue[iColor] = sRGB.c3 / 255.0;
2733
292
        padfAlpha[iColor] = sRGB.c4 / 255.0;
2734
292
    }
2735
2736
19
    HFASetPCT(hHFA, nBand, nColors, padfRed, padfGreen, padfBlue, padfAlpha);
2737
2738
19
    CPLFree(padfRed);
2739
19
    CPLFree(padfGreen);
2740
19
    CPLFree(padfBlue);
2741
19
    CPLFree(padfAlpha);
2742
2743
19
    if (poCT)
2744
0
        delete poCT;
2745
2746
19
    poCT = poCTable->Clone();
2747
2748
19
    return CE_None;
2749
19
}
2750
2751
/************************************************************************/
2752
/*                            SetMetadata()                             */
2753
/************************************************************************/
2754
2755
CPLErr HFARasterBand::SetMetadata(char **papszMDIn, const char *pszDomain)
2756
2757
16.6k
{
2758
16.6k
    bMetadataDirty = true;
2759
2760
16.6k
    return GDALPamRasterBand::SetMetadata(papszMDIn, pszDomain);
2761
16.6k
}
2762
2763
/************************************************************************/
2764
/*                            SetMetadata()                             */
2765
/************************************************************************/
2766
2767
CPLErr HFARasterBand::SetMetadataItem(const char *pszTag, const char *pszValue,
2768
                                      const char *pszDomain)
2769
2770
30.7k
{
2771
30.7k
    bMetadataDirty = true;
2772
2773
30.7k
    return GDALPamRasterBand::SetMetadataItem(pszTag, pszValue, pszDomain);
2774
30.7k
}
2775
2776
/************************************************************************/
2777
/*                           CleanOverviews()                           */
2778
/************************************************************************/
2779
2780
CPLErr HFARasterBand::CleanOverviews()
2781
2782
0
{
2783
0
    if (nOverviews == 0)
2784
0
        return CE_None;
2785
2786
    // Clear our reference to overviews as bands.
2787
0
    for (int iOverview = 0; iOverview < nOverviews; iOverview++)
2788
0
        delete papoOverviewBands[iOverview];
2789
2790
0
    CPLFree(papoOverviewBands);
2791
0
    papoOverviewBands = nullptr;
2792
0
    nOverviews = 0;
2793
2794
    // Search for any RRDNamesList and destroy it.
2795
0
    HFABand *poBand = hHFA->papoBand[nBand - 1];
2796
0
    HFAEntry *poEntry = poBand->poNode->GetNamedChild("RRDNamesList");
2797
0
    if (poEntry != nullptr)
2798
0
    {
2799
0
        poEntry->RemoveAndDestroy();
2800
0
    }
2801
2802
    // Destroy and subsample layers under our band.
2803
0
    for (HFAEntry *poChild = poBand->poNode->GetChild(); poChild != nullptr;)
2804
0
    {
2805
0
        HFAEntry *poNext = poChild->GetNext();
2806
2807
0
        if (EQUAL(poChild->GetType(), "Eimg_Layer_SubSample"))
2808
0
            poChild->RemoveAndDestroy();
2809
2810
0
        poChild = poNext;
2811
0
    }
2812
2813
    // Clean up dependent file if we are the last band under the
2814
    // assumption there will be nothing else referencing it after
2815
    // this.
2816
0
    if (hHFA->psDependent != hHFA && hHFA->psDependent != nullptr)
2817
0
    {
2818
0
        const CPLString osFilename =
2819
0
            CPLFormFilenameSafe(hHFA->psDependent->pszPath,
2820
0
                                hHFA->psDependent->pszFilename, nullptr);
2821
2822
0
        CPL_IGNORE_RET_VAL(HFAClose(hHFA->psDependent));
2823
0
        hHFA->psDependent = nullptr;
2824
2825
0
        CPLDebug("HFA", "Unlink(%s)", osFilename.c_str());
2826
0
        VSIUnlink(osFilename);
2827
0
    }
2828
2829
0
    return CE_None;
2830
0
}
2831
2832
/************************************************************************/
2833
/*                           BuildOverviews()                           */
2834
/************************************************************************/
2835
2836
CPLErr HFARasterBand::BuildOverviews(const char *pszResampling,
2837
                                     int nReqOverviews,
2838
                                     const int *panOverviewList,
2839
                                     GDALProgressFunc pfnProgress,
2840
                                     void *pProgressData,
2841
                                     CSLConstList papszOptions)
2842
2843
0
{
2844
0
    EstablishOverviews();
2845
2846
0
    if (nThisOverview != -1)
2847
0
    {
2848
0
        CPLError(CE_Failure, CPLE_AppDefined,
2849
0
                 "Attempt to build overviews on an overview layer.");
2850
2851
0
        return CE_Failure;
2852
0
    }
2853
2854
0
    if (nReqOverviews == 0)
2855
0
        return CleanOverviews();
2856
2857
0
    GDALRasterBand **papoOvBands = static_cast<GDALRasterBand **>(
2858
0
        CPLCalloc(sizeof(void *), nReqOverviews));
2859
2860
0
    const bool bRegenerate =
2861
0
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "@REGENERATE", "YES"));
2862
2863
    // Loop over overview levels requested.
2864
0
    for (int iOverview = 0; iOverview < nReqOverviews; iOverview++)
2865
0
    {
2866
        // Find this overview level.
2867
0
        const int nReqOvLevel = GDALOvLevelAdjust2(panOverviewList[iOverview],
2868
0
                                                   nRasterXSize, nRasterYSize);
2869
2870
0
        for (int i = 0; i < nOverviews && papoOvBands[iOverview] == nullptr;
2871
0
             i++)
2872
0
        {
2873
0
            if (papoOverviewBands[i] == nullptr)
2874
0
            {
2875
0
                CPLDebug("HFA", "Shouldn't happen happened at line %d",
2876
0
                         __LINE__);
2877
0
                continue;
2878
0
            }
2879
2880
0
            const int nThisOvLevel = GDALComputeOvFactor(
2881
0
                papoOverviewBands[i]->GetXSize(), GetXSize(),
2882
0
                papoOverviewBands[i]->GetYSize(), GetYSize());
2883
2884
0
            if (nReqOvLevel == nThisOvLevel)
2885
0
                papoOvBands[iOverview] = papoOverviewBands[i];
2886
0
        }
2887
2888
        // If this overview level does not yet exist, create it now.
2889
0
        if (papoOvBands[iOverview] == nullptr)
2890
0
        {
2891
0
            const int iResult = HFACreateOverview(
2892
0
                hHFA, nBand, panOverviewList[iOverview], pszResampling);
2893
0
            if (iResult < 0)
2894
0
            {
2895
0
                CPLFree(papoOvBands);
2896
0
                return CE_Failure;
2897
0
            }
2898
2899
0
            if (papoOverviewBands == nullptr && nOverviews == 0 && iResult > 0)
2900
0
            {
2901
0
                CPLDebug("HFA", "Shouldn't happen happened at line %d",
2902
0
                         __LINE__);
2903
0
                papoOverviewBands = static_cast<HFARasterBand **>(
2904
0
                    CPLCalloc(sizeof(void *), iResult));
2905
0
            }
2906
2907
0
            nOverviews = iResult + 1;
2908
0
            papoOverviewBands = static_cast<HFARasterBand **>(
2909
0
                CPLRealloc(papoOverviewBands, sizeof(void *) * nOverviews));
2910
0
            papoOverviewBands[iResult] = new HFARasterBand(
2911
0
                cpl::down_cast<HFADataset *>(poDS), nBand, iResult);
2912
2913
0
            papoOvBands[iOverview] = papoOverviewBands[iResult];
2914
0
        }
2915
0
    }
2916
2917
0
    CPLErr eErr = CE_None;
2918
2919
0
    if (bRegenerate)
2920
0
        eErr = GDALRegenerateOverviewsEx((GDALRasterBandH)this, nReqOverviews,
2921
0
                                         (GDALRasterBandH *)papoOvBands,
2922
0
                                         pszResampling, pfnProgress,
2923
0
                                         pProgressData, papszOptions);
2924
2925
0
    CPLFree(papoOvBands);
2926
2927
0
    return eErr;
2928
0
}
2929
2930
/************************************************************************/
2931
/*                        GetDefaultHistogram()                         */
2932
/************************************************************************/
2933
2934
CPLErr HFARasterBand::GetDefaultHistogram(double *pdfMin, double *pdfMax,
2935
                                          int *pnBuckets,
2936
                                          GUIntBig **ppanHistogram, int bForce,
2937
                                          GDALProgressFunc pfnProgress,
2938
                                          void *pProgressData)
2939
2940
0
{
2941
0
    if (GetMetadataItem("STATISTICS_HISTOBINVALUES") != nullptr &&
2942
0
        GetMetadataItem("STATISTICS_HISTOMIN") != nullptr &&
2943
0
        GetMetadataItem("STATISTICS_HISTOMAX") != nullptr)
2944
0
    {
2945
0
        const char *pszBinValues = GetMetadataItem("STATISTICS_HISTOBINVALUES");
2946
2947
0
        *pdfMin = CPLAtof(GetMetadataItem("STATISTICS_HISTOMIN"));
2948
0
        *pdfMax = CPLAtof(GetMetadataItem("STATISTICS_HISTOMAX"));
2949
2950
0
        *pnBuckets = 0;
2951
0
        for (int i = 0; pszBinValues[i] != '\0'; i++)
2952
0
        {
2953
0
            if (pszBinValues[i] == '|')
2954
0
                (*pnBuckets)++;
2955
0
        }
2956
2957
0
        *ppanHistogram =
2958
0
            static_cast<GUIntBig *>(CPLCalloc(sizeof(GUIntBig), *pnBuckets));
2959
2960
0
        const char *pszNextBin = pszBinValues;
2961
0
        for (int i = 0; i < *pnBuckets; i++)
2962
0
        {
2963
0
            (*ppanHistogram)[i] =
2964
0
                static_cast<GUIntBig>(CPLAtoGIntBig(pszNextBin));
2965
2966
0
            while (*pszNextBin != '|' && *pszNextBin != '\0')
2967
0
                pszNextBin++;
2968
0
            if (*pszNextBin == '|')
2969
0
                pszNextBin++;
2970
0
        }
2971
2972
        // Adjust min/max to reflect outer edges of buckets.
2973
0
        double dfBucketWidth = (*pdfMax - *pdfMin) / (*pnBuckets - 1);
2974
0
        *pdfMax += 0.5 * dfBucketWidth;
2975
0
        *pdfMin -= 0.5 * dfBucketWidth;
2976
2977
0
        return CE_None;
2978
0
    }
2979
2980
0
    return GDALPamRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets,
2981
0
                                                  ppanHistogram, bForce,
2982
0
                                                  pfnProgress, pProgressData);
2983
0
}
2984
2985
/************************************************************************/
2986
/*                           SetDefaultRAT()                            */
2987
/************************************************************************/
2988
2989
CPLErr HFARasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
2990
2991
0
{
2992
0
    if (poRAT == nullptr)
2993
0
        return CE_Failure;
2994
2995
0
    delete poDefaultRAT;
2996
0
    poDefaultRAT = nullptr;
2997
2998
0
    CPLErr r = WriteNamedRAT("Descriptor_Table", poRAT);
2999
0
    if (!r)
3000
0
        GetDefaultRAT();
3001
3002
0
    return r;
3003
0
}
3004
3005
/************************************************************************/
3006
/*                           GetDefaultRAT()                            */
3007
/************************************************************************/
3008
3009
GDALRasterAttributeTable *HFARasterBand::GetDefaultRAT()
3010
3011
51.3k
{
3012
51.3k
    if (poDefaultRAT == nullptr)
3013
28.9k
        poDefaultRAT = new HFARasterAttributeTable(this, "Descriptor_Table");
3014
3015
51.3k
    return poDefaultRAT;
3016
51.3k
}
3017
3018
/************************************************************************/
3019
/*                            WriteNamedRAT()                            */
3020
/************************************************************************/
3021
3022
CPLErr HFARasterBand::WriteNamedRAT(const char * /*pszName*/,
3023
                                    const GDALRasterAttributeTable *poRAT)
3024
0
{
3025
    // Find the requested table.
3026
0
    HFAEntry *poDT =
3027
0
        hHFA->papoBand[nBand - 1]->poNode->GetNamedChild("Descriptor_Table");
3028
0
    if (poDT == nullptr || !EQUAL(poDT->GetType(), "Edsc_Table"))
3029
0
        poDT =
3030
0
            HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo, "Descriptor_Table",
3031
0
                          "Edsc_Table", hHFA->papoBand[nBand - 1]->poNode);
3032
3033
0
    const int nRowCount = poRAT->GetRowCount();
3034
3035
0
    poDT->SetIntField("numrows", nRowCount);
3036
    // Check if binning is set on this RAT.
3037
0
    double dfBinSize = 0.0;
3038
0
    double dfRow0Min = 0.0;
3039
0
    if (poRAT->GetLinearBinning(&dfRow0Min, &dfBinSize))
3040
0
    {
3041
        // Then it should have an Edsc_BinFunction.
3042
0
        HFAEntry *poBinFunction = poDT->GetNamedChild("#Bin_Function#");
3043
0
        if (poBinFunction == nullptr ||
3044
0
            !EQUAL(poBinFunction->GetType(), "Edsc_BinFunction"))
3045
0
        {
3046
0
            poBinFunction =
3047
0
                HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo,
3048
0
                              "#Bin_Function#", "Edsc_BinFunction", poDT);
3049
0
        }
3050
3051
        // direct for thematic layers, linear otherwise
3052
0
        const char *pszLayerType =
3053
0
            hHFA->papoBand[nBand - 1]->poNode->GetStringField("layerType");
3054
0
        if (pszLayerType == nullptr || STARTS_WITH_CI(pszLayerType, "thematic"))
3055
0
            poBinFunction->SetStringField("binFunctionType", "direct");
3056
0
        else
3057
0
            poBinFunction->SetStringField("binFunctionType", "linear");
3058
3059
0
        poBinFunction->SetDoubleField("minLimit", dfRow0Min);
3060
0
        poBinFunction->SetDoubleField("maxLimit",
3061
0
                                      (nRowCount - 1) * dfBinSize + dfRow0Min);
3062
0
        poBinFunction->SetIntField("numBins", nRowCount);
3063
0
    }
3064
3065
    // Loop through each column in the RAT.
3066
0
    const int nColCount = poRAT->GetColumnCount();
3067
3068
0
    for (int col = 0; col < nColCount; col++)
3069
0
    {
3070
0
        const char *pszName = nullptr;
3071
3072
0
        const auto eUsage = poRAT->GetUsageOfCol(col);
3073
0
        if (eUsage == GFU_Red)
3074
0
        {
3075
0
            pszName = "Red";
3076
0
        }
3077
0
        else if (eUsage == GFU_Green)
3078
0
        {
3079
0
            pszName = "Green";
3080
0
        }
3081
0
        else if (eUsage == GFU_Blue)
3082
0
        {
3083
0
            pszName = "Blue";
3084
0
        }
3085
0
        else if (eUsage == GFU_Alpha)
3086
0
        {
3087
0
            pszName = "Opacity";
3088
0
        }
3089
0
        else if (eUsage == GFU_PixelCount)
3090
0
        {
3091
0
            pszName = "Histogram";
3092
0
        }
3093
0
        else if (eUsage == GFU_Name)
3094
0
        {
3095
0
            pszName = "Class_Names";
3096
0
        }
3097
0
        else
3098
0
        {
3099
0
            pszName = poRAT->GetNameOfCol(col);
3100
0
        }
3101
3102
        // Check to see if a column with pszName exists and create if
3103
        // if necessary.
3104
0
        HFAEntry *poColumn = poDT->GetNamedChild(pszName);
3105
3106
0
        if (poColumn == nullptr || !EQUAL(poColumn->GetType(), "Edsc_Column"))
3107
0
            poColumn = HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo, pszName,
3108
0
                                     "Edsc_Column", poDT);
3109
3110
0
        poColumn->SetIntField("numRows", nRowCount);
3111
        // Color cols which are integer in GDAL are written as floats in HFA.
3112
0
        bool bIsColorCol = false;
3113
0
        if (eUsage == GFU_Red || eUsage == GFU_Green || eUsage == GFU_Blue ||
3114
0
            eUsage == GFU_Alpha)
3115
0
        {
3116
0
            bIsColorCol = true;
3117
0
        }
3118
3119
        // Write float also if a color column or histogram.
3120
0
        auto eType = poRAT->GetTypeOfCol(col);
3121
0
        if (bIsColorCol || eUsage == GFU_PixelCount)
3122
0
            eType = GFT_Real;
3123
0
        switch (eType)
3124
0
        {
3125
0
            case GFT_Real:
3126
0
            {
3127
0
                if (static_cast<GUInt32>(nRowCount) >
3128
0
                    std::numeric_limits<unsigned>::max() / sizeof(double))
3129
0
                {
3130
0
                    CPLError(CE_Failure, CPLE_OutOfMemory,
3131
0
                             "WriteNamedRAT(): too much content");
3132
0
                    return CE_Failure;
3133
0
                }
3134
0
                const int nOffset = HFAAllocateSpace(
3135
0
                    hHFA->papoBand[nBand - 1]->psInfo,
3136
0
                    static_cast<GUInt32>(nRowCount * sizeof(double)));
3137
0
                poColumn->SetIntField("columnDataPtr", nOffset);
3138
0
                poColumn->SetStringField("dataType", "real");
3139
3140
0
                double *padfColData = static_cast<double *>(
3141
0
                    VSI_MALLOC_VERBOSE(nRowCount * sizeof(double)));
3142
0
                if (!padfColData)
3143
0
                    return CE_Failure;
3144
0
                for (int i = 0; i < nRowCount; i++)
3145
0
                {
3146
0
                    if (bIsColorCol)
3147
                        // Stored 0..1
3148
0
                        padfColData[i] = poRAT->GetValueAsInt(i, col) / 255.0;
3149
0
                    else
3150
0
                        padfColData[i] = poRAT->GetValueAsDouble(i, col);
3151
0
                }
3152
#ifdef CPL_MSB
3153
                GDALSwapWords(padfColData, 8, nRowCount, 8);
3154
#endif
3155
0
                if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
3156
0
                    VSIFWriteL(padfColData, nRowCount, sizeof(double),
3157
0
                               hHFA->fp) != sizeof(double))
3158
0
                {
3159
0
                    CPLError(CE_Failure, CPLE_FileIO, "WriteNamedRAT() failed");
3160
0
                    CPLFree(padfColData);
3161
0
                    return CE_Failure;
3162
0
                }
3163
0
                CPLFree(padfColData);
3164
0
                break;
3165
0
            }
3166
3167
0
            case GFT_String:
3168
0
            case GFT_DateTime:
3169
0
            case GFT_WKBGeometry:
3170
0
            {
3171
0
                unsigned int nMaxNumChars = 1;
3172
0
                if (eType == GFT_DateTime)
3173
0
                {
3174
0
                    nMaxNumChars = static_cast<unsigned>(strlen(
3175
0
                                       "YYYY-MM-DDTHH:MM:SS.sss+hh:mm")) +
3176
0
                                   1;
3177
0
                }
3178
0
                else
3179
0
                {
3180
                    // Find the length of the longest string.
3181
0
                    for (int i = 0; i < nRowCount; i++)
3182
0
                    {
3183
                        // Include terminating byte.
3184
0
                        nMaxNumChars = std::max(
3185
0
                            nMaxNumChars,
3186
0
                            static_cast<unsigned>(std::min<size_t>(
3187
0
                                std::numeric_limits<unsigned>::max(),
3188
0
                                strlen(poRAT->GetValueAsString(i, col)) + 1)));
3189
0
                    }
3190
0
                }
3191
0
                if (static_cast<unsigned>(nRowCount) >
3192
0
                    std::numeric_limits<unsigned>::max() / nMaxNumChars)
3193
0
                {
3194
0
                    CPLError(CE_Failure, CPLE_OutOfMemory,
3195
0
                             "WriteNamedRAT(): too much content");
3196
0
                    return CE_Failure;
3197
0
                }
3198
3199
0
                const int nOffset =
3200
0
                    HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
3201
0
                                     nRowCount * nMaxNumChars);
3202
0
                poColumn->SetIntField("columnDataPtr", nOffset);
3203
0
                poColumn->SetStringField("dataType", "string");
3204
0
                poColumn->SetIntField("maxNumChars", nMaxNumChars);
3205
3206
0
                char *pachColData = static_cast<char *>(
3207
0
                    VSI_MALLOC_VERBOSE(nRowCount * nMaxNumChars));
3208
0
                if (!pachColData)
3209
0
                    return CE_Failure;
3210
0
                for (int i = 0; i < nRowCount; i++)
3211
0
                {
3212
0
                    const char *pszVal = poRAT->GetValueAsString(i, col);
3213
0
                    const unsigned nSize = static_cast<unsigned>(
3214
0
                        std::min<size_t>(std::numeric_limits<unsigned>::max(),
3215
0
                                         strlen(pszVal)));
3216
0
                    memcpy(&pachColData[nMaxNumChars * i], pszVal, nSize);
3217
0
                    if (nSize < nMaxNumChars)
3218
0
                        memset(&pachColData[nMaxNumChars * i] + nSize, 0,
3219
0
                               nMaxNumChars - nSize);
3220
0
                }
3221
0
                if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
3222
0
                    VSIFWriteL(pachColData, nRowCount, nMaxNumChars,
3223
0
                               hHFA->fp) != nMaxNumChars)
3224
0
                {
3225
0
                    CPLError(CE_Failure, CPLE_FileIO, "WriteNamedRAT() failed");
3226
0
                    CPLFree(pachColData);
3227
0
                    return CE_Failure;
3228
0
                }
3229
0
                CPLFree(pachColData);
3230
0
                break;
3231
0
            }
3232
3233
0
            case GFT_Integer:
3234
0
            case GFT_Boolean:
3235
0
            {
3236
0
                if (static_cast<GUInt32>(nRowCount) >
3237
0
                    std::numeric_limits<unsigned>::max() / sizeof(GInt32))
3238
0
                {
3239
0
                    CPLError(CE_Failure, CPLE_OutOfMemory,
3240
0
                             "WriteNamedRAT(): too much content");
3241
0
                    return CE_Failure;
3242
0
                }
3243
0
                const int nOffset = HFAAllocateSpace(
3244
0
                    hHFA->papoBand[nBand - 1]->psInfo,
3245
0
                    static_cast<GUInt32>(nRowCount * sizeof(GInt32)));
3246
0
                poColumn->SetIntField("columnDataPtr", nOffset);
3247
0
                poColumn->SetStringField("dataType", "integer");
3248
3249
0
                GInt32 *panColData = static_cast<GInt32 *>(
3250
0
                    VSI_MALLOC_VERBOSE(nRowCount * sizeof(GInt32)));
3251
0
                if (!panColData)
3252
0
                    return CE_Failure;
3253
0
                for (int i = 0; i < nRowCount; i++)
3254
0
                {
3255
0
                    panColData[i] = poRAT->GetValueAsInt(i, col);
3256
0
                }
3257
#ifdef CPL_MSB
3258
                GDALSwapWords(panColData, 4, nRowCount, 4);
3259
#endif
3260
0
                if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
3261
0
                    VSIFWriteL(panColData, nRowCount, sizeof(GInt32),
3262
0
                               hHFA->fp) != sizeof(GInt32))
3263
0
                {
3264
0
                    CPLError(CE_Failure, CPLE_FileIO, "WriteNamedRAT() failed");
3265
0
                    CPLFree(panColData);
3266
0
                    return CE_Failure;
3267
0
                }
3268
0
                CPLFree(panColData);
3269
0
                break;
3270
0
            }
3271
0
        }
3272
0
    }
3273
3274
0
    return CE_None;
3275
0
}
3276
3277
/************************************************************************/
3278
/* ==================================================================== */
3279
/*                            HFADataset                               */
3280
/* ==================================================================== */
3281
/************************************************************************/
3282
3283
/************************************************************************/
3284
/*                            HFADataset()                            */
3285
/************************************************************************/
3286
3287
HFADataset::HFADataset()
3288
12.7k
{
3289
12.7k
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3290
12.7k
}
3291
3292
/************************************************************************/
3293
/*                           ~HFADataset()                            */
3294
/************************************************************************/
3295
3296
HFADataset::~HFADataset()
3297
3298
12.7k
{
3299
12.7k
    HFADataset::FlushCache(true);
3300
3301
    // Destroy the raster bands if they exist.  We forcibly clean
3302
    // them up now to avoid any effort to write to them after the
3303
    // file is closed.
3304
41.6k
    for (int i = 0; i < nBands && papoBands != nullptr; i++)
3305
28.9k
    {
3306
28.9k
        if (papoBands[i] != nullptr)
3307
28.9k
            delete papoBands[i];
3308
28.9k
    }
3309
3310
12.7k
    CPLFree(papoBands);
3311
12.7k
    papoBands = nullptr;
3312
3313
    // Close the file.
3314
12.7k
    if (hHFA != nullptr)
3315
12.7k
    {
3316
12.7k
        if (HFAClose(hHFA) != 0)
3317
0
        {
3318
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
3319
0
        }
3320
12.7k
        hHFA = nullptr;
3321
12.7k
    }
3322
12.7k
}
3323
3324
/************************************************************************/
3325
/*                             FlushCache()                             */
3326
/************************************************************************/
3327
3328
CPLErr HFADataset::FlushCache(bool bAtClosing)
3329
3330
12.8k
{
3331
12.8k
    CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
3332
3333
12.8k
    if (eAccess != GA_Update)
3334
12.5k
        return eErr;
3335
3336
224
    if (bGeoDirty)
3337
50
        WriteProjection();
3338
3339
224
    if (bMetadataDirty && GetMetadata() != nullptr)
3340
77
    {
3341
77
        HFASetMetadata(hHFA, 0, GetMetadata());
3342
77
        bMetadataDirty = false;
3343
77
    }
3344
3345
38.1k
    for (int iBand = 0; iBand < nBands; iBand++)
3346
37.8k
    {
3347
37.8k
        HFARasterBand *poBand =
3348
37.8k
            static_cast<HFARasterBand *>(GetRasterBand(iBand + 1));
3349
37.8k
        if (poBand->bMetadataDirty && poBand->GetMetadata() != nullptr)
3350
15.3k
        {
3351
15.3k
            HFASetMetadata(hHFA, iBand + 1, poBand->GetMetadata());
3352
15.3k
            poBand->bMetadataDirty = false;
3353
15.3k
        }
3354
37.8k
    }
3355
3356
224
    return eErr;
3357
12.8k
}
3358
3359
/************************************************************************/
3360
/*                          WriteProjection()                           */
3361
/************************************************************************/
3362
3363
CPLErr HFADataset::WriteProjection()
3364
3365
50
{
3366
50
    bool bPEStringStored = false;
3367
3368
50
    bGeoDirty = false;
3369
3370
50
    const OGRSpatialReference &oSRS = m_oSRS;
3371
50
    const bool bHaveSRS = !oSRS.IsEmpty();
3372
3373
    // Initialize projection and datum.
3374
50
    Eprj_Datum sDatum;
3375
50
    Eprj_ProParameters sPro;
3376
50
    Eprj_MapInfo sMapInfo;
3377
50
    memset(&sPro, 0, sizeof(sPro));
3378
50
    memset(&sDatum, 0, sizeof(sDatum));
3379
50
    memset(&sMapInfo, 0, sizeof(sMapInfo));
3380
3381
    // Collect datum information.
3382
50
    OGRSpatialReference *poGeogSRS = bHaveSRS ? oSRS.CloneGeogCS() : nullptr;
3383
3384
50
    if (poGeogSRS)
3385
33
    {
3386
33
        sDatum.datumname =
3387
33
            const_cast<char *>(poGeogSRS->GetAttrValue("GEOGCS|DATUM"));
3388
33
        if (sDatum.datumname == nullptr)
3389
5
            sDatum.datumname = const_cast<char *>("");
3390
3391
        // WKT to Imagine translation.
3392
33
        const char *const *papszDatumMap = HFAGetDatumMap();
3393
229
        for (int i = 0; papszDatumMap[i] != nullptr; i += 2)
3394
203
        {
3395
203
            if (EQUAL(sDatum.datumname, papszDatumMap[i + 1]))
3396
7
            {
3397
7
                sDatum.datumname = (char *)papszDatumMap[i];
3398
7
                break;
3399
7
            }
3400
203
        }
3401
3402
        // Map some EPSG datum codes directly to Imagine names.
3403
33
        const int nGCS = poGeogSRS->GetEPSGGeogCS();
3404
3405
33
        if (nGCS == 4326)
3406
3
            sDatum.datumname = const_cast<char *>("WGS 84");
3407
30
        else if (nGCS == 4322)
3408
0
            sDatum.datumname = const_cast<char *>("WGS 1972");
3409
30
        else if (nGCS == 4267)
3410
0
            sDatum.datumname = const_cast<char *>("NAD27");
3411
30
        else if (nGCS == 4269)
3412
0
            sDatum.datumname = const_cast<char *>("NAD83");
3413
30
        else if (nGCS == 4283)
3414
0
            sDatum.datumname = const_cast<char *>("GDA94");
3415
30
        else if (nGCS == 4284)
3416
0
            sDatum.datumname = const_cast<char *>("Pulkovo 1942");
3417
30
        else if (nGCS == 4272)
3418
0
            sDatum.datumname = const_cast<char *>("Geodetic Datum 1949");
3419
3420
33
        if (poGeogSRS->GetTOWGS84(sDatum.params) == OGRERR_NONE)
3421
1
        {
3422
1
            sDatum.type = EPRJ_DATUM_PARAMETRIC;
3423
1
            sDatum.params[3] *= -ARCSEC2RAD;
3424
1
            sDatum.params[4] *= -ARCSEC2RAD;
3425
1
            sDatum.params[5] *= -ARCSEC2RAD;
3426
1
            sDatum.params[6] *= 1e-6;
3427
1
        }
3428
32
        else if (EQUAL(sDatum.datumname, "NAD27"))
3429
0
        {
3430
0
            sDatum.type = EPRJ_DATUM_GRID;
3431
0
            sDatum.gridname = const_cast<char *>("nadcon.dat");
3432
0
        }
3433
32
        else
3434
32
        {
3435
            // We will default to this (effectively WGS84) for now.
3436
32
            sDatum.type = EPRJ_DATUM_PARAMETRIC;
3437
32
        }
3438
3439
        // Verify if we need to write a ESRI PE string.
3440
33
        if (!bDisablePEString)
3441
33
            bPEStringStored = CPL_TO_BOOL(WritePeStringIfNeeded(&oSRS, hHFA));
3442
3443
33
        sPro.proSpheroid.sphereName =
3444
33
            (char *)poGeogSRS->GetAttrValue("GEOGCS|DATUM|SPHEROID");
3445
33
        sPro.proSpheroid.a = poGeogSRS->GetSemiMajor();
3446
33
        sPro.proSpheroid.b = poGeogSRS->GetSemiMinor();
3447
33
        sPro.proSpheroid.radius = sPro.proSpheroid.a;
3448
3449
33
        const double a2 = sPro.proSpheroid.a * sPro.proSpheroid.a;
3450
33
        const double b2 = sPro.proSpheroid.b * sPro.proSpheroid.b;
3451
3452
        // a2 == 0 is non sensical of course. Just to please fuzzers
3453
33
        sPro.proSpheroid.eSquared = (a2 == 0.0) ? 0.0 : (a2 - b2) / a2;
3454
33
    }
3455
3456
50
    if (sDatum.datumname == nullptr)
3457
17
        sDatum.datumname = const_cast<char *>("");
3458
50
    if (sPro.proSpheroid.sphereName == nullptr)
3459
22
        sPro.proSpheroid.sphereName = const_cast<char *>("");
3460
3461
    // Recognise various projections.
3462
50
    const char *pszProjName = nullptr;
3463
3464
50
    if (bHaveSRS)
3465
33
        pszProjName = oSRS.GetAttrValue("PROJCS|PROJECTION");
3466
3467
50
    if (bForceToPEString && !bPEStringStored)
3468
0
    {
3469
0
        char *pszPEString = nullptr;
3470
0
        const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
3471
0
        oSRS.exportToWkt(&pszPEString, apszOptions);
3472
        // Need to transform this into ESRI format.
3473
0
        HFASetPEString(hHFA, pszPEString);
3474
0
        CPLFree(pszPEString);
3475
3476
0
        bPEStringStored = true;
3477
0
    }
3478
50
    else if (pszProjName == nullptr)
3479
41
    {
3480
41
        if (bHaveSRS && oSRS.IsGeographic())
3481
23
        {
3482
23
            sPro.proNumber = EPRJ_LATLONG;
3483
23
            sPro.proName = const_cast<char *>("Geographic (Lat/Lon)");
3484
23
        }
3485
41
    }
3486
    // TODO: Add State Plane.
3487
9
    else if (!bIgnoreUTM && oSRS.GetUTMZone(nullptr) != 0)
3488
1
    {
3489
1
        int bNorth = FALSE;
3490
1
        const int nZone = oSRS.GetUTMZone(&bNorth);
3491
1
        sPro.proNumber = EPRJ_UTM;
3492
1
        sPro.proName = const_cast<char *>("UTM");
3493
1
        sPro.proZone = nZone;
3494
1
        if (bNorth)
3495
1
            sPro.proParams[3] = 1.0;
3496
0
        else
3497
0
            sPro.proParams[3] = -1.0;
3498
1
    }
3499
8
    else if (EQUAL(pszProjName, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
3500
0
    {
3501
0
        sPro.proNumber = EPRJ_ALBERS_CONIC_EQUAL_AREA;
3502
0
        sPro.proName = const_cast<char *>("Albers Conical Equal Area");
3503
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3504
0
        sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
3505
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3506
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3507
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3508
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3509
0
    }
3510
8
    else if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
3511
0
    {
3512
        // Not sure if Imagine has a mapping of LCC_1SP. In the mean time
3513
        // convert it to LCC_2SP
3514
0
        auto poTmpSRS = std::unique_ptr<OGRSpatialReference>(
3515
0
            oSRS.convertToOtherProjection(SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP));
3516
0
        if (poTmpSRS)
3517
0
        {
3518
0
            sPro.proNumber = EPRJ_LAMBERT_CONFORMAL_CONIC;
3519
0
            sPro.proName = const_cast<char *>("Lambert Conformal Conic");
3520
0
            sPro.proParams[2] =
3521
0
                poTmpSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3522
0
            sPro.proParams[3] =
3523
0
                poTmpSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
3524
0
            sPro.proParams[4] =
3525
0
                poTmpSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3526
0
            sPro.proParams[5] =
3527
0
                poTmpSRS->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3528
0
            sPro.proParams[6] = poTmpSRS->GetProjParm(SRS_PP_FALSE_EASTING);
3529
0
            sPro.proParams[7] = poTmpSRS->GetProjParm(SRS_PP_FALSE_NORTHING);
3530
0
        }
3531
0
    }
3532
8
    else if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
3533
0
    {
3534
0
        sPro.proNumber = EPRJ_LAMBERT_CONFORMAL_CONIC;
3535
0
        sPro.proName = const_cast<char *>("Lambert Conformal Conic");
3536
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3537
0
        sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
3538
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3539
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3540
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3541
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3542
0
    }
3543
8
    else if (EQUAL(pszProjName, SRS_PT_MERCATOR_1SP) &&
3544
0
             oSRS.GetProjParm(SRS_PP_SCALE_FACTOR) == 1.0)
3545
0
    {
3546
0
        sPro.proNumber = EPRJ_MERCATOR;
3547
0
        sPro.proName = const_cast<char *>("Mercator");
3548
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3549
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3550
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3551
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3552
0
    }
3553
8
    else if (EQUAL(pszProjName, SRS_PT_MERCATOR_1SP))
3554
0
    {
3555
0
        sPro.proNumber = EPRJ_MERCATOR_VARIANT_A;
3556
0
        sPro.proName = const_cast<char *>("Mercator (Variant A)");
3557
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3558
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3559
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR);
3560
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3561
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3562
0
    }
3563
8
    else if (EQUAL(pszProjName, SRS_PT_MERCATOR_2SP))
3564
0
    {
3565
        // Not sure if Imagine has a mapping of Mercator_2SP. In the mean time
3566
        // convert it to Mercator_1SP
3567
0
        auto poTmpSRS = std::unique_ptr<OGRSpatialReference>(
3568
0
            oSRS.convertToOtherProjection(SRS_PT_MERCATOR_1SP));
3569
0
        if (poTmpSRS)
3570
0
        {
3571
0
            sPro.proNumber = EPRJ_MERCATOR_VARIANT_A;
3572
0
            sPro.proName = const_cast<char *>("Mercator (Variant A)");
3573
0
            sPro.proParams[4] =
3574
0
                poTmpSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3575
0
            sPro.proParams[5] =
3576
0
                poTmpSRS->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3577
0
            sPro.proParams[2] = poTmpSRS->GetProjParm(SRS_PP_SCALE_FACTOR);
3578
0
            sPro.proParams[6] = poTmpSRS->GetProjParm(SRS_PP_FALSE_EASTING);
3579
0
            sPro.proParams[7] = poTmpSRS->GetProjParm(SRS_PP_FALSE_NORTHING);
3580
0
        }
3581
0
    }
3582
8
    else if (EQUAL(pszProjName, SRS_PT_KROVAK))
3583
0
    {
3584
0
        sPro.proNumber = EPRJ_KROVAK;
3585
0
        sPro.proName = const_cast<char *>("Krovak");
3586
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR);
3587
0
        sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
3588
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3589
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3590
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3591
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3592
0
        sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_PSEUDO_STD_PARALLEL_1);
3593
3594
0
        sPro.proParams[8] = 0.0;   // XY plane rotation
3595
0
        sPro.proParams[10] = 1.0;  // X scale
3596
0
        sPro.proParams[11] = 1.0;  // Y scale
3597
0
    }
3598
8
    else if (EQUAL(pszProjName, SRS_PT_POLAR_STEREOGRAPHIC))
3599
1
    {
3600
1
        sPro.proNumber = EPRJ_POLAR_STEREOGRAPHIC;
3601
1
        sPro.proName = const_cast<char *>("Polar Stereographic");
3602
1
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3603
1
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3604
        // Hopefully the scale factor is 1.0!
3605
1
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3606
1
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3607
1
    }
3608
7
    else if (EQUAL(pszProjName, SRS_PT_POLYCONIC))
3609
0
    {
3610
0
        sPro.proNumber = EPRJ_POLYCONIC;
3611
0
        sPro.proName = const_cast<char *>("Polyconic");
3612
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3613
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3614
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3615
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3616
0
    }
3617
7
    else if (EQUAL(pszProjName, SRS_PT_EQUIDISTANT_CONIC))
3618
2
    {
3619
2
        sPro.proNumber = EPRJ_EQUIDISTANT_CONIC;
3620
2
        sPro.proName = const_cast<char *>("Equidistant Conic");
3621
2
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3622
2
        sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
3623
2
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3624
2
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3625
2
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3626
2
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3627
2
        sPro.proParams[8] = 1.0;
3628
2
    }
3629
5
    else if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
3630
1
    {
3631
1
        sPro.proNumber = EPRJ_TRANSVERSE_MERCATOR;
3632
1
        sPro.proName = const_cast<char *>("Transverse Mercator");
3633
1
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3634
1
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3635
1
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3636
1
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3637
1
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3638
1
    }
3639
4
    else if (EQUAL(pszProjName, SRS_PT_STEREOGRAPHIC))
3640
0
    {
3641
0
        sPro.proNumber = EPRJ_STEREOGRAPHIC_EXTENDED;
3642
0
        sPro.proName = const_cast<char *>("Stereographic (Extended)");
3643
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3644
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3645
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3646
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3647
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3648
0
    }
3649
4
    else if (EQUAL(pszProjName, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
3650
0
    {
3651
0
        sPro.proNumber = EPRJ_LAMBERT_AZIMUTHAL_EQUAL_AREA;
3652
0
        sPro.proName = const_cast<char *>("Lambert Azimuthal Equal-area");
3653
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3654
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3655
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3656
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3657
0
    }
3658
4
    else if (EQUAL(pszProjName, SRS_PT_AZIMUTHAL_EQUIDISTANT))
3659
0
    {
3660
0
        sPro.proNumber = EPRJ_AZIMUTHAL_EQUIDISTANT;
3661
0
        sPro.proName = const_cast<char *>("Azimuthal Equidistant");
3662
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3663
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3664
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3665
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3666
0
    }
3667
4
    else if (EQUAL(pszProjName, SRS_PT_GNOMONIC))
3668
0
    {
3669
0
        sPro.proNumber = EPRJ_GNOMONIC;
3670
0
        sPro.proName = const_cast<char *>("Gnomonic");
3671
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3672
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3673
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3674
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3675
0
    }
3676
4
    else if (EQUAL(pszProjName, SRS_PT_ORTHOGRAPHIC))
3677
0
    {
3678
0
        sPro.proNumber = EPRJ_ORTHOGRAPHIC;
3679
0
        sPro.proName = const_cast<char *>("Orthographic");
3680
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3681
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3682
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3683
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3684
0
    }
3685
4
    else if (EQUAL(pszProjName, SRS_PT_SINUSOIDAL))
3686
0
    {
3687
0
        sPro.proNumber = EPRJ_SINUSOIDAL;
3688
0
        sPro.proName = const_cast<char *>("Sinusoidal");
3689
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3690
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3691
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3692
0
    }
3693
4
    else if (EQUAL(pszProjName, SRS_PT_EQUIRECTANGULAR))
3694
0
    {
3695
0
        sPro.proNumber = EPRJ_EQUIRECTANGULAR;
3696
0
        sPro.proName = const_cast<char *>("Equirectangular");
3697
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3698
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3699
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3700
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3701
0
    }
3702
4
    else if (EQUAL(pszProjName, SRS_PT_MILLER_CYLINDRICAL))
3703
0
    {
3704
0
        sPro.proNumber = EPRJ_MILLER_CYLINDRICAL;
3705
0
        sPro.proName = const_cast<char *>("Miller Cylindrical");
3706
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3707
        // Hopefully the latitude is zero!
3708
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3709
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3710
0
    }
3711
4
    else if (EQUAL(pszProjName, SRS_PT_VANDERGRINTEN))
3712
0
    {
3713
0
        sPro.proNumber = EPRJ_VANDERGRINTEN;
3714
0
        sPro.proName = const_cast<char *>("Van der Grinten");
3715
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3716
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3717
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3718
0
    }
3719
4
    else if (EQUAL(pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
3720
0
    {
3721
0
        if (oSRS.GetProjParm(SRS_PP_RECTIFIED_GRID_ANGLE) == 0.0)
3722
0
        {
3723
0
            sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR;
3724
0
            sPro.proName = const_cast<char *>("Oblique Mercator (Hotine)");
3725
0
            sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3726
0
            sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
3727
0
            sPro.proParams[4] =
3728
0
                oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3729
0
            sPro.proParams[5] =
3730
0
                oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3731
0
            sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3732
0
            sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3733
0
            sPro.proParams[12] = 1.0;
3734
0
        }
3735
0
        else
3736
0
        {
3737
0
            sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_VARIANT_A;
3738
0
            sPro.proName =
3739
0
                const_cast<char *>("Hotine Oblique Mercator (Variant A)");
3740
0
            sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3741
0
            sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
3742
0
            sPro.proParams[4] =
3743
0
                oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3744
0
            sPro.proParams[5] =
3745
0
                oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3746
0
            sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3747
0
            sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3748
0
            sPro.proParams[8] =
3749
0
                oSRS.GetProjParm(SRS_PP_RECTIFIED_GRID_ANGLE) * D2R;
3750
0
        }
3751
0
    }
3752
4
    else if (EQUAL(pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER))
3753
0
    {
3754
0
        sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER;
3755
0
        sPro.proName =
3756
0
            const_cast<char *>("Hotine Oblique Mercator Azimuth Center");
3757
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3758
0
        sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
3759
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3760
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3761
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3762
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3763
0
        sPro.proParams[12] = 1.0;
3764
0
    }
3765
4
    else if (EQUAL(pszProjName, SRS_PT_ROBINSON))
3766
0
    {
3767
0
        sPro.proNumber = EPRJ_ROBINSON;
3768
0
        sPro.proName = const_cast<char *>("Robinson");
3769
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3770
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3771
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3772
0
    }
3773
4
    else if (EQUAL(pszProjName, SRS_PT_MOLLWEIDE))
3774
0
    {
3775
0
        sPro.proNumber = EPRJ_MOLLWEIDE;
3776
0
        sPro.proName = const_cast<char *>("Mollweide");
3777
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3778
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3779
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3780
0
    }
3781
4
    else if (EQUAL(pszProjName, SRS_PT_ECKERT_I))
3782
0
    {
3783
0
        sPro.proNumber = EPRJ_ECKERT_I;
3784
0
        sPro.proName = const_cast<char *>("Eckert I");
3785
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3786
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3787
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3788
0
    }
3789
4
    else if (EQUAL(pszProjName, SRS_PT_ECKERT_II))
3790
0
    {
3791
0
        sPro.proNumber = EPRJ_ECKERT_II;
3792
0
        sPro.proName = const_cast<char *>("Eckert II");
3793
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3794
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3795
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3796
0
    }
3797
4
    else if (EQUAL(pszProjName, SRS_PT_ECKERT_III))
3798
0
    {
3799
0
        sPro.proNumber = EPRJ_ECKERT_III;
3800
0
        sPro.proName = const_cast<char *>("Eckert III");
3801
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3802
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3803
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3804
0
    }
3805
4
    else if (EQUAL(pszProjName, SRS_PT_ECKERT_IV))
3806
0
    {
3807
0
        sPro.proNumber = EPRJ_ECKERT_IV;
3808
0
        sPro.proName = const_cast<char *>("Eckert IV");
3809
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3810
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3811
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3812
0
    }
3813
4
    else if (EQUAL(pszProjName, SRS_PT_ECKERT_V))
3814
0
    {
3815
0
        sPro.proNumber = EPRJ_ECKERT_V;
3816
0
        sPro.proName = const_cast<char *>("Eckert V");
3817
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3818
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3819
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3820
0
    }
3821
4
    else if (EQUAL(pszProjName, SRS_PT_ECKERT_VI))
3822
0
    {
3823
0
        sPro.proNumber = EPRJ_ECKERT_VI;
3824
0
        sPro.proName = const_cast<char *>("Eckert VI");
3825
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3826
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3827
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3828
0
    }
3829
4
    else if (EQUAL(pszProjName, SRS_PT_GALL_STEREOGRAPHIC))
3830
0
    {
3831
0
        sPro.proNumber = EPRJ_GALL_STEREOGRAPHIC;
3832
0
        sPro.proName = const_cast<char *>("Gall Stereographic");
3833
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3834
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3835
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3836
0
    }
3837
4
    else if (EQUAL(pszProjName, SRS_PT_CASSINI_SOLDNER))
3838
0
    {
3839
0
        sPro.proNumber = EPRJ_CASSINI;
3840
0
        sPro.proName = const_cast<char *>("Cassini");
3841
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3842
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3843
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3844
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3845
0
    }
3846
4
    else if (EQUAL(pszProjName, SRS_PT_TWO_POINT_EQUIDISTANT))
3847
0
    {
3848
0
        sPro.proNumber = EPRJ_TWO_POINT_EQUIDISTANT;
3849
0
        sPro.proName = const_cast<char *>("Two_Point_Equidistant");
3850
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3851
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3852
0
        sPro.proParams[8] =
3853
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0) * D2R;
3854
0
        sPro.proParams[9] =
3855
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0) * D2R;
3856
0
        sPro.proParams[10] =
3857
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0) * D2R;
3858
0
        sPro.proParams[11] =
3859
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0) * D2R;
3860
0
    }
3861
4
    else if (EQUAL(pszProjName, SRS_PT_BONNE))
3862
0
    {
3863
0
        sPro.proNumber = EPRJ_BONNE;
3864
0
        sPro.proName = const_cast<char *>("Bonne");
3865
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3866
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3867
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3868
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3869
0
    }
3870
4
    else if (EQUAL(pszProjName, "Loximuthal"))
3871
0
    {
3872
0
        sPro.proNumber = EPRJ_LOXIMUTHAL;
3873
0
        sPro.proName = const_cast<char *>("Loximuthal");
3874
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3875
0
        sPro.proParams[5] = oSRS.GetProjParm("latitude_of_origin") * D2R;
3876
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3877
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3878
0
    }
3879
4
    else if (EQUAL(pszProjName, "Quartic_Authalic"))
3880
0
    {
3881
0
        sPro.proNumber = EPRJ_QUARTIC_AUTHALIC;
3882
0
        sPro.proName = const_cast<char *>("Quartic Authalic");
3883
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3884
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3885
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3886
0
    }
3887
4
    else if (EQUAL(pszProjName, "Winkel_I"))
3888
0
    {
3889
0
        sPro.proNumber = EPRJ_WINKEL_I;
3890
0
        sPro.proName = const_cast<char *>("Winkel I");
3891
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3892
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3893
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3894
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3895
0
    }
3896
4
    else if (EQUAL(pszProjName, "Winkel_II"))
3897
4
    {
3898
4
        sPro.proNumber = EPRJ_WINKEL_II;
3899
4
        sPro.proName = const_cast<char *>("Winkel II");
3900
4
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3901
4
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3902
4
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3903
4
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3904
4
    }
3905
0
    else if (EQUAL(pszProjName, "Behrmann"))
3906
0
    {
3907
        // Mapped to Lambert Cylindrical Equal Area in recent PROJ versions
3908
0
        sPro.proNumber = EPRJ_BEHRMANN;
3909
0
        sPro.proName = const_cast<char *>("Behrmann");
3910
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3911
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3912
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3913
0
    }
3914
0
    else if (EQUAL(pszProjName, "Equidistant_Cylindrical"))
3915
0
    {
3916
        // Dead code path. Mapped to Equirectangular
3917
0
        sPro.proNumber = EPRJ_EQUIDISTANT_CYLINDRICAL;
3918
0
        sPro.proName = const_cast<char *>("Equidistant_Cylindrical");
3919
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3920
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3921
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3922
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3923
0
    }
3924
0
    else if (EQUAL(pszProjName, SRS_PT_KROVAK))
3925
0
    {
3926
0
        sPro.proNumber = EPRJ_KROVAK;
3927
0
        sPro.proName = const_cast<char *>("Krovak");
3928
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3929
0
        sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
3930
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
3931
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
3932
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3933
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3934
0
        sPro.proParams[8] = oSRS.GetProjParm("XY_Plane_Rotation", 0.0) * D2R;
3935
0
        sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3936
0
        sPro.proParams[10] = oSRS.GetProjParm("X_Scale", 1.0);
3937
0
        sPro.proParams[11] = oSRS.GetProjParm("Y_Scale", 1.0);
3938
0
    }
3939
0
    else if (EQUAL(pszProjName, "Double_Stereographic"))
3940
0
    {
3941
0
        sPro.proNumber = EPRJ_DOUBLE_STEREOGRAPHIC;
3942
0
        sPro.proName = const_cast<char *>("Double_Stereographic");
3943
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
3944
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3945
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
3946
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3947
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3948
0
    }
3949
0
    else if (EQUAL(pszProjName, "Aitoff"))
3950
0
    {
3951
0
        sPro.proNumber = EPRJ_AITOFF;
3952
0
        sPro.proName = const_cast<char *>("Aitoff");
3953
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3954
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3955
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3956
0
    }
3957
0
    else if (EQUAL(pszProjName, "Craster_Parabolic"))
3958
0
    {
3959
0
        sPro.proNumber = EPRJ_CRASTER_PARABOLIC;
3960
0
        sPro.proName = const_cast<char *>("Craster_Parabolic");
3961
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3962
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3963
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3964
0
    }
3965
0
    else if (EQUAL(pszProjName, SRS_PT_CYLINDRICAL_EQUAL_AREA))
3966
0
    {
3967
0
        sPro.proNumber = EPRJ_CYLINDRICAL_EQUAL_AREA;
3968
0
        sPro.proName = const_cast<char *>("Cylindrical_Equal_Area");
3969
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3970
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3971
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3972
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3973
0
    }
3974
0
    else if (EQUAL(pszProjName, "Flat_Polar_Quartic"))
3975
0
    {
3976
0
        sPro.proNumber = EPRJ_FLAT_POLAR_QUARTIC;
3977
0
        sPro.proName = const_cast<char *>("Flat_Polar_Quartic");
3978
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3979
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3980
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3981
0
    }
3982
0
    else if (EQUAL(pszProjName, "Times"))
3983
0
    {
3984
0
        sPro.proNumber = EPRJ_TIMES;
3985
0
        sPro.proName = const_cast<char *>("Times");
3986
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3987
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3988
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3989
0
    }
3990
0
    else if (EQUAL(pszProjName, "Winkel_Tripel"))
3991
0
    {
3992
0
        sPro.proNumber = EPRJ_WINKEL_TRIPEL;
3993
0
        sPro.proName = const_cast<char *>("Winkel_Tripel");
3994
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
3995
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
3996
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
3997
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
3998
0
    }
3999
0
    else if (EQUAL(pszProjName, "Hammer_Aitoff"))
4000
0
    {
4001
0
        sPro.proNumber = EPRJ_HAMMER_AITOFF;
4002
0
        sPro.proName = const_cast<char *>("Hammer_Aitoff");
4003
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
4004
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
4005
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
4006
0
    }
4007
0
    else if (
4008
0
        EQUAL(pszProjName,
4009
0
              "Vertical_Near_Side_Perspective"))  // ESRI WKT, before PROJ 6.3.0
4010
0
    {
4011
0
        sPro.proNumber = EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE;
4012
0
        sPro.proName = const_cast<char *>("Vertical_Near_Side_Perspective");
4013
0
        sPro.proParams[2] = oSRS.GetProjParm("Height");
4014
0
        sPro.proParams[4] =
4015
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER, 75.0) * D2R;
4016
0
        sPro.proParams[5] =
4017
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0) * D2R;
4018
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
4019
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
4020
0
    }
4021
0
    else if (EQUAL(pszProjName,
4022
0
                   "Vertical Perspective"))  // WKT2, starting with PROJ 6.3.0
4023
0
    {
4024
0
        sPro.proNumber = EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE;
4025
0
        sPro.proName = const_cast<char *>("Vertical_Near_Side_Perspective");
4026
0
        sPro.proParams[2] = oSRS.GetProjParm("Viewpoint height");
4027
0
        sPro.proParams[4] =
4028
0
            oSRS.GetProjParm("Longitude of topocentric origin", 75.0) * D2R;
4029
0
        sPro.proParams[5] =
4030
0
            oSRS.GetProjParm("Latitude of topocentric origin", 40.0) * D2R;
4031
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
4032
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
4033
0
    }
4034
0
    else if (EQUAL(pszProjName, "Hotine_Oblique_Mercator_Two_Point_Center"))
4035
0
    {
4036
0
        sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_CENTER;
4037
0
        sPro.proName =
4038
0
            const_cast<char *>("Hotine_Oblique_Mercator_Two_Point_Center");
4039
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
4040
0
        sPro.proParams[5] =
4041
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0) * D2R;
4042
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
4043
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
4044
0
        sPro.proParams[8] =
4045
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0) * D2R;
4046
0
        sPro.proParams[9] =
4047
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0) * D2R;
4048
0
        sPro.proParams[10] =
4049
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0) * D2R;
4050
0
        sPro.proParams[11] =
4051
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0) * D2R;
4052
0
    }
4053
0
    else if (EQUAL(pszProjName,
4054
0
                   SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN))
4055
0
    {
4056
0
        sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN;
4057
0
        sPro.proName = const_cast<char *>(
4058
0
            "Hotine_Oblique_Mercator_Two_Point_Natural_Origin");
4059
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
4060
0
        sPro.proParams[5] =
4061
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0) * D2R;
4062
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
4063
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
4064
0
        sPro.proParams[8] =
4065
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0) * D2R;
4066
0
        sPro.proParams[9] =
4067
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0) * D2R;
4068
0
        sPro.proParams[10] =
4069
0
            oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0) * D2R;
4070
0
        sPro.proParams[11] =
4071
0
            oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0) * D2R;
4072
0
    }
4073
0
    else if (EQUAL(pszProjName, "New_Zealand_Map_Grid"))
4074
0
    {
4075
0
        sPro.proType = EPRJ_EXTERNAL;
4076
0
        sPro.proNumber = 0;
4077
0
        sPro.proExeName = const_cast<char *>(EPRJ_EXTERNAL_NZMG);
4078
0
        sPro.proName = const_cast<char *>("New Zealand Map Grid");
4079
0
        sPro.proZone = 0;
4080
0
        sPro.proParams[0] = 0;  // False easting etc not stored in .img it seems
4081
0
        sPro.proParams[1] = 0;  // always fixed by definition.
4082
0
        sPro.proParams[2] = 0;
4083
0
        sPro.proParams[3] = 0;
4084
0
        sPro.proParams[4] = 0;
4085
0
        sPro.proParams[5] = 0;
4086
0
        sPro.proParams[6] = 0;
4087
0
        sPro.proParams[7] = 0;
4088
0
    }
4089
0
    else if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED))
4090
0
    {
4091
0
        sPro.proNumber = EPRJ_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED;
4092
0
        sPro.proName =
4093
0
            const_cast<char *>("Transverse Mercator (South Orientated)");
4094
0
        sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
4095
0
        sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
4096
0
        sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
4097
0
        sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
4098
0
        sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
4099
0
    }
4100
4101
    // Anything we can't map, we store as an ESRI PE_STRING.
4102
0
    else if (oSRS.IsProjected() || oSRS.IsGeographic())
4103
0
    {
4104
0
        if (!bPEStringStored)
4105
0
        {
4106
0
            char *pszPEString = nullptr;
4107
0
            const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
4108
0
            oSRS.exportToWkt(&pszPEString, apszOptions);
4109
            // Need to transform this into ESRI format.
4110
0
            HFASetPEString(hHFA, pszPEString);
4111
0
            CPLFree(pszPEString);
4112
0
            bPEStringStored = true;
4113
0
        }
4114
0
    }
4115
0
    else
4116
0
    {
4117
0
        CPLError(CE_Warning, CPLE_NotSupported,
4118
0
                 "Projection %s not supported for translation to Imagine.",
4119
0
                 pszProjName);
4120
0
    }
4121
4122
    // MapInfo
4123
50
    const char *pszPROJCS = oSRS.GetAttrValue("PROJCS");
4124
4125
50
    if (pszPROJCS)
4126
10
        sMapInfo.proName = (char *)pszPROJCS;
4127
40
    else if (bHaveSRS && sPro.proName != nullptr)
4128
23
        sMapInfo.proName = sPro.proName;
4129
17
    else
4130
17
        sMapInfo.proName = const_cast<char *>("Unknown");
4131
4132
50
    sMapInfo.upperLeftCenter.x = m_gt[0] + m_gt[1] * 0.5;
4133
50
    sMapInfo.upperLeftCenter.y = m_gt[3] + m_gt[5] * 0.5;
4134
4135
50
    sMapInfo.lowerRightCenter.x = m_gt[0] + m_gt[1] * (GetRasterXSize() - 0.5);
4136
50
    sMapInfo.lowerRightCenter.y = m_gt[3] + m_gt[5] * (GetRasterYSize() - 0.5);
4137
4138
50
    sMapInfo.pixelSize.width = std::abs(m_gt[1]);
4139
50
    sMapInfo.pixelSize.height = std::abs(m_gt[5]);
4140
4141
    // Handle units.  Try to match up with a known name.
4142
50
    sMapInfo.units = const_cast<char *>("meters");
4143
4144
50
    if (bHaveSRS && oSRS.IsGeographic())
4145
23
        sMapInfo.units = const_cast<char *>("dd");
4146
27
    else if (bHaveSRS && oSRS.GetLinearUnits() != 1.0)
4147
0
    {
4148
0
        double dfClosestDiff = 100.0;
4149
0
        int iClosest = -1;
4150
0
        const char *pszUnitName = nullptr;
4151
0
        const double dfActualSize = oSRS.GetLinearUnits(&pszUnitName);
4152
4153
0
        const char *const *papszUnitMap = HFAGetUnitMap();
4154
0
        for (int iUnit = 0; papszUnitMap[iUnit] != nullptr; iUnit += 2)
4155
0
        {
4156
0
            if (fabs(CPLAtof(papszUnitMap[iUnit + 1]) - dfActualSize) <
4157
0
                dfClosestDiff)
4158
0
            {
4159
0
                iClosest = iUnit;
4160
0
                dfClosestDiff =
4161
0
                    fabs(CPLAtof(papszUnitMap[iUnit + 1]) - dfActualSize);
4162
0
            }
4163
0
        }
4164
4165
0
        if (iClosest == -1 || fabs(dfClosestDiff / dfActualSize) > 0.0001)
4166
0
        {
4167
0
            CPLError(CE_Warning, CPLE_NotSupported,
4168
0
                     "Unable to identify Erdas units matching %s/%gm, "
4169
0
                     "output units will be wrong.",
4170
0
                     pszUnitName, dfActualSize);
4171
0
        }
4172
0
        else
4173
0
        {
4174
0
            sMapInfo.units = (char *)papszUnitMap[iClosest];
4175
0
        }
4176
4177
        // We need to convert false easting and northing to meters.
4178
0
        sPro.proParams[6] *= dfActualSize;
4179
0
        sPro.proParams[7] *= dfActualSize;
4180
0
    }
4181
4182
    // Write out definitions.
4183
50
    if (m_gt[2] == 0.0 && m_gt[4] == 0.0)
4184
50
    {
4185
50
        HFASetMapInfo(hHFA, &sMapInfo);
4186
50
    }
4187
0
    else
4188
0
    {
4189
0
        HFASetGeoTransform(hHFA, sMapInfo.proName, sMapInfo.units, m_gt.data());
4190
0
    }
4191
4192
50
    if (bHaveSRS && sPro.proName != nullptr)
4193
32
    {
4194
32
        HFASetProParameters(hHFA, &sPro);
4195
32
        HFASetDatum(hHFA, &sDatum);
4196
4197
32
        if (!bPEStringStored)
4198
0
            HFASetPEString(hHFA, "");
4199
32
    }
4200
18
    else if (!bPEStringStored)
4201
18
    {
4202
18
        ClearSR(hHFA);
4203
18
    }
4204
4205
50
    if (poGeogSRS != nullptr)
4206
33
        delete poGeogSRS;
4207
4208
50
    return CE_None;
4209
50
}
4210
4211
/************************************************************************/
4212
/*                       WritePeStringIfNeeded()                        */
4213
/************************************************************************/
4214
int WritePeStringIfNeeded(const OGRSpatialReference *poSRS, HFAHandle hHFA)
4215
33
{
4216
33
    if (!poSRS || !hHFA)
4217
0
        return FALSE;
4218
4219
33
    const char *pszGEOGCS = poSRS->GetAttrValue("GEOGCS");
4220
33
    if (pszGEOGCS == nullptr)
4221
5
        pszGEOGCS = "";
4222
4223
33
    const char *pszDatum = poSRS->GetAttrValue("DATUM");
4224
33
    if (pszDatum == nullptr)
4225
1
        pszDatum = "";
4226
4227
    // The strlen() checks are just there to make Coverity happy because it
4228
    // doesn't seem to realize that STARTS_WITH() success implies them.
4229
33
    const size_t gcsNameOffset =
4230
33
        (strlen(pszGEOGCS) > strlen("GCS_") && STARTS_WITH(pszGEOGCS, "GCS_"))
4231
33
            ? strlen("GCS_")
4232
33
            : 0;
4233
4234
33
    const size_t datumNameOffset =
4235
33
        (strlen(pszDatum) > strlen("D_") && STARTS_WITH(pszDatum, "D_"))
4236
33
            ? strlen("D_")
4237
33
            : 0;
4238
4239
33
    bool ret = false;
4240
33
    if (CPLString(pszGEOGCS + gcsNameOffset).replaceAll(' ', '_').tolower() !=
4241
33
        CPLString(pszDatum + datumNameOffset).replaceAll(' ', '_').tolower())
4242
32
    {
4243
32
        ret = true;
4244
32
    }
4245
1
    else
4246
1
    {
4247
1
        const char *name = poSRS->GetAttrValue("PRIMEM");
4248
1
        if (name && !EQUAL(name, "Greenwich"))
4249
0
            ret = true;
4250
4251
1
        if (!ret)
4252
1
        {
4253
1
            const OGR_SRSNode *poAUnits = poSRS->GetAttrNode("GEOGCS|UNIT");
4254
1
            const OGR_SRSNode *poChild =
4255
1
                poAUnits == nullptr ? nullptr : poAUnits->GetChild(0);
4256
1
            name = poChild == nullptr ? nullptr : poChild->GetValue();
4257
1
            if (name && !EQUAL(name, "Degree"))
4258
0
                ret = true;
4259
1
        }
4260
1
        if (!ret)
4261
1
        {
4262
1
            name = poSRS->GetAttrValue("UNIT");
4263
1
            if (name)
4264
0
            {
4265
0
                ret = true;
4266
0
                const char *const *papszUnitMap = HFAGetUnitMap();
4267
0
                for (int i = 0; papszUnitMap[i] != nullptr; i += 2)
4268
0
                    if (EQUAL(name, papszUnitMap[i]))
4269
0
                        ret = false;
4270
0
            }
4271
1
        }
4272
1
        if (!ret)
4273
1
        {
4274
1
            const int nGCS = poSRS->GetEPSGGeogCS();
4275
1
            switch (nGCS)
4276
1
            {
4277
0
                case 4326:
4278
0
                    if (!EQUAL(pszDatum + datumNameOffset, "WGS_84"))
4279
0
                        ret = true;
4280
0
                    break;
4281
0
                case 4322:
4282
0
                    if (!EQUAL(pszDatum + datumNameOffset, "WGS_72"))
4283
0
                        ret = true;
4284
0
                    break;
4285
0
                case 4267:
4286
0
                    if (!EQUAL(pszDatum + datumNameOffset,
4287
0
                               "North_America_1927"))
4288
0
                        ret = true;
4289
0
                    break;
4290
0
                case 4269:
4291
0
                    if (!EQUAL(pszDatum + datumNameOffset,
4292
0
                               "North_America_1983"))
4293
0
                        ret = true;
4294
0
                    break;
4295
1
            }
4296
1
        }
4297
1
    }
4298
33
    if (ret)
4299
32
    {
4300
32
        char *pszPEString = nullptr;
4301
32
        OGRSpatialReference oSRSForESRI(*poSRS);
4302
32
        oSRSForESRI.morphToESRI();
4303
32
        oSRSForESRI.exportToWkt(&pszPEString);
4304
32
        HFASetPEString(hHFA, pszPEString);
4305
32
        CPLFree(pszPEString);
4306
32
    }
4307
4308
33
    return ret;
4309
33
}
4310
4311
/************************************************************************/
4312
/*                              ClearSR()                               */
4313
/************************************************************************/
4314
void ClearSR(HFAHandle hHFA)
4315
18
{
4316
10.7k
    for (int iBand = 0; iBand < hHFA->nBands; iBand++)
4317
10.7k
    {
4318
10.7k
        HFAEntry *poMIEntry = nullptr;
4319
10.7k
        if (hHFA->papoBand[iBand]->poNode &&
4320
10.7k
            (poMIEntry = hHFA->papoBand[iBand]->poNode->GetNamedChild(
4321
10.7k
                 "Projection")) != nullptr)
4322
0
        {
4323
0
            poMIEntry->MarkDirty();
4324
0
            poMIEntry->SetIntField("proType", 0);
4325
0
            poMIEntry->SetIntField("proNumber", 0);
4326
0
            poMIEntry->SetStringField("proExeName", "");
4327
0
            poMIEntry->SetStringField("proName", "");
4328
0
            poMIEntry->SetIntField("proZone", 0);
4329
0
            poMIEntry->SetDoubleField("proParams[0]", 0.0);
4330
0
            poMIEntry->SetDoubleField("proParams[1]", 0.0);
4331
0
            poMIEntry->SetDoubleField("proParams[2]", 0.0);
4332
0
            poMIEntry->SetDoubleField("proParams[3]", 0.0);
4333
0
            poMIEntry->SetDoubleField("proParams[4]", 0.0);
4334
0
            poMIEntry->SetDoubleField("proParams[5]", 0.0);
4335
0
            poMIEntry->SetDoubleField("proParams[6]", 0.0);
4336
0
            poMIEntry->SetDoubleField("proParams[7]", 0.0);
4337
0
            poMIEntry->SetDoubleField("proParams[8]", 0.0);
4338
0
            poMIEntry->SetDoubleField("proParams[9]", 0.0);
4339
0
            poMIEntry->SetDoubleField("proParams[10]", 0.0);
4340
0
            poMIEntry->SetDoubleField("proParams[11]", 0.0);
4341
0
            poMIEntry->SetDoubleField("proParams[12]", 0.0);
4342
0
            poMIEntry->SetDoubleField("proParams[13]", 0.0);
4343
0
            poMIEntry->SetDoubleField("proParams[14]", 0.0);
4344
0
            poMIEntry->SetStringField("proSpheroid.sphereName", "");
4345
0
            poMIEntry->SetDoubleField("proSpheroid.a", 0.0);
4346
0
            poMIEntry->SetDoubleField("proSpheroid.b", 0.0);
4347
0
            poMIEntry->SetDoubleField("proSpheroid.eSquared", 0.0);
4348
0
            poMIEntry->SetDoubleField("proSpheroid.radius", 0.0);
4349
0
            HFAEntry *poDatumEntry = poMIEntry->GetNamedChild("Datum");
4350
0
            if (poDatumEntry != nullptr)
4351
0
            {
4352
0
                poDatumEntry->MarkDirty();
4353
0
                poDatumEntry->SetStringField("datumname", "");
4354
0
                poDatumEntry->SetIntField("type", 0);
4355
0
                poDatumEntry->SetDoubleField("params[0]", 0.0);
4356
0
                poDatumEntry->SetDoubleField("params[1]", 0.0);
4357
0
                poDatumEntry->SetDoubleField("params[2]", 0.0);
4358
0
                poDatumEntry->SetDoubleField("params[3]", 0.0);
4359
0
                poDatumEntry->SetDoubleField("params[4]", 0.0);
4360
0
                poDatumEntry->SetDoubleField("params[5]", 0.0);
4361
0
                poDatumEntry->SetDoubleField("params[6]", 0.0);
4362
0
                poDatumEntry->SetStringField("gridname", "");
4363
0
            }
4364
0
            poMIEntry->FlushToDisk();
4365
0
            char *peStr = HFAGetPEString(hHFA);
4366
0
            if (peStr != nullptr && strlen(peStr) > 0)
4367
0
                HFASetPEString(hHFA, "");
4368
0
        }
4369
10.7k
    }
4370
18
}
4371
4372
/************************************************************************/
4373
/*                           ReadProjection()                           */
4374
/************************************************************************/
4375
4376
CPLErr HFADataset::ReadProjection()
4377
4378
8.87k
{
4379
    // General case for Erdas style projections.
4380
    //
4381
    // We make a particular effort to adapt the mapinfo->proname as
4382
    // the PROJCS[] name per #2422.
4383
8.87k
    const Eprj_Datum *psDatum = HFAGetDatum(hHFA);
4384
8.87k
    const Eprj_ProParameters *psPro = HFAGetProParameters(hHFA);
4385
8.87k
    const Eprj_MapInfo *psMapInfo = HFAGetMapInfo(hHFA);
4386
4387
8.87k
    HFAEntry *poMapInformation = nullptr;
4388
8.87k
    if (psMapInfo == nullptr)
4389
5.79k
    {
4390
5.79k
        HFABand *poBand = hHFA->papoBand[0];
4391
5.79k
        poMapInformation = poBand->poNode->GetNamedChild("MapInformation");
4392
5.79k
    }
4393
4394
8.87k
    m_oSRS.Clear();
4395
4396
8.87k
    if (psMapInfo == nullptr && poMapInformation == nullptr)
4397
2.57k
    {
4398
2.57k
        return CE_None;
4399
2.57k
    }
4400
6.29k
    else if (((!psDatum || strlen(psDatum->datumname) == 0 ||
4401
705
               EQUAL(psDatum->datumname, "Unknown")) &&
4402
5.58k
              (!psPro || strlen(psPro->proName) == 0 ||
4403
851
               EQUAL(psPro->proName, "Unknown")) &&
4404
4.73k
              (psMapInfo && (strlen(psMapInfo->proName) == 0 ||
4405
863
                             EQUAL(psMapInfo->proName, "Unknown"))) &&
4406
919
              (!psPro || psPro->proZone == 0)))
4407
380
    {
4408
        // It is not clear if Erdas Imagine would recognize a ESRI_PE string
4409
        // alone, but versions of GDAL between 3.0 and 3.6.3 have written CRS
4410
        // using for example the Vertical Projection with a ESRI_PE string only.
4411
380
        char *pszPE_COORDSYS = HFAGetPEString(hHFA);
4412
380
        OGRSpatialReference oSRSFromPE;
4413
380
        oSRSFromPE.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4414
380
        if (pszPE_COORDSYS != nullptr && strlen(pszPE_COORDSYS) > 0 &&
4415
            // Config option for testing purposes only/mostly
4416
283
            CPLTestBool(CPLGetConfigOption("HFA_USE_ESRI_PE_STRING", "YES")) &&
4417
283
            oSRSFromPE.importFromWkt(pszPE_COORDSYS) == OGRERR_NONE)
4418
190
        {
4419
190
            const char *pszProjName =
4420
190
                oSRSFromPE.GetAttrValue("PROJCS|PROJECTION");
4421
190
            if (pszProjName &&
4422
124
                (EQUAL(pszProjName, "Vertical Perspective") ||
4423
121
                 EQUAL(pszProjName, "Vertical_Near_Side_Perspective")) &&
4424
5
                CPLTestBool(CPLGetConfigOption(
4425
5
                    "HFA_SHOW_ESRI_PE_STRING_ONLY_WARNING", "YES")))
4426
5
            {
4427
5
                CPLError(CE_Warning, CPLE_AppDefined,
4428
5
                         "A ESRI_PE string encoding a CRS has been found for "
4429
5
                         "projection method %s, but no corresponding "
4430
5
                         "Eprj_ProParameters are present. This file has likely "
4431
5
                         "been generated by GDAL >= 3.0 and <= 3.6.2. It is "
4432
5
                         "recommended to recreate it, e.g with gdal_translate, "
4433
5
                         "with GDAL >= 3.6.3. This warning can be suppressed "
4434
5
                         "by setting the HFA_SHOW_ESRI_PE_STRING_ONLY_WARNING "
4435
5
                         "configuration option to NO.",
4436
5
                         pszProjName);
4437
5
            }
4438
190
            m_oSRS = std::move(oSRSFromPE);
4439
190
        }
4440
380
        CPLFree(pszPE_COORDSYS);
4441
380
        return m_oSRS.IsEmpty() ? CE_Failure : CE_None;
4442
380
    }
4443
4444
5.91k
    auto poSRS = HFAPCSStructToOSR(psDatum, psPro, psMapInfo, poMapInformation);
4445
5.91k
    if (poSRS)
4446
2.97k
        m_oSRS = *poSRS;
4447
4448
    // If we got a valid projection and managed to identify a EPSG code,
4449
    // then do not use the ESRI PE String.
4450
5.91k
    const bool bTryReadingPEString =
4451
5.91k
        poSRS == nullptr || poSRS->GetAuthorityCode(nullptr) == nullptr;
4452
4453
    // Special logic for PE string in ProjectionX node.
4454
5.91k
    char *pszPE_COORDSYS = nullptr;
4455
5.91k
    if (bTryReadingPEString)
4456
5.74k
        pszPE_COORDSYS = HFAGetPEString(hHFA);
4457
4458
5.91k
    OGRSpatialReference oSRSFromPE;
4459
5.91k
    oSRSFromPE.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4460
5.91k
    if (pszPE_COORDSYS != nullptr && strlen(pszPE_COORDSYS) > 0 &&
4461
        // Config option for testing purposes only/mostly
4462
3.19k
        CPLTestBool(CPLGetConfigOption("HFA_USE_ESRI_PE_STRING", "YES")) &&
4463
3.19k
        oSRSFromPE.importFromWkt(pszPE_COORDSYS) == OGRERR_NONE)
4464
625
    {
4465
625
        m_oSRS = std::move(oSRSFromPE);
4466
4467
        // Copy TOWGS84 clause from HFA SRS to PE SRS.
4468
625
        if (poSRS != nullptr)
4469
183
        {
4470
183
            double adfCoeffs[7];
4471
183
            double adfCoeffsUnused[7];
4472
183
            if (poSRS->GetTOWGS84(adfCoeffs, 7) == OGRERR_NONE &&
4473
27
                m_oSRS.GetTOWGS84(adfCoeffsUnused, 7) == OGRERR_FAILURE)
4474
27
            {
4475
27
                m_oSRS.SetTOWGS84(adfCoeffs[0], adfCoeffs[1], adfCoeffs[2],
4476
27
                                  adfCoeffs[3], adfCoeffs[4], adfCoeffs[5],
4477
27
                                  adfCoeffs[6]);
4478
27
            }
4479
183
        }
4480
625
    }
4481
4482
5.91k
    CPLFree(pszPE_COORDSYS);
4483
4484
5.91k
    return m_oSRS.IsEmpty() ? CE_Failure : CE_None;
4485
8.87k
}
4486
4487
/************************************************************************/
4488
/*                          IBuildOverviews()                           */
4489
/************************************************************************/
4490
4491
CPLErr HFADataset::IBuildOverviews(const char *pszResampling, int nOverviews,
4492
                                   const int *panOverviewList, int nListBands,
4493
                                   const int *panBandList,
4494
                                   GDALProgressFunc pfnProgress,
4495
                                   void *pProgressData,
4496
                                   CSLConstList papszOptions)
4497
4498
0
{
4499
0
    if (GetAccess() == GA_ReadOnly)
4500
0
    {
4501
0
        for (int i = 0; i < nListBands; i++)
4502
0
        {
4503
0
            if (HFAGetOverviewCount(hHFA, panBandList[i]) > 0)
4504
0
            {
4505
0
                CPLError(CE_Failure, CPLE_NotSupported,
4506
0
                         "Cannot add external overviews when there are already "
4507
0
                         "internal overviews");
4508
0
                return CE_Failure;
4509
0
            }
4510
0
        }
4511
4512
0
        return GDALDataset::IBuildOverviews(
4513
0
            pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
4514
0
            pfnProgress, pProgressData, papszOptions);
4515
0
    }
4516
4517
0
    for (int i = 0; i < nListBands; i++)
4518
0
    {
4519
0
        void *pScaledProgressData = GDALCreateScaledProgress(
4520
0
            i * 1.0 / nListBands, (i + 1) * 1.0 / nListBands, pfnProgress,
4521
0
            pProgressData);
4522
4523
0
        GDALRasterBand *poBand = GetRasterBand(panBandList[i]);
4524
4525
        // GetRasterBand can return NULL.
4526
0
        if (poBand == nullptr)
4527
0
        {
4528
0
            CPLError(CE_Failure, CPLE_ObjectNull, "GetRasterBand failed");
4529
0
            GDALDestroyScaledProgress(pScaledProgressData);
4530
0
            return CE_Failure;
4531
0
        }
4532
4533
0
        const CPLErr eErr = poBand->BuildOverviews(
4534
0
            pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
4535
0
            pScaledProgressData, papszOptions);
4536
4537
0
        GDALDestroyScaledProgress(pScaledProgressData);
4538
4539
0
        if (eErr != CE_None)
4540
0
            return eErr;
4541
0
    }
4542
4543
0
    return CE_None;
4544
0
}
4545
4546
/************************************************************************/
4547
/*                              Identify()                              */
4548
/************************************************************************/
4549
4550
int HFADataset::Identify(GDALOpenInfo *poOpenInfo)
4551
4552
581k
{
4553
    // Verify that this is a HFA file.
4554
581k
    if (poOpenInfo->nHeaderBytes < 15 ||
4555
99.3k
        !STARTS_WITH_CI((char *)poOpenInfo->pabyHeader, "EHFA_HEADER_TAG"))
4556
557k
        return FALSE;
4557
4558
23.5k
    return TRUE;
4559
581k
}
4560
4561
/************************************************************************/
4562
/*                                Open()                                */
4563
/************************************************************************/
4564
4565
GDALDataset *HFADataset::Open(GDALOpenInfo *poOpenInfo)
4566
4567
23.5k
{
4568
    // Verify that this is a HFA file.
4569
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
4570
    // During fuzzing, do not use Identify to reject crazy content.
4571
    if (!Identify(poOpenInfo))
4572
        return nullptr;
4573
#endif
4574
4575
    // Open the file.
4576
23.5k
    HFAHandle hHFA = HFAOpen(poOpenInfo->pszFilename,
4577
23.5k
                             (poOpenInfo->eAccess == GA_Update ? "r+" : "r"));
4578
4579
23.5k
    if (hHFA == nullptr)
4580
10.8k
        return nullptr;
4581
4582
    // Create a corresponding GDALDataset.
4583
12.7k
    HFADataset *poDS = new HFADataset();
4584
4585
12.7k
    poDS->hHFA = hHFA;
4586
12.7k
    poDS->eAccess = poOpenInfo->eAccess;
4587
4588
    // Establish raster info.
4589
12.7k
    HFAGetRasterInfo(hHFA, &poDS->nRasterXSize, &poDS->nRasterYSize,
4590
12.7k
                     &poDS->nBands);
4591
4592
12.7k
    if (poDS->nBands == 0)
4593
3.85k
    {
4594
3.85k
        delete poDS;
4595
3.85k
        CPLError(CE_Failure, CPLE_AppDefined,
4596
3.85k
                 "Unable to open %s, it has zero usable bands.",
4597
3.85k
                 poOpenInfo->pszFilename);
4598
3.85k
        return nullptr;
4599
3.85k
    }
4600
4601
8.87k
    if (poDS->nRasterXSize == 0 || poDS->nRasterYSize == 0)
4602
0
    {
4603
0
        delete poDS;
4604
0
        CPLError(CE_Failure, CPLE_AppDefined,
4605
0
                 "Unable to open %s, it has no pixels.",
4606
0
                 poOpenInfo->pszFilename);
4607
0
        return nullptr;
4608
0
    }
4609
4610
    // Get geotransform, or if that fails, try to find XForms to
4611
    // build gcps, and metadata.
4612
8.87k
    if (!HFAGetGeoTransform(hHFA, poDS->m_gt.data()))
4613
5.78k
    {
4614
5.78k
        Efga_Polynomial *pasPolyListForward = nullptr;
4615
5.78k
        Efga_Polynomial *pasPolyListReverse = nullptr;
4616
5.78k
        const int nStepCount =
4617
5.78k
            HFAReadXFormStack(hHFA, &pasPolyListForward, &pasPolyListReverse);
4618
4619
5.78k
        if (nStepCount > 0)
4620
14
        {
4621
14
            poDS->UseXFormStack(nStepCount, pasPolyListForward,
4622
14
                                pasPolyListReverse);
4623
14
            CPLFree(pasPolyListForward);
4624
14
            CPLFree(pasPolyListReverse);
4625
14
        }
4626
5.78k
    }
4627
4628
8.87k
    poDS->ReadProjection();
4629
4630
8.87k
    char **papszCM = HFAReadCameraModel(hHFA);
4631
4632
8.87k
    if (papszCM != nullptr)
4633
0
    {
4634
0
        poDS->SetMetadata(papszCM, "CAMERA_MODEL");
4635
0
        CSLDestroy(papszCM);
4636
0
    }
4637
4638
37.8k
    for (int i = 0; i < poDS->nBands; i++)
4639
28.9k
    {
4640
28.9k
        poDS->SetBand(i + 1, new HFARasterBand(poDS, i + 1, -1));
4641
28.9k
    }
4642
4643
    // Collect GDAL custom Metadata, and "auxiliary" metadata from
4644
    // well known HFA structures for the bands.  We defer this till
4645
    // now to ensure that the bands are properly setup before
4646
    // interacting with PAM.
4647
37.8k
    for (int i = 0; i < poDS->nBands; i++)
4648
28.9k
    {
4649
28.9k
        HFARasterBand *poBand =
4650
28.9k
            static_cast<HFARasterBand *>(poDS->GetRasterBand(i + 1));
4651
4652
28.9k
        char **papszMD = HFAGetMetadata(hHFA, i + 1);
4653
28.9k
        if (papszMD != nullptr)
4654
238
        {
4655
238
            poBand->SetMetadata(papszMD);
4656
238
            CSLDestroy(papszMD);
4657
238
        }
4658
4659
28.9k
        poBand->ReadAuxMetadata();
4660
28.9k
        poBand->ReadHistogramMetadata();
4661
28.9k
    }
4662
4663
    // Check for GDAL style metadata.
4664
8.87k
    char **papszMD = HFAGetMetadata(hHFA, 0);
4665
8.87k
    if (papszMD != nullptr)
4666
401
    {
4667
401
        poDS->SetMetadata(papszMD);
4668
401
        CSLDestroy(papszMD);
4669
401
    }
4670
4671
    // Read the elevation metadata, if present.
4672
37.8k
    for (int iBand = 0; iBand < poDS->nBands; iBand++)
4673
28.9k
    {
4674
28.9k
        HFARasterBand *poBand =
4675
28.9k
            static_cast<HFARasterBand *>(poDS->GetRasterBand(iBand + 1));
4676
28.9k
        const char *pszEU = HFAReadElevationUnit(hHFA, iBand);
4677
4678
28.9k
        if (pszEU != nullptr)
4679
26
        {
4680
26
            poBand->SetUnitType(pszEU);
4681
26
            if (poDS->nBands == 1)
4682
26
            {
4683
26
                poDS->SetMetadataItem("ELEVATION_UNITS", pszEU);
4684
26
            }
4685
26
        }
4686
28.9k
    }
4687
4688
    // Check for dependent dataset value.
4689
8.87k
    HFAInfo_t *psInfo = hHFA;
4690
8.87k
    HFAEntry *poEntry = psInfo->poRoot->GetNamedChild("DependentFile");
4691
8.87k
    if (poEntry != nullptr)
4692
245
    {
4693
245
        poDS->SetMetadataItem("HFA_DEPENDENT_FILE",
4694
245
                              poEntry->GetStringField("dependent.string"),
4695
245
                              "HFA");
4696
245
    }
4697
4698
    // Initialize any PAM information.
4699
8.87k
    poDS->SetDescription(poOpenInfo->pszFilename);
4700
8.87k
    poDS->TryLoadXML();
4701
4702
    // Check for external overviews.
4703
8.87k
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
4704
4705
    // Clear dirty metadata flags.
4706
37.8k
    for (int i = 0; i < poDS->nBands; i++)
4707
28.9k
    {
4708
28.9k
        HFARasterBand *poBand =
4709
28.9k
            static_cast<HFARasterBand *>(poDS->GetRasterBand(i + 1));
4710
28.9k
        poBand->bMetadataDirty = false;
4711
28.9k
    }
4712
8.87k
    poDS->bMetadataDirty = false;
4713
4714
8.87k
    return poDS;
4715
8.87k
}
4716
4717
/************************************************************************/
4718
/*                          GetSpatialRef()                             */
4719
/************************************************************************/
4720
4721
const OGRSpatialReference *HFADataset::GetSpatialRef() const
4722
8.76k
{
4723
8.76k
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
4724
8.76k
}
4725
4726
/************************************************************************/
4727
/*                           SetSpatialRef()                            */
4728
/************************************************************************/
4729
4730
CPLErr HFADataset::SetSpatialRef(const OGRSpatialReference *poSRS)
4731
4732
33
{
4733
33
    m_oSRS.Clear();
4734
33
    if (poSRS)
4735
33
        m_oSRS = *poSRS;
4736
33
    bGeoDirty = true;
4737
4738
33
    return CE_None;
4739
33
}
4740
4741
/************************************************************************/
4742
/*                            SetMetadata()                             */
4743
/************************************************************************/
4744
4745
CPLErr HFADataset::SetMetadata(char **papszMDIn, const char *pszDomain)
4746
4747
478
{
4748
478
    bMetadataDirty = true;
4749
4750
478
    return GDALPamDataset::SetMetadata(papszMDIn, pszDomain);
4751
478
}
4752
4753
/************************************************************************/
4754
/*                            SetMetadata()                             */
4755
/************************************************************************/
4756
4757
CPLErr HFADataset::SetMetadataItem(const char *pszTag, const char *pszValue,
4758
                                   const char *pszDomain)
4759
4760
271
{
4761
271
    bMetadataDirty = true;
4762
4763
271
    return GDALPamDataset::SetMetadataItem(pszTag, pszValue, pszDomain);
4764
271
}
4765
4766
/************************************************************************/
4767
/*                          GetGeoTransform()                           */
4768
/************************************************************************/
4769
4770
CPLErr HFADataset::GetGeoTransform(GDALGeoTransform &gt) const
4771
4772
8.77k
{
4773
8.77k
    if (m_gt[0] != 0.0 || m_gt[1] != 1.0 || m_gt[2] != 0.0 || m_gt[3] != 0.0 ||
4774
5.66k
        m_gt[4] != 0.0 || m_gt[5] != 1.0)
4775
3.11k
    {
4776
3.11k
        gt = m_gt;
4777
3.11k
        return CE_None;
4778
3.11k
    }
4779
4780
5.66k
    return GDALPamDataset::GetGeoTransform(gt);
4781
8.77k
}
4782
4783
/************************************************************************/
4784
/*                          SetGeoTransform()                           */
4785
/************************************************************************/
4786
4787
CPLErr HFADataset::SetGeoTransform(const GDALGeoTransform &gt)
4788
4789
57
{
4790
57
    m_gt = gt;
4791
57
    bGeoDirty = true;
4792
4793
57
    return CE_None;
4794
57
}
4795
4796
/************************************************************************/
4797
/*                             IRasterIO()                              */
4798
/*                                                                      */
4799
/*      Multi-band raster io handler.  Here we ensure that the block    */
4800
/*      based loading is used for spill file rasters.  That is          */
4801
/*      because they are effectively pixel interleaved, so              */
4802
/*      processing all bands for a given block together avoid extra     */
4803
/*      seeks.                                                          */
4804
/************************************************************************/
4805
4806
CPLErr HFADataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
4807
                             int nXSize, int nYSize, void *pData, int nBufXSize,
4808
                             int nBufYSize, GDALDataType eBufType,
4809
                             int nBandCount, BANDMAP_TYPE panBandMap,
4810
                             GSpacing nPixelSpace, GSpacing nLineSpace,
4811
                             GSpacing nBandSpace,
4812
                             GDALRasterIOExtraArg *psExtraArg)
4813
4814
8.41k
{
4815
8.41k
    if (hHFA->papoBand[panBandMap[0] - 1]->fpExternal != nullptr &&
4816
0
        nBandCount > 1)
4817
0
        return GDALDataset::BlockBasedRasterIO(
4818
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
4819
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
4820
0
            nBandSpace, psExtraArg);
4821
4822
8.41k
    return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
4823
8.41k
                                  nBufXSize, nBufYSize, eBufType, nBandCount,
4824
8.41k
                                  panBandMap, nPixelSpace, nLineSpace,
4825
8.41k
                                  nBandSpace, psExtraArg);
4826
8.41k
}
4827
4828
/************************************************************************/
4829
/*                           UseXFormStack()                            */
4830
/************************************************************************/
4831
4832
void HFADataset::UseXFormStack(int nStepCount, Efga_Polynomial *pasPLForward,
4833
                               Efga_Polynomial *pasPLReverse)
4834
4835
14
{
4836
    // Generate GCPs using the transform.
4837
98
    for (double dfYRatio = 0.0; dfYRatio < 1.001; dfYRatio += 0.2)
4838
84
    {
4839
588
        for (double dfXRatio = 0.0; dfXRatio < 1.001; dfXRatio += 0.2)
4840
504
        {
4841
504
            const double dfLine = 0.5 + (GetRasterYSize() - 1) * dfYRatio;
4842
504
            const double dfPixel = 0.5 + (GetRasterXSize() - 1) * dfXRatio;
4843
4844
504
            gdal::GCP gcp("", "", dfPixel, dfLine,
4845
504
                          /* X = */ dfPixel,
4846
504
                          /* Y = */ dfLine);
4847
504
            if (HFAEvaluateXFormStack(nStepCount, FALSE, pasPLReverse,
4848
504
                                      &(gcp.X()), &(gcp.Y())))
4849
504
            {
4850
504
                m_aoGCPs.emplace_back(std::move(gcp));
4851
504
            }
4852
504
        }
4853
84
    }
4854
4855
    // Store the transform as metadata.
4856
14
    GDALMajorObject::SetMetadataItem(
4857
14
        "XFORM_STEPS", CPLString().Printf("%d", nStepCount), "XFORMS");
4858
4859
32
    for (int iStep = 0; iStep < nStepCount; iStep++)
4860
18
    {
4861
18
        GDALMajorObject::SetMetadataItem(
4862
18
            CPLString().Printf("XFORM%d_ORDER", iStep),
4863
18
            CPLString().Printf("%d", pasPLForward[iStep].order), "XFORMS");
4864
4865
18
        if (pasPLForward[iStep].order == 1)
4866
8
        {
4867
40
            for (int i = 0; i < 4; i++)
4868
32
                GDALMajorObject::SetMetadataItem(
4869
32
                    CPLString().Printf("XFORM%d_POLYCOEFMTX[%d]", iStep, i),
4870
32
                    CPLString().Printf("%.15g",
4871
32
                                       pasPLForward[iStep].polycoefmtx[i]),
4872
32
                    "XFORMS");
4873
4874
24
            for (int i = 0; i < 2; i++)
4875
16
                GDALMajorObject::SetMetadataItem(
4876
16
                    CPLString().Printf("XFORM%d_POLYCOEFVECTOR[%d]", iStep, i),
4877
16
                    CPLString().Printf("%.15g",
4878
16
                                       pasPLForward[iStep].polycoefvector[i]),
4879
16
                    "XFORMS");
4880
4881
8
            continue;
4882
8
        }
4883
4884
10
        int nCoefCount = 10;
4885
4886
10
        if (pasPLForward[iStep].order != 2)
4887
10
        {
4888
10
            CPLAssert(pasPLForward[iStep].order == 3);
4889
10
            nCoefCount = 18;
4890
10
        }
4891
4892
190
        for (int i = 0; i < nCoefCount; i++)
4893
180
            GDALMajorObject::SetMetadataItem(
4894
180
                CPLString().Printf("XFORM%d_FWD_POLYCOEFMTX[%d]", iStep, i),
4895
180
                CPLString().Printf("%.15g", pasPLForward[iStep].polycoefmtx[i]),
4896
180
                "XFORMS");
4897
4898
30
        for (int i = 0; i < 2; i++)
4899
20
            GDALMajorObject::SetMetadataItem(
4900
20
                CPLString().Printf("XFORM%d_FWD_POLYCOEFVECTOR[%d]", iStep, i),
4901
20
                CPLString().Printf("%.15g",
4902
20
                                   pasPLForward[iStep].polycoefvector[i]),
4903
20
                "XFORMS");
4904
4905
190
        for (int i = 0; i < nCoefCount; i++)
4906
180
            GDALMajorObject::SetMetadataItem(
4907
180
                CPLString().Printf("XFORM%d_REV_POLYCOEFMTX[%d]", iStep, i),
4908
180
                CPLString().Printf("%.15g", pasPLReverse[iStep].polycoefmtx[i]),
4909
180
                "XFORMS");
4910
4911
30
        for (int i = 0; i < 2; i++)
4912
20
            GDALMajorObject::SetMetadataItem(
4913
20
                CPLString().Printf("XFORM%d_REV_POLYCOEFVECTOR[%d]", iStep, i),
4914
20
                CPLString().Printf("%.15g",
4915
20
                                   pasPLReverse[iStep].polycoefvector[i]),
4916
20
                "XFORMS");
4917
10
    }
4918
14
}
4919
4920
/************************************************************************/
4921
/*                            GetGCPCount()                             */
4922
/************************************************************************/
4923
4924
int HFADataset::GetGCPCount()
4925
8.75k
{
4926
8.75k
    const int nPAMCount = GDALPamDataset::GetGCPCount();
4927
8.75k
    return nPAMCount > 0 ? nPAMCount : static_cast<int>(m_aoGCPs.size());
4928
8.75k
}
4929
4930
/************************************************************************/
4931
/*                          GetGCPSpatialRef()                          */
4932
/************************************************************************/
4933
4934
const OGRSpatialReference *HFADataset::GetGCPSpatialRef() const
4935
4936
8.73k
{
4937
8.73k
    const OGRSpatialReference *poSRS = GDALPamDataset::GetGCPSpatialRef();
4938
8.73k
    if (poSRS)
4939
0
        return poSRS;
4940
8.73k
    return !m_aoGCPs.empty() && !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
4941
8.73k
}
4942
4943
/************************************************************************/
4944
/*                               GetGCPs()                              */
4945
/************************************************************************/
4946
4947
const GDAL_GCP *HFADataset::GetGCPs()
4948
8.73k
{
4949
8.73k
    const GDAL_GCP *psPAMGCPs = GDALPamDataset::GetGCPs();
4950
8.73k
    if (psPAMGCPs)
4951
0
        return psPAMGCPs;
4952
8.73k
    return gdal::GCP::c_ptr(m_aoGCPs);
4953
8.73k
}
4954
4955
/************************************************************************/
4956
/*                            GetFileList()                             */
4957
/************************************************************************/
4958
4959
char **HFADataset::GetFileList()
4960
4961
17.4k
{
4962
17.4k
    CPLStringList oFileList(GDALPamDataset::GetFileList());
4963
4964
17.4k
    const std::string osIGEFilename = HFAGetIGEFilename(hHFA);
4965
17.4k
    if (!osIGEFilename.empty())
4966
276
    {
4967
276
        oFileList.push_back(osIGEFilename);
4968
276
    }
4969
4970
    // Request an overview to force opening of dependent overview files.
4971
17.4k
    if (nBands > 0 && GetRasterBand(1)->GetOverviewCount() > 0)
4972
366
        GetRasterBand(1)->GetOverview(0);
4973
4974
17.4k
    if (hHFA->psDependent != nullptr)
4975
0
    {
4976
0
        HFAInfo_t *psDep = hHFA->psDependent;
4977
4978
0
        oFileList.push_back(
4979
0
            CPLFormFilenameSafe(psDep->pszPath, psDep->pszFilename, nullptr));
4980
4981
0
        const std::string osIGEFilenameDep = HFAGetIGEFilename(psDep);
4982
0
        if (!osIGEFilenameDep.empty())
4983
0
            oFileList.push_back(osIGEFilenameDep);
4984
0
    }
4985
4986
17.4k
    return oFileList.StealList();
4987
17.4k
}
4988
4989
/************************************************************************/
4990
/*                               Create()                               */
4991
/************************************************************************/
4992
4993
GDALDataset *HFADataset::Create(const char *pszFilenameIn, int nXSize,
4994
                                int nYSize, int nBandsIn, GDALDataType eType,
4995
                                char **papszParamList)
4996
4997
136
{
4998
136
    const int nBits = CSLFetchNameValue(papszParamList, "NBITS") != nullptr
4999
136
                          ? atoi(CSLFetchNameValue(papszParamList, "NBITS"))
5000
136
                          : 0;
5001
5002
136
    const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
5003
136
    if (pszPixelType == nullptr)
5004
136
        pszPixelType = "";
5005
5006
    // Translate the data type.
5007
136
    EPTType eHfaDataType;
5008
136
    switch (eType)
5009
136
    {
5010
64
        case GDT_UInt8:
5011
64
            if (nBits == 1)
5012
0
                eHfaDataType = EPT_u1;
5013
64
            else if (nBits == 2)
5014
0
                eHfaDataType = EPT_u2;
5015
64
            else if (nBits == 4)
5016
0
                eHfaDataType = EPT_u4;
5017
64
            else if (EQUAL(pszPixelType, "SIGNEDBYTE"))
5018
0
                eHfaDataType = EPT_s8;
5019
64
            else
5020
64
                eHfaDataType = EPT_u8;
5021
64
            break;
5022
5023
6
        case GDT_Int8:
5024
6
            eHfaDataType = EPT_s8;
5025
6
            break;
5026
5027
19
        case GDT_UInt16:
5028
19
            eHfaDataType = EPT_u16;
5029
19
            break;
5030
5031
14
        case GDT_Int16:
5032
14
            eHfaDataType = EPT_s16;
5033
14
            break;
5034
5035
7
        case GDT_Int32:
5036
7
            eHfaDataType = EPT_s32;
5037
7
            break;
5038
5039
8
        case GDT_UInt32:
5040
8
            eHfaDataType = EPT_u32;
5041
8
            break;
5042
5043
9
        case GDT_Float32:
5044
9
            eHfaDataType = EPT_f32;
5045
9
            break;
5046
5047
9
        case GDT_Float64:
5048
9
            eHfaDataType = EPT_f64;
5049
9
            break;
5050
5051
0
        case GDT_CFloat32:
5052
0
            eHfaDataType = EPT_c64;
5053
0
            break;
5054
5055
0
        case GDT_CFloat64:
5056
0
            eHfaDataType = EPT_c128;
5057
0
            break;
5058
5059
0
        default:
5060
0
            CPLError(
5061
0
                CE_Failure, CPLE_NotSupported,
5062
0
                "Data type %s not supported by Erdas Imagine (HFA) format.",
5063
0
                GDALGetDataTypeName(eType));
5064
0
            return nullptr;
5065
136
    }
5066
5067
136
    const bool bForceToPEString =
5068
136
        CPLFetchBool(papszParamList, "FORCETOPESTRING", false);
5069
136
    const bool bDisablePEString =
5070
136
        CPLFetchBool(papszParamList, "DISABLEPESTRING", false);
5071
136
    if (bForceToPEString && bDisablePEString)
5072
0
    {
5073
0
        CPLError(CE_Failure, CPLE_AppDefined,
5074
0
                 "FORCETOPESTRING and DISABLEPESTRING are mutually exclusive");
5075
0
        return nullptr;
5076
0
    }
5077
5078
    // Create the new file.
5079
136
    HFAHandle hHFA = HFACreate(pszFilenameIn, nXSize, nYSize, nBandsIn,
5080
136
                               eHfaDataType, papszParamList);
5081
136
    if (hHFA == nullptr)
5082
0
        return nullptr;
5083
5084
136
    if (HFAClose(hHFA) != 0)
5085
0
    {
5086
0
        CPLError(CE_Failure, CPLE_FileIO, "I/O error");
5087
0
        return nullptr;
5088
0
    }
5089
5090
    // Open the dataset normally.
5091
136
    HFADataset *poDS = (HFADataset *)GDALOpen(pszFilenameIn, GA_Update);
5092
5093
    // Special creation option to disable checking for UTM
5094
    // parameters when writing the projection.  This is a special
5095
    // hack for sam.gillingham@nrm.qld.gov.au.
5096
136
    if (poDS != nullptr)
5097
136
    {
5098
136
        poDS->bIgnoreUTM = CPLFetchBool(papszParamList, "IGNOREUTM", false);
5099
136
    }
5100
5101
    // Sometimes we can improve ArcGIS compatibility by forcing
5102
    // generation of a PEString instead of traditional Imagine
5103
    // coordinate system descriptions.
5104
136
    if (poDS != nullptr)
5105
136
    {
5106
136
        poDS->bForceToPEString = bForceToPEString;
5107
136
        poDS->bDisablePEString = bDisablePEString;
5108
136
    }
5109
5110
136
    return poDS;
5111
136
}
5112
5113
/************************************************************************/
5114
/*                               Rename()                               */
5115
/*                                                                      */
5116
/*      Custom Rename() implementation that knows how to update         */
5117
/*      filename references in .img and .aux files.                     */
5118
/************************************************************************/
5119
5120
CPLErr HFADataset::Rename(const char *pszNewName, const char *pszOldName)
5121
5122
0
{
5123
    // Rename all the files at the filesystem level.
5124
0
    CPLErr eErr = GDALDriver::DefaultRename(pszNewName, pszOldName);
5125
0
    if (eErr != CE_None)
5126
0
        return eErr;
5127
5128
    // Now try to go into the .img file and update RRDNames[] lists.
5129
0
    CPLString osOldBasename = CPLGetBasenameSafe(pszOldName);
5130
0
    CPLString osNewBasename = CPLGetBasenameSafe(pszNewName);
5131
5132
0
    if (osOldBasename != osNewBasename)
5133
0
    {
5134
0
        HFAHandle hHFA = HFAOpen(pszNewName, "r+");
5135
5136
0
        if (hHFA != nullptr)
5137
0
        {
5138
0
            eErr = HFARenameReferences(hHFA, osNewBasename, osOldBasename);
5139
5140
0
            HFAGetOverviewCount(hHFA, 1);
5141
5142
0
            if (hHFA->psDependent != nullptr)
5143
0
                HFARenameReferences(hHFA->psDependent, osNewBasename,
5144
0
                                    osOldBasename);
5145
5146
0
            if (HFAClose(hHFA) != 0)
5147
0
                eErr = CE_Failure;
5148
0
        }
5149
0
    }
5150
5151
0
    return eErr;
5152
0
}
5153
5154
/************************************************************************/
5155
/*                             CopyFiles()                              */
5156
/*                                                                      */
5157
/*      Custom CopyFiles() implementation that knows how to update      */
5158
/*      filename references in .img and .aux files.                     */
5159
/************************************************************************/
5160
5161
CPLErr HFADataset::CopyFiles(const char *pszNewName, const char *pszOldName)
5162
5163
0
{
5164
    // Rename all the files at the filesystem level.
5165
0
    CPLErr eErr = GDALDriver::DefaultCopyFiles(pszNewName, pszOldName);
5166
5167
0
    if (eErr != CE_None)
5168
0
        return eErr;
5169
5170
    // Now try to go into the .img file and update RRDNames[] lists.
5171
0
    CPLString osOldBasename = CPLGetBasenameSafe(pszOldName);
5172
0
    CPLString osNewBasename = CPLGetBasenameSafe(pszNewName);
5173
5174
0
    if (osOldBasename != osNewBasename)
5175
0
    {
5176
0
        HFAHandle hHFA = HFAOpen(pszNewName, "r+");
5177
5178
0
        if (hHFA != nullptr)
5179
0
        {
5180
0
            eErr = HFARenameReferences(hHFA, osNewBasename, osOldBasename);
5181
5182
0
            HFAGetOverviewCount(hHFA, 1);
5183
5184
0
            if (hHFA->psDependent != nullptr)
5185
0
                HFARenameReferences(hHFA->psDependent, osNewBasename,
5186
0
                                    osOldBasename);
5187
5188
0
            if (HFAClose(hHFA) != 0)
5189
0
                eErr = CE_Failure;
5190
0
        }
5191
0
    }
5192
5193
0
    return eErr;
5194
0
}
5195
5196
/************************************************************************/
5197
/*                             CreateCopy()                             */
5198
/************************************************************************/
5199
5200
GDALDataset *HFADataset::CreateCopy(const char *pszFilename,
5201
                                    GDALDataset *poSrcDS, int /* bStrict */,
5202
                                    char **papszOptions,
5203
                                    GDALProgressFunc pfnProgress,
5204
                                    void *pProgressData)
5205
136
{
5206
    // Do we really just want to create an .aux file?
5207
136
    const bool bCreateAux = CPLFetchBool(papszOptions, "AUX", false);
5208
5209
    // Establish a representative data type to use.
5210
136
    char **papszModOptions = CSLDuplicate(papszOptions);
5211
136
    if (!pfnProgress(0.0, nullptr, pProgressData))
5212
0
    {
5213
0
        CSLDestroy(papszModOptions);
5214
0
        return nullptr;
5215
0
    }
5216
5217
136
    const int nBandCount = poSrcDS->GetRasterCount();
5218
136
    GDALDataType eType = GDT_Unknown;
5219
5220
19.4k
    for (int iBand = 0; iBand < nBandCount; iBand++)
5221
19.3k
    {
5222
19.3k
        GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
5223
19.3k
        if (iBand == 0)
5224
136
            eType = poBand->GetRasterDataType();
5225
19.2k
        else
5226
19.2k
            eType = GDALDataTypeUnion(eType, poBand->GetRasterDataType());
5227
19.3k
    }
5228
5229
    // If we have PIXELTYPE metadata in the source, pass it
5230
    // through as a creation option.
5231
136
    if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
5232
136
        nBandCount > 0 && eType == GDT_UInt8)
5233
64
    {
5234
64
        auto poSrcBand = poSrcDS->GetRasterBand(1);
5235
64
        poSrcBand->EnablePixelTypeSignedByteWarning(false);
5236
64
        const char *pszPixelType =
5237
64
            poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
5238
64
        poSrcBand->EnablePixelTypeSignedByteWarning(true);
5239
64
        if (pszPixelType)
5240
0
        {
5241
0
            papszModOptions =
5242
0
                CSLSetNameValue(papszModOptions, "PIXELTYPE", pszPixelType);
5243
0
        }
5244
64
    }
5245
5246
136
    HFADataset *poDS = cpl::down_cast<HFADataset *>(
5247
136
        Create(pszFilename, poSrcDS->GetRasterXSize(),
5248
136
               poSrcDS->GetRasterYSize(), nBandCount, eType, papszModOptions));
5249
5250
136
    CSLDestroy(papszModOptions);
5251
5252
136
    if (poDS == nullptr)
5253
0
        return nullptr;
5254
5255
    // Does the source have a PCT or RAT for any of the bands?  If so, copy it
5256
    // over.
5257
19.4k
    for (int iBand = 0; iBand < nBandCount; iBand++)
5258
19.3k
    {
5259
19.3k
        GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
5260
5261
19.3k
        GDALColorTable *poCT = poBand->GetColorTable();
5262
19.3k
        if (poCT != nullptr)
5263
19
        {
5264
19
            poDS->GetRasterBand(iBand + 1)->SetColorTable(poCT);
5265
19
        }
5266
5267
19.3k
        if (poBand->GetDefaultRAT() != nullptr)
5268
0
            poDS->GetRasterBand(iBand + 1)->SetDefaultRAT(
5269
0
                poBand->GetDefaultRAT());
5270
19.3k
    }
5271
5272
    // Do we have metadata for any of the bands or the dataset as a whole?
5273
136
    if (poSrcDS->GetMetadata() != nullptr)
5274
77
        poDS->SetMetadata(poSrcDS->GetMetadata());
5275
5276
19.4k
    for (int iBand = 0; iBand < nBandCount; iBand++)
5277
19.3k
    {
5278
19.3k
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
5279
19.3k
        GDALRasterBand *poDstBand = poDS->GetRasterBand(iBand + 1);
5280
5281
19.3k
        if (poSrcBand->GetMetadata() != nullptr)
5282
5.48k
            poDstBand->SetMetadata(poSrcBand->GetMetadata());
5283
5284
19.3k
        if (strlen(poSrcBand->GetDescription()) > 0)
5285
1.20k
            poDstBand->SetDescription(poSrcBand->GetDescription());
5286
5287
19.3k
        int bSuccess = FALSE;
5288
19.3k
        const double dfNoDataValue = poSrcBand->GetNoDataValue(&bSuccess);
5289
19.3k
        if (bSuccess)
5290
6.25k
            poDstBand->SetNoDataValue(dfNoDataValue);
5291
19.3k
    }
5292
5293
    // Copy projection information.
5294
136
    GDALGeoTransform gt;
5295
136
    if (poSrcDS->GetGeoTransform(gt) == CE_None)
5296
47
        poDS->SetGeoTransform(gt);
5297
5298
136
    const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
5299
136
    if (poSrcSRS)
5300
33
        poDS->SetSpatialRef(poSrcSRS);
5301
5302
    // Copy the imagery.
5303
136
    if (!bCreateAux)
5304
136
    {
5305
136
        const CPLErr eErr = GDALDatasetCopyWholeRaster(
5306
136
            (GDALDatasetH)poSrcDS, (GDALDatasetH)poDS, nullptr, pfnProgress,
5307
136
            pProgressData);
5308
5309
136
        if (eErr != CE_None)
5310
48
        {
5311
48
            delete poDS;
5312
48
            return nullptr;
5313
48
        }
5314
136
    }
5315
5316
    // Do we want to generate statistics and a histogram?
5317
88
    if (CPLFetchBool(papszOptions, "STATISTICS", false))
5318
39
    {
5319
15.1k
        for (int iBand = 0; iBand < nBandCount; iBand++)
5320
15.0k
        {
5321
15.0k
            GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
5322
15.0k
            double dfMin = 0.0;
5323
15.0k
            double dfMax = 0.0;
5324
15.0k
            double dfMean = 0.0;
5325
15.0k
            double dfStdDev = 0.0;
5326
15.0k
            char **papszStatsMD = nullptr;
5327
5328
            // Statistics
5329
15.0k
            if (poSrcBand->GetStatistics(TRUE, FALSE, &dfMin, &dfMax, &dfMean,
5330
15.0k
                                         &dfStdDev) == CE_None ||
5331
11.0k
                poSrcBand->ComputeStatistics(TRUE, &dfMin, &dfMax, &dfMean,
5332
11.0k
                                             &dfStdDev, pfnProgress,
5333
11.0k
                                             pProgressData) == CE_None)
5334
15.0k
            {
5335
15.0k
                CPLString osValue;
5336
5337
15.0k
                papszStatsMD =
5338
15.0k
                    CSLSetNameValue(papszStatsMD, "STATISTICS_MINIMUM",
5339
15.0k
                                    osValue.Printf("%.15g", dfMin));
5340
15.0k
                papszStatsMD =
5341
15.0k
                    CSLSetNameValue(papszStatsMD, "STATISTICS_MAXIMUM",
5342
15.0k
                                    osValue.Printf("%.15g", dfMax));
5343
15.0k
                papszStatsMD = CSLSetNameValue(papszStatsMD, "STATISTICS_MEAN",
5344
15.0k
                                               osValue.Printf("%.15g", dfMean));
5345
15.0k
                papszStatsMD =
5346
15.0k
                    CSLSetNameValue(papszStatsMD, "STATISTICS_STDDEV",
5347
15.0k
                                    osValue.Printf("%.15g", dfStdDev));
5348
15.0k
            }
5349
5350
            // Histogram
5351
15.0k
            int nBuckets = 0;
5352
15.0k
            GUIntBig *panHistogram = nullptr;
5353
5354
15.0k
            if (poSrcBand->GetDefaultHistogram(&dfMin, &dfMax, &nBuckets,
5355
15.0k
                                               &panHistogram, TRUE, pfnProgress,
5356
15.0k
                                               pProgressData) == CE_None)
5357
15.0k
            {
5358
15.0k
                CPLString osValue;
5359
15.0k
                const double dfBinWidth = (dfMax - dfMin) / nBuckets;
5360
5361
15.0k
                papszStatsMD = CSLSetNameValue(
5362
15.0k
                    papszStatsMD, "STATISTICS_HISTOMIN",
5363
15.0k
                    osValue.Printf("%.15g", dfMin + dfBinWidth * 0.5));
5364
15.0k
                papszStatsMD = CSLSetNameValue(
5365
15.0k
                    papszStatsMD, "STATISTICS_HISTOMAX",
5366
15.0k
                    osValue.Printf("%.15g", dfMax - dfBinWidth * 0.5));
5367
15.0k
                papszStatsMD =
5368
15.0k
                    CSLSetNameValue(papszStatsMD, "STATISTICS_HISTONUMBINS",
5369
15.0k
                                    osValue.Printf("%d", nBuckets));
5370
5371
15.0k
                int nBinValuesLen = 0;
5372
15.0k
                char *pszBinValues =
5373
15.0k
                    static_cast<char *>(CPLCalloc(20, nBuckets + 1));
5374
2.19M
                for (int iBin = 0; iBin < nBuckets; iBin++)
5375
2.18M
                {
5376
5377
2.18M
                    strcat(pszBinValues + nBinValuesLen,
5378
2.18M
                           osValue.Printf(CPL_FRMT_GUIB, panHistogram[iBin]));
5379
2.18M
                    strcat(pszBinValues + nBinValuesLen, "|");
5380
2.18M
                    nBinValuesLen +=
5381
2.18M
                        static_cast<int>(strlen(pszBinValues + nBinValuesLen));
5382
2.18M
                }
5383
15.0k
                papszStatsMD = CSLSetNameValue(
5384
15.0k
                    papszStatsMD, "STATISTICS_HISTOBINVALUES", pszBinValues);
5385
15.0k
                CPLFree(pszBinValues);
5386
15.0k
            }
5387
5388
15.0k
            CPLFree(panHistogram);
5389
5390
15.0k
            if (CSLCount(papszStatsMD) > 0)
5391
15.0k
                HFASetMetadata(poDS->hHFA, iBand + 1, papszStatsMD);
5392
5393
15.0k
            CSLDestroy(papszStatsMD);
5394
15.0k
        }
5395
39
    }
5396
5397
    // All report completion.
5398
88
    if (!pfnProgress(1.0, nullptr, pProgressData))
5399
0
    {
5400
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
5401
0
        delete poDS;
5402
5403
0
        GDALDriver *poHFADriver = (GDALDriver *)GDALGetDriverByName("HFA");
5404
0
        poHFADriver->Delete(pszFilename);
5405
0
        return nullptr;
5406
0
    }
5407
5408
88
    poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
5409
5410
88
    return poDS;
5411
88
}
5412
5413
/************************************************************************/
5414
/*                          GDALRegister_HFA()                          */
5415
/************************************************************************/
5416
5417
void GDALRegister_HFA()
5418
5419
24
{
5420
24
    if (GDALGetDriverByName("HFA") != nullptr)
5421
0
        return;
5422
5423
24
    GDALDriver *poDriver = new GDALDriver();
5424
5425
24
    poDriver->SetDescription("HFA");
5426
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
5427
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Erdas Imagine Images (.img)");
5428
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/hfa.html");
5429
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "img");
5430
24
    poDriver->SetMetadataItem(
5431
24
        GDAL_DMD_CREATIONDATATYPES,
5432
24
        "Byte Int8 Int16 UInt16 Int32 UInt32 Float32 Float64 "
5433
24
        "CFloat32 CFloat64");
5434
5435
24
    poDriver->SetMetadataItem(
5436
24
        GDAL_DMD_CREATIONOPTIONLIST,
5437
24
        "<CreationOptionList>"
5438
24
        "   <Option name='BLOCKSIZE' type='integer' description='tile "
5439
24
        "width/height (32-2048)' default='64'/>"
5440
24
        "   <Option name='USE_SPILL' type='boolean' description='Force use of "
5441
24
        "spill file'/>"
5442
24
        "   <Option name='COMPRESSED' alias='COMPRESS' type='boolean' "
5443
24
        "description='compress blocks'/>"
5444
24
        "   <Option name='PIXELTYPE' type='string' description='(deprecated, "
5445
24
        "use Int8) By setting this to SIGNEDBYTE, a new Byte file can be "
5446
24
        "forced to be written as signed byte'/>"
5447
24
        "   <Option name='AUX' type='boolean' description='Create an .aux "
5448
24
        "file'/>"
5449
24
        "   <Option name='IGNOREUTM' type='boolean' description='Ignore UTM "
5450
24
        "when selecting coordinate system - will use Transverse Mercator. Only "
5451
24
        "used for Create() method'/>"
5452
24
        "   <Option name='NBITS' type='integer' description='Create file with "
5453
24
        "special sub-byte data type (1/2/4)'/>"
5454
24
        "   <Option name='STATISTICS' type='boolean' description='Generate "
5455
24
        "statistics and a histogram'/>"
5456
24
        "   <Option name='DEPENDENT_FILE' type='string' description='Name of "
5457
24
        "dependent file (must not have absolute path)'/>"
5458
24
        "   <Option name='FORCETOPESTRING' type='boolean' description='Force "
5459
24
        "use of ArcGIS PE String in file instead of Imagine coordinate system "
5460
24
        "format' default='NO'/>"
5461
24
        "   <Option name='DISABLEPESTRING' type='boolean' description='Disable "
5462
24
        "use of ArcGIS PE String' default='NO'/>"
5463
24
        "</CreationOptionList>");
5464
5465
24
    poDriver->SetMetadataItem(
5466
24
        GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST,
5467
24
        "<OverviewCreationOptionList>"
5468
24
        "   <Option name='COMPRESSED' alias='COMPRESS' type='boolean' "
5469
24
        "description='compress blocks'/>"
5470
24
        "</OverviewCreationOptionList>");
5471
5472
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
5473
5474
24
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
5475
24
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
5476
24
                              "GeoTransform SRS NoData "
5477
24
                              "RasterValues "
5478
24
                              "DatasetMetadata BandMetadata");
5479
5480
24
    poDriver->pfnOpen = HFADataset::Open;
5481
24
    poDriver->pfnCreate = HFADataset::Create;
5482
24
    poDriver->pfnCreateCopy = HFADataset::CreateCopy;
5483
24
    poDriver->pfnIdentify = HFADataset::Identify;
5484
24
    poDriver->pfnRename = HFADataset::Rename;
5485
24
    poDriver->pfnCopyFiles = HFADataset::CopyFiles;
5486
5487
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
5488
24
}