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