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