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