Coverage Report

Created: 2025-06-09 07:43

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