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