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