Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISIS Version 3 Driver
4 : * Purpose: Implementation of ISIS3Dataset
5 : * Author: Trent Hare (thare@usgs.gov)
6 : * Frank Warmerdam (warmerdam@pobox.com)
7 : * Even Rouault (even.rouault at spatialys.com)
8 : *
9 : * NOTE: Original code authored by Trent and placed in the public domain as
10 : * per US government policy. I have (within my rights) appropriated it and
11 : * placed it under the following license. This is not intended to diminish
12 : * Trents contribution.
13 : ******************************************************************************
14 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
15 : * Copyright (c) 2009-2010, Even Rouault <even.rouault at spatialys.com>
16 : * Copyright (c) 2017 Hobu Inc
17 : * Copyright (c) 2017, Dmitry Baryshnikov <polimax@mail.ru>
18 : * Copyright (c) 2017, NextGIS <info@nextgis.com>
19 : *
20 : * SPDX-License-Identifier: MIT
21 : ****************************************************************************/
22 :
23 : #include "cpl_json.h"
24 : #include "cpl_string.h"
25 : #include "cpl_time.h"
26 : #include "cpl_vsi_error.h"
27 : #include "gdal_frmts.h"
28 : #include "gdal_proxy.h"
29 : #include "nasakeywordhandler.h"
30 : #include "ogrgeojsonreader.h"
31 : #include "ogr_spatialref.h"
32 : #include "rawdataset.h"
33 : #include "vrtdataset.h"
34 : #include "cpl_safemaths.hpp"
35 : #include "pdsdrivercore.h"
36 : #include "json_utils.h"
37 :
38 : // For gethostname()
39 : #ifdef _WIN32
40 : #include <winsock2.h>
41 : #else
42 : #include <unistd.h>
43 : #endif
44 :
45 : #include <algorithm>
46 : #include <map>
47 : #include <utility> // pair
48 : #include <vector>
49 :
50 : // Constants coming from ISIS3 source code
51 : // in isis/src/base/objs/SpecialPixel/SpecialPixel.h
52 :
53 : // There are several types of special pixels
54 : // * Isis::Null Pixel has no data available
55 : // * Isis::Lis Pixel was saturated on the instrument
56 : // * Isis::His Pixel was saturated on the instrument
57 : // * Isis::Lrs Pixel was saturated during a computation
58 : // * Isis::Hrs Pixel was saturated during a computation
59 :
60 : // 1-byte special pixel values
61 : const unsigned char ISIS3_NULL1 = 0;
62 : const unsigned char LOW_REPR_SAT1 = 0;
63 : const unsigned char LOW_INSTR_SAT1 = 0;
64 : const unsigned char HIGH_INSTR_SAT1 = 255;
65 : const unsigned char HIGH_REPR_SAT1 = 255;
66 :
67 : // 2-byte unsigned special pixel values
68 : const unsigned short ISIS3_NULLU2 = 0;
69 : const unsigned short LOW_REPR_SATU2 = 1;
70 : const unsigned short LOW_INSTR_SATU2 = 2;
71 : const unsigned short HIGH_INSTR_SATU2 = 65534;
72 : const unsigned short HIGH_REPR_SATU2 = 65535;
73 :
74 : // 2-byte signed special pixel values
75 : const short ISIS3_NULL2 = -32768;
76 : const short LOW_REPR_SAT2 = -32767;
77 : const short LOW_INSTR_SAT2 = -32766;
78 : const short HIGH_INSTR_SAT2 = -32765;
79 : const short HIGH_REPR_SAT2 = -32764;
80 :
81 : // Define 4-byte special pixel values for IEEE floating point
82 : const float ISIS3_NULL4 = -3.4028226550889045e+38f; // 0xFF7FFFFB;
83 : const float LOW_REPR_SAT4 = -3.4028228579130005e+38f; // 0xFF7FFFFC;
84 : const float LOW_INSTR_SAT4 = -3.4028230607370965e+38f; // 0xFF7FFFFD;
85 : const float HIGH_INSTR_SAT4 = -3.4028232635611926e+38f; // 0xFF7FFFFE;
86 : const float HIGH_REPR_SAT4 = -3.4028234663852886e+38f; // 0xFF7FFFFF;
87 :
88 : // Must be large enough to hold an integer
89 : static const char *const pszSTARTBYTE_PLACEHOLDER = "!*^STARTBYTE^*!";
90 : // Must be large enough to hold an integer
91 : static const char *const pszLABEL_BYTES_PLACEHOLDER = "!*^LABEL_BYTES^*!";
92 : // Must be large enough to hold an integer
93 : static const char *const pszHISTORY_STARTBYTE_PLACEHOLDER =
94 : "!*^HISTORY_STARTBYTE^*!";
95 :
96 : /************************************************************************/
97 : /* ==================================================================== */
98 : /* ISISDataset */
99 : /* ==================================================================== */
100 : /************************************************************************/
101 :
102 : class ISIS3Dataset final : public RawDataset
103 : {
104 : friend class ISIS3RawRasterBand;
105 : friend class ISISTiledBand;
106 : friend class ISIS3WrapperRasterBand;
107 :
108 : class NonPixelSection
109 : {
110 : public:
111 : CPLString osSrcFilename;
112 : CPLString osDstFilename; // empty for same file
113 : vsi_l_offset nSrcOffset;
114 : vsi_l_offset nSize;
115 : CPLString osPlaceHolder; // empty if not same file
116 : };
117 :
118 : VSILFILE *m_fpLabel; // label file (only used for writing)
119 : VSILFILE *m_fpImage; // image data file. May be == fpLabel
120 : GDALDataset *m_poExternalDS; // external dataset (GeoTIFF)
121 : bool m_bGeoTIFFAsRegularExternal; // creation only
122 : bool m_bGeoTIFFInitDone; // creation only
123 :
124 : CPLString m_osExternalFilename;
125 : bool m_bIsLabelWritten; // creation only
126 :
127 : bool m_bIsTiled;
128 : bool m_bInitToNodata; // creation only
129 :
130 : NASAKeywordHandler m_oKeywords;
131 :
132 : bool m_bGotTransform;
133 : double m_adfGeoTransform[6];
134 :
135 : bool m_bHasSrcNoData; // creation only
136 : double m_dfSrcNoData; // creation only
137 :
138 : OGRSpatialReference m_oSRS;
139 :
140 : // creation only variables
141 : CPLString m_osComment;
142 : CPLString m_osLatitudeType;
143 : CPLString m_osLongitudeDirection;
144 : CPLString m_osTargetName;
145 : bool m_bForce360;
146 : bool m_bWriteBoundingDegrees;
147 : CPLString m_osBoundingDegrees;
148 :
149 : CPLJSONObject m_oJSonLabel;
150 : CPLString m_osHistory; // creation only
151 : bool m_bUseSrcLabel; // creation only
152 : bool m_bUseSrcMapping; // creation only
153 : bool m_bUseSrcHistory; // creation only
154 : bool m_bAddGDALHistory; // creation only
155 : CPLString m_osGDALHistory; // creation only
156 : std::vector<NonPixelSection> m_aoNonPixelSections; // creation only
157 : CPLJSONObject m_oSrcJSonLabel; // creation only
158 : CPLStringList m_aosISIS3MD;
159 : CPLStringList m_aosAdditionalFiles;
160 : CPLString m_osFromFilename; // creation only
161 :
162 : RawBinaryLayout m_sLayout{};
163 :
164 : const char *GetKeyword(const char *pszPath, const char *pszDefault = "");
165 :
166 : double FixLong(double dfLong);
167 : void BuildLabel();
168 : void BuildHistory();
169 : void WriteLabel();
170 : void InvalidateLabel();
171 :
172 : static CPLString SerializeAsPDL(const CPLJSONObject &oObj);
173 : static void SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
174 : int nDepth = 0);
175 :
176 : protected:
177 : CPLErr Close() override;
178 :
179 : public:
180 : ISIS3Dataset();
181 : virtual ~ISIS3Dataset();
182 :
183 : virtual int CloseDependentDatasets() override;
184 :
185 : virtual CPLErr GetGeoTransform(double *padfTransform) override;
186 : virtual CPLErr SetGeoTransform(double *padfTransform) override;
187 :
188 : const OGRSpatialReference *GetSpatialRef() const override;
189 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
190 :
191 : virtual char **GetFileList() override;
192 :
193 : virtual char **GetMetadataDomainList() override;
194 : virtual char **GetMetadata(const char *pszDomain = "") override;
195 : virtual CPLErr SetMetadata(char **papszMD,
196 : const char *pszDomain = "") override;
197 :
198 : bool GetRawBinaryLayout(GDALDataset::RawBinaryLayout &) override;
199 :
200 : static GDALDataset *Open(GDALOpenInfo *);
201 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
202 : int nBandsIn, GDALDataType eType,
203 : char **papszOptions);
204 : static GDALDataset *CreateCopy(const char *pszFilename,
205 : GDALDataset *poSrcDS, int bStrict,
206 : char **papszOptions,
207 : GDALProgressFunc pfnProgress,
208 : void *pProgressData);
209 : };
210 :
211 : /************************************************************************/
212 : /* ==================================================================== */
213 : /* ISISTiledBand */
214 : /* ==================================================================== */
215 : /************************************************************************/
216 :
217 : class ISISTiledBand final : public GDALPamRasterBand
218 : {
219 : friend class ISIS3Dataset;
220 :
221 : VSILFILE *m_fpVSIL;
222 : GIntBig m_nFirstTileOffset;
223 : GIntBig m_nXTileOffset;
224 : GIntBig m_nYTileOffset;
225 : int m_bNativeOrder;
226 : bool m_bHasOffset;
227 : bool m_bHasScale;
228 : double m_dfOffset;
229 : double m_dfScale;
230 : double m_dfNoData;
231 : bool m_bValid = false;
232 :
233 : public:
234 : ISISTiledBand(GDALDataset *poDS, VSILFILE *fpVSIL, int nBand,
235 : GDALDataType eDT, int nTileXSize, int nTileYSize,
236 : GIntBig nFirstTileOffset, GIntBig nXTileOffset,
237 : GIntBig nYTileOffset, int bNativeOrder);
238 :
239 102 : virtual ~ISISTiledBand()
240 51 : {
241 102 : }
242 :
243 41 : bool IsValid() const
244 : {
245 41 : return m_bValid;
246 : }
247 :
248 : virtual CPLErr IReadBlock(int, int, void *) override;
249 : virtual CPLErr IWriteBlock(int, int, void *) override;
250 :
251 : virtual double GetOffset(int *pbSuccess = nullptr) override;
252 : virtual double GetScale(int *pbSuccess = nullptr) override;
253 : virtual CPLErr SetOffset(double dfNewOffset) override;
254 : virtual CPLErr SetScale(double dfNewScale) override;
255 : virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
256 : virtual CPLErr SetNoDataValue(double dfNewNoData) override;
257 :
258 : void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
259 : };
260 :
261 : /************************************************************************/
262 : /* ==================================================================== */
263 : /* ISIS3RawRasterBand */
264 : /* ==================================================================== */
265 : /************************************************************************/
266 :
267 : class ISIS3RawRasterBand final : public RawRasterBand
268 : {
269 : friend class ISIS3Dataset;
270 :
271 : bool m_bHasOffset;
272 : bool m_bHasScale;
273 : double m_dfOffset;
274 : double m_dfScale;
275 : double m_dfNoData;
276 :
277 : public:
278 : ISIS3RawRasterBand(GDALDataset *l_poDS, int l_nBand, VSILFILE *l_fpRaw,
279 : vsi_l_offset l_nImgOffset, int l_nPixelOffset,
280 : int l_nLineOffset, GDALDataType l_eDataType,
281 : int l_bNativeOrder);
282 :
283 732 : virtual ~ISIS3RawRasterBand()
284 366 : {
285 732 : }
286 :
287 : virtual CPLErr IReadBlock(int, int, void *) override;
288 : virtual CPLErr IWriteBlock(int, int, void *) override;
289 :
290 : virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
291 : GDALDataType, GSpacing nPixelSpace,
292 : GSpacing nLineSpace,
293 : GDALRasterIOExtraArg *psExtraArg) override;
294 :
295 : virtual double GetOffset(int *pbSuccess = nullptr) override;
296 : virtual double GetScale(int *pbSuccess = nullptr) override;
297 : virtual CPLErr SetOffset(double dfNewOffset) override;
298 : virtual CPLErr SetScale(double dfNewScale) override;
299 : virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
300 : virtual CPLErr SetNoDataValue(double dfNewNoData) override;
301 :
302 : void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
303 : };
304 :
305 : /************************************************************************/
306 : /* ==================================================================== */
307 : /* ISIS3WrapperRasterBand */
308 : /* */
309 : /* proxy for bands stored in other formats. */
310 : /* ==================================================================== */
311 : /************************************************************************/
312 : class ISIS3WrapperRasterBand final : public GDALProxyRasterBand
313 : {
314 : friend class ISIS3Dataset;
315 :
316 : GDALRasterBand *m_poBaseBand;
317 : bool m_bHasOffset;
318 : bool m_bHasScale;
319 : double m_dfOffset;
320 : double m_dfScale;
321 : double m_dfNoData;
322 :
323 : protected:
324 : virtual GDALRasterBand *
325 127 : RefUnderlyingRasterBand(bool /* bForceOpen */) const override
326 : {
327 127 : return m_poBaseBand;
328 : }
329 :
330 : public:
331 : explicit ISIS3WrapperRasterBand(GDALRasterBand *poBaseBandIn);
332 :
333 90 : ~ISIS3WrapperRasterBand()
334 45 : {
335 90 : }
336 :
337 : void InitFile();
338 :
339 : virtual CPLErr Fill(double dfRealValue,
340 : double dfImaginaryValue = 0) override;
341 : virtual CPLErr IWriteBlock(int, int, void *) override;
342 :
343 : virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
344 : GDALDataType, GSpacing nPixelSpace,
345 : GSpacing nLineSpace,
346 : GDALRasterIOExtraArg *psExtraArg) override;
347 :
348 : virtual double GetOffset(int *pbSuccess = nullptr) override;
349 : virtual double GetScale(int *pbSuccess = nullptr) override;
350 : virtual CPLErr SetOffset(double dfNewOffset) override;
351 : virtual CPLErr SetScale(double dfNewScale) override;
352 : virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
353 : virtual CPLErr SetNoDataValue(double dfNewNoData) override;
354 :
355 4 : int GetMaskFlags() override
356 : {
357 4 : return nMaskFlags;
358 : }
359 :
360 4 : GDALRasterBand *GetMaskBand() override
361 : {
362 4 : return poMask;
363 : }
364 :
365 : void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
366 : };
367 :
368 : /************************************************************************/
369 : /* ==================================================================== */
370 : /* ISISMaskBand */
371 : /* ==================================================================== */
372 :
373 : class ISISMaskBand final : public GDALRasterBand
374 : {
375 : GDALRasterBand *m_poBaseBand;
376 : void *m_pBuffer;
377 :
378 : public:
379 : explicit ISISMaskBand(GDALRasterBand *poBaseBand);
380 : ~ISISMaskBand();
381 :
382 : virtual CPLErr IReadBlock(int, int, void *) override;
383 : };
384 :
385 : /************************************************************************/
386 : /* ISISTiledBand() */
387 : /************************************************************************/
388 :
389 51 : ISISTiledBand::ISISTiledBand(GDALDataset *poDSIn, VSILFILE *fpVSILIn,
390 : int nBandIn, GDALDataType eDT, int nTileXSize,
391 : int nTileYSize, GIntBig nFirstTileOffsetIn,
392 : GIntBig nXTileOffsetIn, GIntBig nYTileOffsetIn,
393 51 : int bNativeOrderIn)
394 : : m_fpVSIL(fpVSILIn), m_nFirstTileOffset(0), m_nXTileOffset(nXTileOffsetIn),
395 : m_nYTileOffset(nYTileOffsetIn), m_bNativeOrder(bNativeOrderIn),
396 : m_bHasOffset(false), m_bHasScale(false), m_dfOffset(0.0), m_dfScale(1.0),
397 51 : m_dfNoData(0.0)
398 : {
399 51 : poDS = poDSIn;
400 51 : nBand = nBandIn;
401 51 : eDataType = eDT;
402 51 : nBlockXSize = nTileXSize;
403 51 : nBlockYSize = nTileYSize;
404 51 : nRasterXSize = poDSIn->GetRasterXSize();
405 51 : nRasterYSize = poDSIn->GetRasterYSize();
406 :
407 51 : const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
408 51 : const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
409 :
410 51 : if (m_nXTileOffset == 0 && m_nYTileOffset == 0)
411 : {
412 51 : m_nXTileOffset =
413 51 : static_cast<GIntBig>(GDALGetDataTypeSizeBytes(eDT)) * nTileXSize;
414 51 : if (m_nXTileOffset > GINTBIG_MAX / nTileYSize)
415 : {
416 0 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
417 0 : return;
418 : }
419 51 : m_nXTileOffset *= nTileYSize;
420 :
421 51 : if (m_nXTileOffset > GINTBIG_MAX / l_nBlocksPerRow)
422 : {
423 0 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
424 0 : return;
425 : }
426 51 : m_nYTileOffset = m_nXTileOffset * l_nBlocksPerRow;
427 : }
428 :
429 51 : m_nFirstTileOffset = nFirstTileOffsetIn;
430 51 : if (nBand > 1)
431 : {
432 19 : if (m_nYTileOffset > GINTBIG_MAX / (nBand - 1) ||
433 19 : (nBand - 1) * m_nYTileOffset > GINTBIG_MAX / l_nBlocksPerColumn ||
434 19 : m_nFirstTileOffset >
435 19 : GINTBIG_MAX - (nBand - 1) * m_nYTileOffset * l_nBlocksPerColumn)
436 : {
437 0 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
438 0 : return;
439 : }
440 19 : m_nFirstTileOffset += (nBand - 1) * m_nYTileOffset * l_nBlocksPerColumn;
441 : }
442 51 : m_bValid = true;
443 : }
444 :
445 : /************************************************************************/
446 : /* IReadBlock() */
447 : /************************************************************************/
448 :
449 476 : CPLErr ISISTiledBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
450 :
451 : {
452 476 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
453 476 : if (poGDS->m_osExternalFilename.empty())
454 : {
455 232 : if (!poGDS->m_bIsLabelWritten)
456 2 : poGDS->WriteLabel();
457 : }
458 :
459 476 : const GIntBig nOffset = m_nFirstTileOffset + nXBlock * m_nXTileOffset +
460 476 : nYBlock * m_nYTileOffset;
461 476 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
462 476 : const size_t nBlockSize =
463 476 : static_cast<size_t>(nDTSize) * nBlockXSize * nBlockYSize;
464 :
465 476 : if (VSIFSeekL(m_fpVSIL, nOffset, SEEK_SET) != 0)
466 : {
467 0 : CPLError(CE_Failure, CPLE_FileIO,
468 : "Failed to seek to offset %d to read tile %d,%d.",
469 : static_cast<int>(nOffset), nXBlock, nYBlock);
470 0 : return CE_Failure;
471 : }
472 :
473 476 : if (VSIFReadL(pImage, 1, nBlockSize, m_fpVSIL) != nBlockSize)
474 : {
475 0 : CPLError(CE_Failure, CPLE_FileIO,
476 : "Failed to read %d bytes for tile %d,%d.",
477 : static_cast<int>(nBlockSize), nXBlock, nYBlock);
478 0 : return CE_Failure;
479 : }
480 :
481 476 : if (!m_bNativeOrder && eDataType != GDT_Byte)
482 0 : GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
483 :
484 476 : return CE_None;
485 : }
486 :
487 : /************************************************************************/
488 : /* RemapNoDataT() */
489 : /************************************************************************/
490 :
491 : template <class T>
492 6 : static void RemapNoDataT(T *pBuffer, int nItems, T srcNoData, T dstNoData)
493 : {
494 67542 : for (int i = 0; i < nItems; i++)
495 : {
496 67536 : if (pBuffer[i] == srcNoData)
497 6 : pBuffer[i] = dstNoData;
498 : }
499 6 : }
500 :
501 : /************************************************************************/
502 : /* RemapNoData() */
503 : /************************************************************************/
504 :
505 6 : static void RemapNoData(GDALDataType eDataType, void *pBuffer, int nItems,
506 : double dfSrcNoData, double dfDstNoData)
507 : {
508 6 : if (eDataType == GDT_Byte)
509 : {
510 3 : RemapNoDataT(reinterpret_cast<GByte *>(pBuffer), nItems,
511 3 : static_cast<GByte>(dfSrcNoData),
512 3 : static_cast<GByte>(dfDstNoData));
513 : }
514 3 : else if (eDataType == GDT_UInt16)
515 : {
516 1 : RemapNoDataT(reinterpret_cast<GUInt16 *>(pBuffer), nItems,
517 1 : static_cast<GUInt16>(dfSrcNoData),
518 1 : static_cast<GUInt16>(dfDstNoData));
519 : }
520 2 : else if (eDataType == GDT_Int16)
521 : {
522 1 : RemapNoDataT(reinterpret_cast<GInt16 *>(pBuffer), nItems,
523 1 : static_cast<GInt16>(dfSrcNoData),
524 1 : static_cast<GInt16>(dfDstNoData));
525 : }
526 : else
527 : {
528 1 : CPLAssert(eDataType == GDT_Float32);
529 1 : RemapNoDataT(reinterpret_cast<float *>(pBuffer), nItems,
530 : static_cast<float>(dfSrcNoData),
531 : static_cast<float>(dfDstNoData));
532 : }
533 6 : }
534 :
535 : /************************************************************************/
536 : /* IReadBlock() */
537 : /************************************************************************/
538 :
539 225 : CPLErr ISISTiledBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
540 :
541 : {
542 225 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
543 225 : if (poGDS->m_osExternalFilename.empty())
544 : {
545 223 : if (!poGDS->m_bIsLabelWritten)
546 2 : poGDS->WriteLabel();
547 : }
548 :
549 225 : if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
550 : {
551 1 : RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
552 : poGDS->m_dfSrcNoData, m_dfNoData);
553 : }
554 :
555 225 : const GIntBig nOffset = m_nFirstTileOffset + nXBlock * m_nXTileOffset +
556 225 : nYBlock * m_nYTileOffset;
557 225 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
558 225 : const size_t nBlockSize =
559 225 : static_cast<size_t>(nDTSize) * nBlockXSize * nBlockYSize;
560 :
561 225 : const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
562 225 : const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
563 :
564 : // Pad partial blocks to nodata value
565 225 : if (nXBlock == l_nBlocksPerRow - 1 && (nRasterXSize % nBlockXSize) != 0)
566 : {
567 24 : GByte *pabyImage = static_cast<GByte *>(pImage);
568 24 : int nXStart = nRasterXSize % nBlockXSize;
569 1688 : for (int iY = 0; iY < nBlockYSize; iY++)
570 : {
571 1664 : GDALCopyWords(&m_dfNoData, GDT_Float64, 0,
572 1664 : pabyImage + (iY * nBlockXSize + nXStart) * nDTSize,
573 1664 : eDataType, nDTSize, nBlockXSize - nXStart);
574 : }
575 : }
576 225 : if (nYBlock == l_nBlocksPerColumn - 1 && (nRasterYSize % nBlockYSize) != 0)
577 : {
578 49 : GByte *pabyImage = static_cast<GByte *>(pImage);
579 1685 : for (int iY = nRasterYSize % nBlockYSize; iY < nBlockYSize; iY++)
580 : {
581 1636 : GDALCopyWords(&m_dfNoData, GDT_Float64, 0,
582 1636 : pabyImage + iY * nBlockXSize * nDTSize, eDataType,
583 : nDTSize, nBlockXSize);
584 : }
585 : }
586 :
587 225 : if (VSIFSeekL(m_fpVSIL, nOffset, SEEK_SET) != 0)
588 : {
589 0 : CPLError(CE_Failure, CPLE_FileIO,
590 : "Failed to seek to offset %d to read tile %d,%d.",
591 : static_cast<int>(nOffset), nXBlock, nYBlock);
592 0 : return CE_Failure;
593 : }
594 :
595 225 : if (!m_bNativeOrder && eDataType != GDT_Byte)
596 0 : GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
597 :
598 225 : if (VSIFWriteL(pImage, 1, nBlockSize, m_fpVSIL) != nBlockSize)
599 : {
600 0 : CPLError(CE_Failure, CPLE_FileIO,
601 : "Failed to write %d bytes for tile %d,%d.",
602 : static_cast<int>(nBlockSize), nXBlock, nYBlock);
603 0 : return CE_Failure;
604 : }
605 :
606 225 : if (!m_bNativeOrder && eDataType != GDT_Byte)
607 0 : GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
608 :
609 225 : return CE_None;
610 : }
611 :
612 : /************************************************************************/
613 : /* SetMaskBand() */
614 : /************************************************************************/
615 :
616 41 : void ISISTiledBand::SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand)
617 : {
618 41 : poMask.reset(std::move(poMaskBand));
619 41 : nMaskFlags = 0;
620 41 : }
621 :
622 : /************************************************************************/
623 : /* GetOffset() */
624 : /************************************************************************/
625 :
626 14 : double ISISTiledBand::GetOffset(int *pbSuccess)
627 : {
628 14 : if (pbSuccess)
629 4 : *pbSuccess = m_bHasOffset;
630 14 : return m_dfOffset;
631 : }
632 :
633 : /************************************************************************/
634 : /* GetScale() */
635 : /************************************************************************/
636 :
637 14 : double ISISTiledBand::GetScale(int *pbSuccess)
638 : {
639 14 : if (pbSuccess)
640 4 : *pbSuccess = m_bHasScale;
641 14 : return m_dfScale;
642 : }
643 :
644 : /************************************************************************/
645 : /* SetOffset() */
646 : /************************************************************************/
647 :
648 14 : CPLErr ISISTiledBand::SetOffset(double dfNewOffset)
649 : {
650 14 : m_dfOffset = dfNewOffset;
651 14 : m_bHasOffset = true;
652 14 : return CE_None;
653 : }
654 :
655 : /************************************************************************/
656 : /* SetScale() */
657 : /************************************************************************/
658 :
659 14 : CPLErr ISISTiledBand::SetScale(double dfNewScale)
660 : {
661 14 : m_dfScale = dfNewScale;
662 14 : m_bHasScale = true;
663 14 : return CE_None;
664 : }
665 :
666 : /************************************************************************/
667 : /* GetNoDataValue() */
668 : /************************************************************************/
669 :
670 12 : double ISISTiledBand::GetNoDataValue(int *pbSuccess)
671 : {
672 12 : if (pbSuccess)
673 8 : *pbSuccess = true;
674 12 : return m_dfNoData;
675 : }
676 :
677 : /************************************************************************/
678 : /* SetNoDataValue() */
679 : /************************************************************************/
680 :
681 51 : CPLErr ISISTiledBand::SetNoDataValue(double dfNewNoData)
682 : {
683 51 : m_dfNoData = dfNewNoData;
684 51 : return CE_None;
685 : }
686 :
687 : /************************************************************************/
688 : /* ISIS3RawRasterBand() */
689 : /************************************************************************/
690 :
691 366 : ISIS3RawRasterBand::ISIS3RawRasterBand(GDALDataset *l_poDS, int l_nBand,
692 : VSILFILE *l_fpRaw,
693 : vsi_l_offset l_nImgOffset,
694 : int l_nPixelOffset, int l_nLineOffset,
695 : GDALDataType l_eDataType,
696 366 : int l_bNativeOrder)
697 : : RawRasterBand(l_poDS, l_nBand, l_fpRaw, l_nImgOffset, l_nPixelOffset,
698 : l_nLineOffset, l_eDataType, l_bNativeOrder,
699 : RawRasterBand::OwnFP::NO),
700 : m_bHasOffset(false), m_bHasScale(false), m_dfOffset(0.0), m_dfScale(1.0),
701 366 : m_dfNoData(0.0)
702 : {
703 366 : }
704 :
705 : /************************************************************************/
706 : /* IReadBlock() */
707 : /************************************************************************/
708 :
709 2071 : CPLErr ISIS3RawRasterBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
710 :
711 : {
712 2071 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
713 2071 : if (poGDS->m_osExternalFilename.empty())
714 : {
715 930 : if (!poGDS->m_bIsLabelWritten)
716 0 : poGDS->WriteLabel();
717 : }
718 2071 : return RawRasterBand::IReadBlock(nXBlock, nYBlock, pImage);
719 : }
720 :
721 : /************************************************************************/
722 : /* IWriteBlock() */
723 : /************************************************************************/
724 :
725 856 : CPLErr ISIS3RawRasterBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
726 :
727 : {
728 856 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
729 856 : if (poGDS->m_osExternalFilename.empty())
730 : {
731 796 : if (!poGDS->m_bIsLabelWritten)
732 0 : poGDS->WriteLabel();
733 : }
734 :
735 856 : if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
736 : {
737 0 : RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
738 : poGDS->m_dfSrcNoData, m_dfNoData);
739 : }
740 :
741 856 : return RawRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
742 : }
743 :
744 : /************************************************************************/
745 : /* IRasterIO() */
746 : /************************************************************************/
747 :
748 1939 : CPLErr ISIS3RawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
749 : int nXSize, int nYSize, void *pData,
750 : int nBufXSize, int nBufYSize,
751 : GDALDataType eBufType,
752 : GSpacing nPixelSpace, GSpacing nLineSpace,
753 : GDALRasterIOExtraArg *psExtraArg)
754 :
755 : {
756 1939 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
757 1939 : if (poGDS->m_osExternalFilename.empty())
758 : {
759 1136 : if (!poGDS->m_bIsLabelWritten)
760 49 : poGDS->WriteLabel();
761 : }
762 1939 : if (eRWFlag == GF_Write && poGDS->m_bHasSrcNoData &&
763 27 : poGDS->m_dfSrcNoData != m_dfNoData)
764 : {
765 4 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
766 4 : if (eBufType == eDataType && nPixelSpace == nDTSize &&
767 4 : nLineSpace == nPixelSpace * nBufXSize)
768 : {
769 4 : RemapNoData(eDataType, pData, nBufXSize * nBufYSize,
770 : poGDS->m_dfSrcNoData, m_dfNoData);
771 : }
772 : else
773 : {
774 0 : const GByte *pabySrc = reinterpret_cast<GByte *>(pData);
775 : GByte *pabyTemp = reinterpret_cast<GByte *>(
776 0 : VSI_MALLOC3_VERBOSE(nDTSize, nBufXSize, nBufYSize));
777 0 : for (int i = 0; i < nBufYSize; i++)
778 : {
779 0 : GDALCopyWords(pabySrc + i * nLineSpace, eBufType,
780 : static_cast<int>(nPixelSpace),
781 0 : pabyTemp + i * nBufXSize * nDTSize, eDataType,
782 : nDTSize, nBufXSize);
783 : }
784 0 : RemapNoData(eDataType, pabyTemp, nBufXSize * nBufYSize,
785 : poGDS->m_dfSrcNoData, m_dfNoData);
786 0 : CPLErr eErr = RawRasterBand::IRasterIO(
787 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nBufXSize,
788 : nBufYSize, eDataType, nDTSize,
789 0 : static_cast<GSpacing>(nDTSize) * nBufXSize, psExtraArg);
790 0 : VSIFree(pabyTemp);
791 0 : return eErr;
792 : }
793 : }
794 1939 : return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
795 : pData, nBufXSize, nBufYSize, eBufType,
796 1939 : nPixelSpace, nLineSpace, psExtraArg);
797 : }
798 :
799 : /************************************************************************/
800 : /* SetMaskBand() */
801 : /************************************************************************/
802 :
803 234 : void ISIS3RawRasterBand::SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand)
804 : {
805 234 : poMask.reset(std::move(poMaskBand));
806 234 : nMaskFlags = 0;
807 234 : }
808 :
809 : /************************************************************************/
810 : /* GetOffset() */
811 : /************************************************************************/
812 :
813 157 : double ISIS3RawRasterBand::GetOffset(int *pbSuccess)
814 : {
815 157 : if (pbSuccess)
816 24 : *pbSuccess = m_bHasOffset;
817 157 : return m_dfOffset;
818 : }
819 :
820 : /************************************************************************/
821 : /* GetScale() */
822 : /************************************************************************/
823 :
824 157 : double ISIS3RawRasterBand::GetScale(int *pbSuccess)
825 : {
826 157 : if (pbSuccess)
827 24 : *pbSuccess = m_bHasScale;
828 157 : return m_dfScale;
829 : }
830 :
831 : /************************************************************************/
832 : /* SetOffset() */
833 : /************************************************************************/
834 :
835 51 : CPLErr ISIS3RawRasterBand::SetOffset(double dfNewOffset)
836 : {
837 51 : m_dfOffset = dfNewOffset;
838 51 : m_bHasOffset = true;
839 51 : return CE_None;
840 : }
841 :
842 : /************************************************************************/
843 : /* SetScale() */
844 : /************************************************************************/
845 :
846 51 : CPLErr ISIS3RawRasterBand::SetScale(double dfNewScale)
847 : {
848 51 : m_dfScale = dfNewScale;
849 51 : m_bHasScale = true;
850 51 : return CE_None;
851 : }
852 :
853 : /************************************************************************/
854 : /* GetNoDataValue() */
855 : /************************************************************************/
856 :
857 138 : double ISIS3RawRasterBand::GetNoDataValue(int *pbSuccess)
858 : {
859 138 : if (pbSuccess)
860 87 : *pbSuccess = true;
861 138 : return m_dfNoData;
862 : }
863 :
864 : /************************************************************************/
865 : /* SetNoDataValue() */
866 : /************************************************************************/
867 :
868 367 : CPLErr ISIS3RawRasterBand::SetNoDataValue(double dfNewNoData)
869 : {
870 367 : m_dfNoData = dfNewNoData;
871 367 : return CE_None;
872 : }
873 :
874 : /************************************************************************/
875 : /* ISIS3WrapperRasterBand() */
876 : /************************************************************************/
877 :
878 45 : ISIS3WrapperRasterBand::ISIS3WrapperRasterBand(GDALRasterBand *poBaseBandIn)
879 : : m_poBaseBand(poBaseBandIn), m_bHasOffset(false), m_bHasScale(false),
880 45 : m_dfOffset(0.0), m_dfScale(1.0), m_dfNoData(0.0)
881 : {
882 45 : eDataType = m_poBaseBand->GetRasterDataType();
883 45 : m_poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
884 45 : }
885 :
886 : /************************************************************************/
887 : /* SetMaskBand() */
888 : /************************************************************************/
889 :
890 21 : void ISIS3WrapperRasterBand::SetMaskBand(
891 : std::unique_ptr<GDALRasterBand> poMaskBand)
892 : {
893 21 : poMask.reset(std::move(poMaskBand));
894 21 : nMaskFlags = 0;
895 21 : }
896 :
897 : /************************************************************************/
898 : /* GetOffset() */
899 : /************************************************************************/
900 :
901 22 : double ISIS3WrapperRasterBand::GetOffset(int *pbSuccess)
902 : {
903 22 : if (pbSuccess)
904 4 : *pbSuccess = m_bHasOffset;
905 22 : return m_dfOffset;
906 : }
907 :
908 : /************************************************************************/
909 : /* GetScale() */
910 : /************************************************************************/
911 :
912 22 : double ISIS3WrapperRasterBand::GetScale(int *pbSuccess)
913 : {
914 22 : if (pbSuccess)
915 4 : *pbSuccess = m_bHasScale;
916 22 : return m_dfScale;
917 : }
918 :
919 : /************************************************************************/
920 : /* SetOffset() */
921 : /************************************************************************/
922 :
923 12 : CPLErr ISIS3WrapperRasterBand::SetOffset(double dfNewOffset)
924 : {
925 12 : m_dfOffset = dfNewOffset;
926 12 : m_bHasOffset = true;
927 :
928 12 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
929 12 : if (poGDS->m_poExternalDS && eAccess == GA_Update)
930 4 : poGDS->m_poExternalDS->GetRasterBand(nBand)->SetOffset(dfNewOffset);
931 :
932 12 : return CE_None;
933 : }
934 :
935 : /************************************************************************/
936 : /* SetScale() */
937 : /************************************************************************/
938 :
939 12 : CPLErr ISIS3WrapperRasterBand::SetScale(double dfNewScale)
940 : {
941 12 : m_dfScale = dfNewScale;
942 12 : m_bHasScale = true;
943 :
944 12 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
945 12 : if (poGDS->m_poExternalDS && eAccess == GA_Update)
946 4 : poGDS->m_poExternalDS->GetRasterBand(nBand)->SetScale(dfNewScale);
947 :
948 12 : return CE_None;
949 : }
950 :
951 : /************************************************************************/
952 : /* GetNoDataValue() */
953 : /************************************************************************/
954 :
955 4 : double ISIS3WrapperRasterBand::GetNoDataValue(int *pbSuccess)
956 : {
957 4 : if (pbSuccess)
958 4 : *pbSuccess = true;
959 4 : return m_dfNoData;
960 : }
961 :
962 : /************************************************************************/
963 : /* SetNoDataValue() */
964 : /************************************************************************/
965 :
966 45 : CPLErr ISIS3WrapperRasterBand::SetNoDataValue(double dfNewNoData)
967 : {
968 45 : m_dfNoData = dfNewNoData;
969 :
970 45 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
971 45 : if (poGDS->m_poExternalDS && eAccess == GA_Update)
972 25 : poGDS->m_poExternalDS->GetRasterBand(nBand)->SetNoDataValue(
973 25 : dfNewNoData);
974 :
975 45 : return CE_None;
976 : }
977 :
978 : /************************************************************************/
979 : /* InitFile() */
980 : /************************************************************************/
981 :
982 8 : void ISIS3WrapperRasterBand::InitFile()
983 : {
984 8 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
985 8 : if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
986 : {
987 8 : poGDS->m_bGeoTIFFInitDone = true;
988 :
989 8 : const int nBands = poGDS->GetRasterCount();
990 : // We need to make sure that blocks are written in the right order
991 22 : for (int i = 0; i < nBands; i++)
992 : {
993 14 : poGDS->m_poExternalDS->GetRasterBand(i + 1)->Fill(m_dfNoData);
994 : }
995 8 : poGDS->m_poExternalDS->FlushCache(false);
996 :
997 : // Check that blocks are effectively written in expected order.
998 : const int nBlockSizeBytes =
999 8 : nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType);
1000 :
1001 8 : GIntBig nLastOffset = 0;
1002 8 : bool bGoOn = true;
1003 8 : const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
1004 8 : const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
1005 22 : for (int i = 0; i < nBands && bGoOn; i++)
1006 : {
1007 641 : for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
1008 : {
1009 1473 : for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
1010 : {
1011 : const char *pszBlockOffset =
1012 846 : poGDS->m_poExternalDS->GetRasterBand(i + 1)
1013 846 : ->GetMetadataItem(
1014 846 : CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y), "TIFF");
1015 846 : if (pszBlockOffset)
1016 : {
1017 846 : GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
1018 846 : if (i != 0 || x != 0 || y != 0)
1019 : {
1020 838 : if (nOffset != nLastOffset + nBlockSizeBytes)
1021 : {
1022 0 : CPLError(CE_Warning, CPLE_AppDefined,
1023 : "Block %d,%d band %d not at expected "
1024 : "offset",
1025 : x, y, i + 1);
1026 0 : bGoOn = false;
1027 0 : poGDS->m_bGeoTIFFAsRegularExternal = false;
1028 : }
1029 : }
1030 846 : nLastOffset = nOffset;
1031 : }
1032 : else
1033 : {
1034 0 : CPLError(CE_Warning, CPLE_AppDefined,
1035 : "Block %d,%d band %d not at expected "
1036 : "offset",
1037 : x, y, i + 1);
1038 0 : bGoOn = false;
1039 0 : poGDS->m_bGeoTIFFAsRegularExternal = false;
1040 : }
1041 : }
1042 : }
1043 : }
1044 : }
1045 8 : }
1046 :
1047 : /************************************************************************/
1048 : /* Fill() */
1049 : /************************************************************************/
1050 :
1051 4 : CPLErr ISIS3WrapperRasterBand::Fill(double dfRealValue, double dfImaginaryValue)
1052 : {
1053 4 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
1054 4 : if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData == dfRealValue)
1055 : {
1056 0 : dfRealValue = m_dfNoData;
1057 : }
1058 4 : if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
1059 : {
1060 1 : InitFile();
1061 : }
1062 :
1063 4 : return GDALProxyRasterBand::Fill(dfRealValue, dfImaginaryValue);
1064 : }
1065 :
1066 : /************************************************************************/
1067 : /* IWriteBlock() */
1068 : /************************************************************************/
1069 :
1070 0 : CPLErr ISIS3WrapperRasterBand::IWriteBlock(int nXBlock, int nYBlock,
1071 : void *pImage)
1072 :
1073 : {
1074 0 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
1075 0 : if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
1076 : {
1077 0 : RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
1078 : poGDS->m_dfSrcNoData, m_dfNoData);
1079 : }
1080 0 : if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
1081 : {
1082 0 : InitFile();
1083 : }
1084 :
1085 0 : return GDALProxyRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
1086 : }
1087 :
1088 : /************************************************************************/
1089 : /* IRasterIO() */
1090 : /************************************************************************/
1091 :
1092 46 : CPLErr ISIS3WrapperRasterBand::IRasterIO(
1093 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1094 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1095 : GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
1096 :
1097 : {
1098 46 : ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
1099 46 : if (eRWFlag == GF_Write && poGDS->m_bGeoTIFFAsRegularExternal &&
1100 17 : !poGDS->m_bGeoTIFFInitDone)
1101 : {
1102 6 : InitFile();
1103 : }
1104 46 : if (eRWFlag == GF_Write && poGDS->m_bHasSrcNoData &&
1105 9 : poGDS->m_dfSrcNoData != m_dfNoData)
1106 : {
1107 1 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1108 1 : if (eBufType == eDataType && nPixelSpace == nDTSize &&
1109 1 : nLineSpace == nPixelSpace * nBufXSize)
1110 : {
1111 1 : RemapNoData(eDataType, pData, nBufXSize * nBufYSize,
1112 : poGDS->m_dfSrcNoData, m_dfNoData);
1113 : }
1114 : else
1115 : {
1116 0 : const GByte *pabySrc = reinterpret_cast<GByte *>(pData);
1117 : GByte *pabyTemp = reinterpret_cast<GByte *>(
1118 0 : VSI_MALLOC3_VERBOSE(nDTSize, nBufXSize, nBufYSize));
1119 0 : for (int i = 0; i < nBufYSize; i++)
1120 : {
1121 0 : GDALCopyWords(pabySrc + i * nLineSpace, eBufType,
1122 : static_cast<int>(nPixelSpace),
1123 0 : pabyTemp + i * nBufXSize * nDTSize, eDataType,
1124 : nDTSize, nBufXSize);
1125 : }
1126 0 : RemapNoData(eDataType, pabyTemp, nBufXSize * nBufYSize,
1127 : poGDS->m_dfSrcNoData, m_dfNoData);
1128 0 : CPLErr eErr = GDALProxyRasterBand::IRasterIO(
1129 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nBufXSize,
1130 : nBufYSize, eDataType, nDTSize,
1131 0 : static_cast<GSpacing>(nDTSize) * nBufXSize, psExtraArg);
1132 0 : VSIFree(pabyTemp);
1133 0 : return eErr;
1134 : }
1135 : }
1136 46 : return GDALProxyRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1137 : pData, nBufXSize, nBufYSize, eBufType,
1138 46 : nPixelSpace, nLineSpace, psExtraArg);
1139 : }
1140 :
1141 : /************************************************************************/
1142 : /* ISISMaskBand() */
1143 : /************************************************************************/
1144 :
1145 296 : ISISMaskBand::ISISMaskBand(GDALRasterBand *poBaseBand)
1146 296 : : m_poBaseBand(poBaseBand), m_pBuffer(nullptr)
1147 : {
1148 296 : eDataType = GDT_Byte;
1149 296 : poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1150 296 : nRasterXSize = poBaseBand->GetXSize();
1151 296 : nRasterYSize = poBaseBand->GetYSize();
1152 296 : }
1153 :
1154 : /************************************************************************/
1155 : /* ~ISISMaskBand() */
1156 : /************************************************************************/
1157 :
1158 592 : ISISMaskBand::~ISISMaskBand()
1159 : {
1160 296 : VSIFree(m_pBuffer);
1161 592 : }
1162 :
1163 : /************************************************************************/
1164 : /* FillMask() */
1165 : /************************************************************************/
1166 :
1167 : template <class T>
1168 68 : static void FillMask(void *pvBuffer, GByte *pabyDst, int nReqXSize,
1169 : int nReqYSize, int nBlockXSize, T NULL_VAL, T LOW_REPR_SAT,
1170 : T LOW_INSTR_SAT, T HIGH_INSTR_SAT, T HIGH_REPR_SAT)
1171 : {
1172 68 : const T *pSrc = static_cast<T *>(pvBuffer);
1173 140 : for (int y = 0; y < nReqYSize; y++)
1174 : {
1175 9624 : for (int x = 0; x < nReqXSize; x++)
1176 : {
1177 9552 : const T nSrc = pSrc[y * nBlockXSize + x];
1178 9552 : if (nSrc == NULL_VAL || nSrc == LOW_REPR_SAT ||
1179 6338 : nSrc == LOW_INSTR_SAT || nSrc == HIGH_INSTR_SAT ||
1180 : nSrc == HIGH_REPR_SAT)
1181 : {
1182 3214 : pabyDst[y * nBlockXSize + x] = 0;
1183 : }
1184 : else
1185 : {
1186 6338 : pabyDst[y * nBlockXSize + x] = 255;
1187 : }
1188 : }
1189 : }
1190 68 : }
1191 :
1192 : /************************************************************************/
1193 : /* IReadBlock() */
1194 : /************************************************************************/
1195 :
1196 68 : CPLErr ISISMaskBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
1197 :
1198 : {
1199 68 : const GDALDataType eSrcDT = m_poBaseBand->GetRasterDataType();
1200 68 : const int nSrcDTSize = GDALGetDataTypeSizeBytes(eSrcDT);
1201 68 : if (m_pBuffer == nullptr)
1202 : {
1203 23 : m_pBuffer = VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, nSrcDTSize);
1204 23 : if (m_pBuffer == nullptr)
1205 0 : return CE_Failure;
1206 : }
1207 :
1208 68 : int nXOff = nXBlock * nBlockXSize;
1209 68 : int nReqXSize = nBlockXSize;
1210 68 : if (nXOff + nReqXSize > nRasterXSize)
1211 4 : nReqXSize = nRasterXSize - nXOff;
1212 68 : int nYOff = nYBlock * nBlockYSize;
1213 68 : int nReqYSize = nBlockYSize;
1214 68 : if (nYOff + nReqYSize > nRasterYSize)
1215 4 : nReqYSize = nRasterYSize - nYOff;
1216 :
1217 136 : if (m_poBaseBand->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
1218 : m_pBuffer, nReqXSize, nReqYSize, eSrcDT,
1219 : nSrcDTSize,
1220 68 : static_cast<GSpacing>(nSrcDTSize) * nBlockXSize,
1221 68 : nullptr) != CE_None)
1222 : {
1223 0 : return CE_Failure;
1224 : }
1225 :
1226 68 : GByte *pabyDst = static_cast<GByte *>(pImage);
1227 68 : if (eSrcDT == GDT_Byte)
1228 : {
1229 44 : FillMask<GByte>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
1230 : ISIS3_NULL1, LOW_REPR_SAT1, LOW_INSTR_SAT1,
1231 : HIGH_INSTR_SAT1, HIGH_REPR_SAT1);
1232 : }
1233 24 : else if (eSrcDT == GDT_UInt16)
1234 : {
1235 8 : FillMask<GUInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
1236 : ISIS3_NULLU2, LOW_REPR_SATU2, LOW_INSTR_SATU2,
1237 : HIGH_INSTR_SATU2, HIGH_REPR_SATU2);
1238 : }
1239 16 : else if (eSrcDT == GDT_Int16)
1240 : {
1241 8 : FillMask<GInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
1242 : ISIS3_NULL2, LOW_REPR_SAT2, LOW_INSTR_SAT2,
1243 : HIGH_INSTR_SAT2, HIGH_REPR_SAT2);
1244 : }
1245 : else
1246 : {
1247 8 : CPLAssert(eSrcDT == GDT_Float32);
1248 8 : FillMask<float>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
1249 : ISIS3_NULL4, LOW_REPR_SAT4, LOW_INSTR_SAT4,
1250 : HIGH_INSTR_SAT4, HIGH_REPR_SAT4);
1251 : }
1252 :
1253 68 : return CE_None;
1254 : }
1255 :
1256 : /************************************************************************/
1257 : /* ISIS3Dataset() */
1258 : /************************************************************************/
1259 :
1260 385 : ISIS3Dataset::ISIS3Dataset()
1261 : : m_fpLabel(nullptr), m_fpImage(nullptr), m_poExternalDS(nullptr),
1262 : m_bGeoTIFFAsRegularExternal(false), m_bGeoTIFFInitDone(true),
1263 : m_bIsLabelWritten(true), m_bIsTiled(false), m_bInitToNodata(false),
1264 : m_bGotTransform(false), m_bHasSrcNoData(false), m_dfSrcNoData(0.0),
1265 : m_bForce360(false), m_bWriteBoundingDegrees(true), m_bUseSrcLabel(true),
1266 385 : m_bUseSrcMapping(false), m_bUseSrcHistory(true), m_bAddGDALHistory(true)
1267 : {
1268 385 : m_oKeywords.SetStripSurroundingQuotes(true);
1269 385 : m_adfGeoTransform[0] = 0.0;
1270 385 : m_adfGeoTransform[1] = 1.0;
1271 385 : m_adfGeoTransform[2] = 0.0;
1272 385 : m_adfGeoTransform[3] = 0.0;
1273 385 : m_adfGeoTransform[4] = 0.0;
1274 385 : m_adfGeoTransform[5] = 1.0;
1275 :
1276 : // Deinit JSON objects
1277 385 : m_oJSonLabel.Deinit();
1278 385 : m_oSrcJSonLabel.Deinit();
1279 385 : }
1280 :
1281 : /************************************************************************/
1282 : /* ~ISIS3Dataset() */
1283 : /************************************************************************/
1284 :
1285 770 : ISIS3Dataset::~ISIS3Dataset()
1286 :
1287 : {
1288 385 : ISIS3Dataset::Close();
1289 770 : }
1290 :
1291 : /************************************************************************/
1292 : /* Close() */
1293 : /************************************************************************/
1294 :
1295 745 : CPLErr ISIS3Dataset::Close()
1296 : {
1297 745 : CPLErr eErr = CE_None;
1298 745 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
1299 : {
1300 385 : if (!m_bIsLabelWritten)
1301 72 : WriteLabel();
1302 385 : if (m_poExternalDS && m_bGeoTIFFAsRegularExternal &&
1303 8 : !m_bGeoTIFFInitDone)
1304 : {
1305 0 : reinterpret_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
1306 0 : ->InitFile();
1307 : }
1308 385 : if (ISIS3Dataset::FlushCache(true) != CE_None)
1309 0 : eErr = CE_Failure;
1310 385 : if (m_fpLabel)
1311 : {
1312 125 : if (VSIFCloseL(m_fpLabel) != 0)
1313 0 : eErr = CE_Failure;
1314 : }
1315 385 : if (m_fpImage && m_fpImage != m_fpLabel)
1316 : {
1317 238 : if (VSIFCloseL(m_fpImage) != 0)
1318 0 : eErr = CE_Failure;
1319 : }
1320 :
1321 385 : ISIS3Dataset::CloseDependentDatasets();
1322 385 : if (GDALPamDataset::Close() != CE_None)
1323 0 : eErr = CE_Failure;
1324 : }
1325 745 : return eErr;
1326 : }
1327 :
1328 : /************************************************************************/
1329 : /* CloseDependentDatasets() */
1330 : /************************************************************************/
1331 :
1332 385 : int ISIS3Dataset::CloseDependentDatasets()
1333 : {
1334 385 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
1335 :
1336 385 : if (m_poExternalDS)
1337 : {
1338 43 : bHasDroppedRef = FALSE;
1339 43 : delete m_poExternalDS;
1340 43 : m_poExternalDS = nullptr;
1341 : }
1342 :
1343 847 : for (int iBand = 0; iBand < nBands; iBand++)
1344 : {
1345 462 : delete papoBands[iBand];
1346 : }
1347 385 : nBands = 0;
1348 :
1349 385 : return bHasDroppedRef;
1350 : }
1351 :
1352 : /************************************************************************/
1353 : /* GetFileList() */
1354 : /************************************************************************/
1355 :
1356 97 : char **ISIS3Dataset::GetFileList()
1357 :
1358 : {
1359 97 : char **papszFileList = GDALPamDataset::GetFileList();
1360 :
1361 97 : if (!m_osExternalFilename.empty())
1362 31 : papszFileList = CSLAddString(papszFileList, m_osExternalFilename);
1363 127 : for (int i = 0; i < m_aosAdditionalFiles.Count(); ++i)
1364 : {
1365 30 : if (CSLFindString(papszFileList, m_aosAdditionalFiles[i]) < 0)
1366 : {
1367 : papszFileList =
1368 30 : CSLAddString(papszFileList, m_aosAdditionalFiles[i]);
1369 : }
1370 : }
1371 :
1372 97 : return papszFileList;
1373 : }
1374 :
1375 : /************************************************************************/
1376 : /* GetSpatialRef() */
1377 : /************************************************************************/
1378 :
1379 59 : const OGRSpatialReference *ISIS3Dataset::GetSpatialRef() const
1380 :
1381 : {
1382 59 : if (!m_oSRS.IsEmpty())
1383 39 : return &m_oSRS;
1384 :
1385 20 : return GDALPamDataset::GetSpatialRef();
1386 : }
1387 :
1388 : /************************************************************************/
1389 : /* SetSpatialRef() */
1390 : /************************************************************************/
1391 :
1392 61 : CPLErr ISIS3Dataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1393 : {
1394 61 : if (eAccess == GA_ReadOnly)
1395 0 : return GDALPamDataset::SetSpatialRef(poSRS);
1396 61 : if (poSRS)
1397 61 : m_oSRS = *poSRS;
1398 : else
1399 0 : m_oSRS.Clear();
1400 61 : if (m_poExternalDS)
1401 6 : m_poExternalDS->SetSpatialRef(poSRS);
1402 61 : InvalidateLabel();
1403 61 : return CE_None;
1404 : }
1405 :
1406 : /************************************************************************/
1407 : /* GetGeoTransform() */
1408 : /************************************************************************/
1409 :
1410 78 : CPLErr ISIS3Dataset::GetGeoTransform(double *padfTransform)
1411 :
1412 : {
1413 78 : if (m_bGotTransform)
1414 : {
1415 59 : memcpy(padfTransform, m_adfGeoTransform, sizeof(double) * 6);
1416 59 : return CE_None;
1417 : }
1418 :
1419 19 : return GDALPamDataset::GetGeoTransform(padfTransform);
1420 : }
1421 :
1422 : /************************************************************************/
1423 : /* SetGeoTransform() */
1424 : /************************************************************************/
1425 :
1426 53 : CPLErr ISIS3Dataset::SetGeoTransform(double *padfTransform)
1427 :
1428 : {
1429 53 : if (eAccess == GA_ReadOnly)
1430 0 : return GDALPamDataset::SetGeoTransform(padfTransform);
1431 53 : if (padfTransform[1] <= 0.0 || padfTransform[1] != -padfTransform[5] ||
1432 53 : padfTransform[2] != 0.0 || padfTransform[4] != 0.0)
1433 : {
1434 0 : CPLError(CE_Failure, CPLE_NotSupported,
1435 : "Only north-up geotransform with square pixels supported");
1436 0 : return CE_Failure;
1437 : }
1438 53 : m_bGotTransform = true;
1439 53 : memcpy(m_adfGeoTransform, padfTransform, sizeof(double) * 6);
1440 53 : if (m_poExternalDS)
1441 6 : m_poExternalDS->SetGeoTransform(padfTransform);
1442 53 : InvalidateLabel();
1443 53 : return CE_None;
1444 : }
1445 :
1446 : /************************************************************************/
1447 : /* GetMetadataDomainList() */
1448 : /************************************************************************/
1449 :
1450 1 : char **ISIS3Dataset::GetMetadataDomainList()
1451 : {
1452 1 : return BuildMetadataDomainList(nullptr, FALSE, "", "json:ISIS3", nullptr);
1453 : }
1454 :
1455 : /************************************************************************/
1456 : /* GetMetadata() */
1457 : /************************************************************************/
1458 :
1459 105 : char **ISIS3Dataset::GetMetadata(const char *pszDomain)
1460 : {
1461 105 : if (pszDomain != nullptr && EQUAL(pszDomain, "json:ISIS3"))
1462 : {
1463 53 : if (m_aosISIS3MD.empty())
1464 : {
1465 48 : if (eAccess == GA_Update && !m_oJSonLabel.IsValid())
1466 : {
1467 1 : BuildLabel();
1468 : }
1469 48 : CPLAssert(m_oJSonLabel.IsValid());
1470 : const CPLString osJson =
1471 96 : m_oJSonLabel.Format(CPLJSONObject::PrettyFormat::Pretty);
1472 48 : m_aosISIS3MD.InsertString(0, osJson.c_str());
1473 : }
1474 53 : return m_aosISIS3MD.List();
1475 : }
1476 52 : return GDALPamDataset::GetMetadata(pszDomain);
1477 : }
1478 :
1479 : /************************************************************************/
1480 : /* InvalidateLabel() */
1481 : /************************************************************************/
1482 :
1483 139 : void ISIS3Dataset::InvalidateLabel()
1484 : {
1485 139 : m_oJSonLabel.Deinit();
1486 139 : m_aosISIS3MD.Clear();
1487 139 : }
1488 :
1489 : /************************************************************************/
1490 : /* SetMetadata() */
1491 : /************************************************************************/
1492 :
1493 25 : CPLErr ISIS3Dataset::SetMetadata(char **papszMD, const char *pszDomain)
1494 : {
1495 25 : if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr &&
1496 25 : EQUAL(pszDomain, "json:ISIS3"))
1497 : {
1498 25 : m_oSrcJSonLabel.Deinit();
1499 25 : InvalidateLabel();
1500 25 : if (papszMD != nullptr && papszMD[0] != nullptr)
1501 : {
1502 25 : CPLJSONDocument oJSONDocument;
1503 25 : const GByte *pabyData = reinterpret_cast<const GByte *>(papszMD[0]);
1504 25 : if (!oJSONDocument.LoadMemory(pabyData))
1505 : {
1506 1 : return CE_Failure;
1507 : }
1508 :
1509 24 : m_oSrcJSonLabel = oJSONDocument.GetRoot();
1510 24 : if (!m_oSrcJSonLabel.IsValid())
1511 : {
1512 0 : return CE_Failure;
1513 : }
1514 : }
1515 24 : return CE_None;
1516 : }
1517 0 : return GDALPamDataset::SetMetadata(papszMD, pszDomain);
1518 : }
1519 :
1520 : /************************************************************************/
1521 : /* GetRawBinaryLayout() */
1522 : /************************************************************************/
1523 :
1524 2 : bool ISIS3Dataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1525 : {
1526 2 : if (m_sLayout.osRawFilename.empty())
1527 0 : return false;
1528 2 : sLayout = m_sLayout;
1529 2 : return true;
1530 : }
1531 :
1532 : /************************************************************************/
1533 : /* GetValueAndUnits() */
1534 : /************************************************************************/
1535 :
1536 50 : static void GetValueAndUnits(const CPLJSONObject &obj,
1537 : std::vector<double> &adfValues,
1538 : std::vector<std::string> &aosUnits,
1539 : int nExpectedVals)
1540 : {
1541 100 : if (obj.GetType() == CPLJSONObject::Type::Integer ||
1542 50 : obj.GetType() == CPLJSONObject::Type::Double)
1543 : {
1544 32 : adfValues.push_back(obj.ToDouble());
1545 : }
1546 18 : else if (obj.GetType() == CPLJSONObject::Type::Object)
1547 : {
1548 24 : auto oValue = obj.GetObj("value");
1549 24 : auto oUnit = obj.GetObj("unit");
1550 8 : if (oValue.IsValid() &&
1551 8 : (oValue.GetType() == CPLJSONObject::Type::Integer ||
1552 2 : oValue.GetType() == CPLJSONObject::Type::Double ||
1553 2 : oValue.GetType() == CPLJSONObject::Type::Array) &&
1554 16 : oUnit.IsValid() && oUnit.GetType() == CPLJSONObject::Type::String)
1555 : {
1556 8 : if (oValue.GetType() == CPLJSONObject::Type::Array)
1557 : {
1558 2 : GetValueAndUnits(oValue, adfValues, aosUnits, nExpectedVals);
1559 : }
1560 : else
1561 : {
1562 6 : adfValues.push_back(oValue.ToDouble());
1563 : }
1564 8 : aosUnits.push_back(oUnit.ToString());
1565 : }
1566 : }
1567 10 : else if (obj.GetType() == CPLJSONObject::Type::Array)
1568 : {
1569 10 : auto oArray = obj.ToArray();
1570 10 : if (oArray.Size() == nExpectedVals)
1571 : {
1572 34 : for (int i = 0; i < nExpectedVals; i++)
1573 : {
1574 60 : if (oArray[i].GetType() == CPLJSONObject::Type::Integer ||
1575 36 : oArray[i].GetType() == CPLJSONObject::Type::Double)
1576 : {
1577 24 : adfValues.push_back(oArray[i].ToDouble());
1578 : }
1579 : else
1580 : {
1581 0 : adfValues.clear();
1582 0 : return;
1583 : }
1584 : }
1585 : }
1586 : }
1587 : }
1588 :
1589 : /************************************************************************/
1590 : /* Open() */
1591 : /************************************************************************/
1592 :
1593 260 : GDALDataset *ISIS3Dataset::Open(GDALOpenInfo *poOpenInfo)
1594 :
1595 : {
1596 : /* -------------------------------------------------------------------- */
1597 : /* Does this look like a CUBE dataset? */
1598 : /* -------------------------------------------------------------------- */
1599 260 : if (!ISIS3DriverIdentify(poOpenInfo))
1600 0 : return nullptr;
1601 :
1602 : /* -------------------------------------------------------------------- */
1603 : /* Open the file using the large file API. */
1604 : /* -------------------------------------------------------------------- */
1605 520 : auto poDS = std::make_unique<ISIS3Dataset>();
1606 :
1607 260 : if (!poDS->m_oKeywords.Ingest(poOpenInfo->fpL, 0))
1608 : {
1609 1 : VSIFCloseL(poOpenInfo->fpL);
1610 1 : poOpenInfo->fpL = nullptr;
1611 1 : return nullptr;
1612 : }
1613 259 : poDS->m_oJSonLabel = poDS->m_oKeywords.GetJsonObject();
1614 259 : poDS->m_oJSonLabel.Add("_filename", poOpenInfo->pszFilename);
1615 :
1616 : // Find additional files from the label
1617 1292 : for (const CPLJSONObject &oObj : poDS->m_oJSonLabel.GetChildren())
1618 : {
1619 1033 : if (oObj.GetType() == CPLJSONObject::Type::Object)
1620 : {
1621 1548 : CPLString osContainerName = oObj.GetName();
1622 2322 : CPLJSONObject oContainerName = oObj.GetObj("_container_name");
1623 774 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
1624 : {
1625 24 : osContainerName = oContainerName.ToString();
1626 : }
1627 :
1628 1548 : CPLJSONObject oFilename = oObj.GetObj("^" + osContainerName);
1629 774 : if (oFilename.GetType() == CPLJSONObject::Type::String)
1630 : {
1631 : VSIStatBufL sStat;
1632 357 : const CPLString osFilename(CPLFormFilenameSafe(
1633 238 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
1634 476 : oFilename.ToString().c_str(), nullptr));
1635 119 : if (VSIStatL(osFilename, &sStat) == 0)
1636 : {
1637 81 : poDS->m_aosAdditionalFiles.AddString(osFilename);
1638 : }
1639 : else
1640 : {
1641 38 : CPLDebug("ISIS3", "File %s referenced but not foud",
1642 : osFilename.c_str());
1643 : }
1644 : }
1645 : }
1646 : }
1647 :
1648 259 : VSIFCloseL(poOpenInfo->fpL);
1649 259 : poOpenInfo->fpL = nullptr;
1650 :
1651 : /* -------------------------------------------------------------------- */
1652 : /* Assume user is pointing to label (i.e. .lbl) file for detached option */
1653 : /* -------------------------------------------------------------------- */
1654 : // Image can be inline or detached and point to an image name
1655 : // the Format can be Tiled or Raw
1656 : // Object = Core
1657 : // StartByte = 65537
1658 : // Format = Tile
1659 : // TileSamples = 128
1660 : // TileLines = 128
1661 : // OR-----
1662 : // Object = Core
1663 : // StartByte = 1
1664 : // ^Core = r0200357_detatched.cub
1665 : // Format = BandSequential
1666 : // OR-----
1667 : // Object = Core
1668 : // StartByte = 1
1669 : // ^Core = r0200357_detached_tiled.cub
1670 : // Format = Tile
1671 : // TileSamples = 128
1672 : // TileLines = 128
1673 : // OR-----
1674 : // Object = Core
1675 : // StartByte = 1
1676 : // ^Core = some.tif
1677 : // Format = GeoTIFF
1678 :
1679 : /* -------------------------------------------------------------------- */
1680 : /* What file contains the actual data? */
1681 : /* -------------------------------------------------------------------- */
1682 259 : const char *pszCore = poDS->GetKeyword("IsisCube.Core.^Core");
1683 : CPLString osQubeFile(
1684 259 : EQUAL(pszCore, "")
1685 356 : ? CPLString(poOpenInfo->pszFilename)
1686 : : CPLFormFilenameSafe(
1687 453 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str(), pszCore,
1688 518 : nullptr));
1689 259 : if (!EQUAL(pszCore, ""))
1690 : {
1691 97 : poDS->m_osExternalFilename = osQubeFile;
1692 : }
1693 :
1694 : /* -------------------------------------------------------------------- */
1695 : /* Check if file an ISIS3 header file? Read a few lines of text */
1696 : /* searching for something starting with nrows or ncols. */
1697 : /* -------------------------------------------------------------------- */
1698 :
1699 : /************* Skipbytes *****************************/
1700 259 : int nSkipBytes = atoi(poDS->GetKeyword("IsisCube.Core.StartByte", "1"));
1701 259 : if (nSkipBytes <= 1)
1702 91 : nSkipBytes = 0;
1703 : else
1704 168 : nSkipBytes -= 1;
1705 :
1706 : /******* Grab format type (BandSequential, Tiled) *******/
1707 518 : CPLString osFormat = poDS->GetKeyword("IsisCube.Core.Format");
1708 :
1709 259 : int tileSizeX = 0;
1710 259 : int tileSizeY = 0;
1711 :
1712 259 : if (EQUAL(osFormat, "Tile"))
1713 : {
1714 26 : poDS->m_bIsTiled = true;
1715 : /******* Get Tile Sizes *********/
1716 26 : tileSizeX = atoi(poDS->GetKeyword("IsisCube.Core.TileSamples"));
1717 26 : tileSizeY = atoi(poDS->GetKeyword("IsisCube.Core.TileLines"));
1718 26 : if (tileSizeX <= 0 || tileSizeY <= 0)
1719 : {
1720 1 : CPLError(CE_Failure, CPLE_OpenFailed,
1721 : "Wrong tile dimensions : %d x %d", tileSizeX, tileSizeY);
1722 1 : return nullptr;
1723 : }
1724 : }
1725 233 : else if (!EQUAL(osFormat, "BandSequential") && !EQUAL(osFormat, "GeoTIFF"))
1726 : {
1727 1 : CPLError(CE_Failure, CPLE_OpenFailed, "%s format not supported.",
1728 : osFormat.c_str());
1729 1 : return nullptr;
1730 : }
1731 :
1732 : /*********** Grab samples lines band ************/
1733 : const int nCols =
1734 257 : atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Samples"));
1735 257 : const int nRows = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Lines"));
1736 257 : const int nBands = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Bands"));
1737 :
1738 : /****** Grab format type - ISIS3 only supports 8,U16,S16,32 *****/
1739 257 : GDALDataType eDataType = GDT_Byte;
1740 257 : double dfNoData = 0.0;
1741 :
1742 257 : const char *itype = poDS->GetKeyword("IsisCube.Core.Pixels.Type");
1743 257 : if (EQUAL(itype, "UnsignedByte"))
1744 : {
1745 209 : eDataType = GDT_Byte;
1746 209 : dfNoData = ISIS3_NULL1;
1747 : }
1748 48 : else if (EQUAL(itype, "UnsignedWord"))
1749 : {
1750 14 : eDataType = GDT_UInt16;
1751 14 : dfNoData = ISIS3_NULLU2;
1752 : }
1753 34 : else if (EQUAL(itype, "SignedWord"))
1754 : {
1755 14 : eDataType = GDT_Int16;
1756 14 : dfNoData = ISIS3_NULL2;
1757 : }
1758 20 : else if (EQUAL(itype, "Real") || EQUAL(itype, ""))
1759 : {
1760 19 : eDataType = GDT_Float32;
1761 19 : dfNoData = ISIS3_NULL4;
1762 : }
1763 : else
1764 : {
1765 1 : CPLError(CE_Failure, CPLE_OpenFailed, "%s pixel type not supported.",
1766 : itype);
1767 1 : return nullptr;
1768 : }
1769 :
1770 : /*********** Grab samples lines band ************/
1771 :
1772 : // default to MSB
1773 : const bool bIsLSB =
1774 256 : EQUAL(poDS->GetKeyword("IsisCube.Core.Pixels.ByteOrder"), "Lsb");
1775 :
1776 : /*********** Grab Cellsize ************/
1777 256 : double dfXDim = 1.0;
1778 256 : double dfYDim = 1.0;
1779 :
1780 256 : const char *pszRes = poDS->GetKeyword("IsisCube.Mapping.PixelResolution");
1781 256 : if (strlen(pszRes) > 0)
1782 : {
1783 97 : dfXDim = CPLAtof(pszRes); /* values are in meters */
1784 97 : dfYDim = -CPLAtof(pszRes);
1785 : }
1786 :
1787 : /*********** Grab UpperLeftCornerY ************/
1788 256 : double dfULYMap = 0.5;
1789 :
1790 256 : const char *pszULY = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerY");
1791 256 : if (strlen(pszULY) > 0)
1792 : {
1793 97 : dfULYMap = CPLAtof(pszULY);
1794 : }
1795 :
1796 : /*********** Grab UpperLeftCornerX ************/
1797 256 : double dfULXMap = 0.5;
1798 :
1799 256 : const char *pszULX = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerX");
1800 256 : if (strlen(pszULX) > 0)
1801 : {
1802 97 : dfULXMap = CPLAtof(pszULX);
1803 : }
1804 :
1805 : /*********** Grab TARGET_NAME ************/
1806 : /**** This is the planets name i.e. Mars ***/
1807 256 : const char *target_name = poDS->GetKeyword("IsisCube.Mapping.TargetName");
1808 :
1809 : #ifdef notdef
1810 : const double dfLongitudeMulFactor =
1811 : EQUAL(poDS->GetKeyword("IsisCube.Mapping.LongitudeDirection",
1812 : "PositiveEast"),
1813 : "PositiveEast")
1814 : ? 1
1815 : : -1;
1816 : #else
1817 256 : const double dfLongitudeMulFactor = 1;
1818 : #endif
1819 :
1820 : /*********** Grab MAP_PROJECTION_TYPE ************/
1821 : const char *map_proj_name =
1822 256 : poDS->GetKeyword("IsisCube.Mapping.ProjectionName");
1823 :
1824 : /*********** Grab SEMI-MAJOR ************/
1825 : const double semi_major =
1826 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.EquatorialRadius"));
1827 :
1828 : /*********** Grab semi-minor ************/
1829 : const double semi_minor =
1830 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PolarRadius"));
1831 :
1832 : /*********** Grab CENTER_LAT ************/
1833 : const double center_lat =
1834 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.CenterLatitude"));
1835 :
1836 : /*********** Grab CENTER_LON ************/
1837 : const double center_lon =
1838 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.CenterLongitude")) *
1839 : dfLongitudeMulFactor;
1840 :
1841 : /*********** Grab 1st std parallel ************/
1842 : const double first_std_parallel =
1843 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.FirstStandardParallel"));
1844 :
1845 : /*********** Grab 2nd std parallel ************/
1846 : const double second_std_parallel =
1847 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.SecondStandardParallel"));
1848 :
1849 : /*********** Grab scaleFactor ************/
1850 : const double scaleFactor =
1851 256 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.scaleFactor", "1.0"));
1852 :
1853 : /*** grab LatitudeType = Planetographic ****/
1854 : // Need to further study how ocentric/ographic will effect the gdal library
1855 : // So far we will use this fact to define a sphere or ellipse for some
1856 : // projections
1857 :
1858 : // Frank - may need to talk this over
1859 256 : bool bIsGeographic = true;
1860 256 : if (EQUAL(poDS->GetKeyword("IsisCube.Mapping.LatitudeType"),
1861 : "Planetocentric"))
1862 87 : bIsGeographic = false;
1863 :
1864 : // Set oSRS projection and parameters
1865 : // ############################################################
1866 : // ISIS3 Projection types
1867 : // Equirectangular
1868 : // LambertConformal
1869 : // Mercator
1870 : // ObliqueCylindrical
1871 : // Orthographic
1872 : // PolarStereographic
1873 : // SimpleCylindrical
1874 : // Sinusoidal
1875 : // TransverseMercator
1876 :
1877 : #ifdef DEBUG
1878 256 : CPLDebug("ISIS3", "using projection %s", map_proj_name);
1879 : #endif
1880 :
1881 512 : OGRSpatialReference oSRS;
1882 256 : bool bProjectionSet = true;
1883 :
1884 256 : if ((EQUAL(map_proj_name, "Equirectangular")) ||
1885 189 : (EQUAL(map_proj_name, "SimpleCylindrical")))
1886 : {
1887 82 : oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
1888 : }
1889 174 : else if (EQUAL(map_proj_name, "Orthographic"))
1890 : {
1891 2 : oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
1892 : }
1893 172 : else if (EQUAL(map_proj_name, "Sinusoidal"))
1894 : {
1895 2 : oSRS.SetSinusoidal(center_lon, 0, 0);
1896 : }
1897 170 : else if (EQUAL(map_proj_name, "Mercator"))
1898 : {
1899 2 : oSRS.SetMercator(center_lat, center_lon, scaleFactor, 0, 0);
1900 : }
1901 168 : else if (EQUAL(map_proj_name, "PolarStereographic"))
1902 : {
1903 2 : oSRS.SetPS(center_lat, center_lon, scaleFactor, 0, 0);
1904 : }
1905 166 : else if (EQUAL(map_proj_name, "TransverseMercator"))
1906 : {
1907 16 : oSRS.SetTM(center_lat, center_lon, scaleFactor, 0, 0);
1908 : }
1909 150 : else if (EQUAL(map_proj_name, "LambertConformal"))
1910 : {
1911 2 : oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
1912 : center_lon, 0, 0);
1913 : }
1914 148 : else if (EQUAL(map_proj_name, "PointPerspective"))
1915 : {
1916 : // Distance parameter is the distance to the center of the body, and is
1917 : // given in km
1918 : const double distance =
1919 1 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.Distance")) * 1000.0;
1920 1 : const double height_above_ground = distance - semi_major;
1921 1 : oSRS.SetVerticalPerspective(center_lat, center_lon, 0,
1922 : height_above_ground, 0, 0);
1923 : }
1924 147 : else if (EQUAL(map_proj_name, "ObliqueCylindrical"))
1925 : {
1926 : const double poleLatitude =
1927 4 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleLatitude"));
1928 : const double poleLongitude =
1929 4 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleLongitude")) *
1930 : dfLongitudeMulFactor;
1931 : const double poleRotation =
1932 4 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleRotation"));
1933 8 : CPLString oProj4String;
1934 : // ISIS3 rotated pole doesn't use the same conventions than PROJ ob_tran
1935 : // Compare the sign difference in
1936 : // https://github.com/USGS-Astrogeology/ISIS3/blob/3.8.0/isis/src/base/objs/ObliqueCylindrical/ObliqueCylindrical.cpp#L244
1937 : // and
1938 : // https://github.com/OSGeo/PROJ/blob/6.2/src/projections/ob_tran.cpp#L34
1939 : // They can be compensated by modifying the poleLatitude to
1940 : // 180-poleLatitude There's also a sign difference for the poleRotation
1941 : // parameter The existence of those different conventions is
1942 : // acknowledged in
1943 : // https://pds-imaging.jpl.nasa.gov/documentation/Cassini_BIDRSIS.PDF in
1944 : // the middle of page 10
1945 : oProj4String.Printf("+proj=ob_tran +o_proj=eqc +o_lon_p=%.17g "
1946 : "+o_lat_p=%.17g +lon_0=%.17g",
1947 4 : -poleRotation, 180 - poleLatitude, poleLongitude);
1948 4 : oSRS.SetFromUserInput(oProj4String);
1949 : }
1950 : else
1951 : {
1952 143 : CPLDebug("ISIS3",
1953 : "Dataset projection %s is not supported. Continuing...",
1954 : map_proj_name);
1955 143 : bProjectionSet = false;
1956 : }
1957 :
1958 256 : if (bProjectionSet)
1959 : {
1960 : // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
1961 226 : CPLString osProjTargetName(map_proj_name);
1962 113 : osProjTargetName += " ";
1963 113 : osProjTargetName += target_name;
1964 113 : oSRS.SetProjCS(osProjTargetName); // set ProjCS keyword
1965 :
1966 : // The geographic/geocentric name will be the same basic name as the
1967 : // body name 'GCS' = Geographic/Geocentric Coordinate System
1968 226 : CPLString osGeogName("GCS_");
1969 113 : osGeogName += target_name;
1970 :
1971 : // The datum name will be the same basic name as the planet
1972 226 : CPLString osDatumName("D_");
1973 113 : osDatumName += target_name;
1974 :
1975 226 : CPLString osSphereName(target_name);
1976 : // strcat(osSphereName, "_IAU_IAG"); //Might not be IAU defined so
1977 : // don't add
1978 :
1979 : // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
1980 113 : double iflattening = 0.0;
1981 113 : if ((semi_major - semi_minor) < 0.0000001)
1982 38 : iflattening = 0;
1983 : else
1984 75 : iflattening = semi_major / (semi_major - semi_minor);
1985 :
1986 : // Set the body size but take into consideration which proj is being
1987 : // used to help w/ proj4 compatibility The use of a Sphere, polar radius
1988 : // or ellipse here is based on how ISIS does it internally
1989 113 : if (((EQUAL(map_proj_name, "Stereographic") &&
1990 0 : (fabs(center_lat) == 90))) ||
1991 113 : (EQUAL(map_proj_name, "PolarStereographic")))
1992 : {
1993 2 : if (bIsGeographic)
1994 : {
1995 : // Geograpraphic, so set an ellipse
1996 0 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
1997 : semi_major, iflattening, "Reference_Meridian",
1998 : 0.0);
1999 : }
2000 : else
2001 : {
2002 : // Geocentric, so force a sphere using the semi-minor axis. I
2003 : // hope...
2004 2 : osSphereName += "_polarRadius";
2005 2 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2006 : semi_minor, 0.0, "Reference_Meridian", 0.0);
2007 : }
2008 : }
2009 111 : else if ((EQUAL(map_proj_name, "SimpleCylindrical")) ||
2010 96 : (EQUAL(map_proj_name, "Orthographic")) ||
2011 94 : (EQUAL(map_proj_name, "Stereographic")) ||
2012 94 : (EQUAL(map_proj_name, "Sinusoidal")) ||
2013 92 : (EQUAL(map_proj_name, "PointPerspective")))
2014 : {
2015 : // ISIS uses the spherical equation for these projections
2016 : // so force a sphere.
2017 20 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, semi_major,
2018 : 0.0, "Reference_Meridian", 0.0);
2019 : }
2020 91 : else if (EQUAL(map_proj_name, "Equirectangular"))
2021 : {
2022 : // Calculate localRadius using ISIS3 simple elliptical method
2023 : // not the more standard Radius of Curvature method
2024 : // PI = 4 * atan(1);
2025 67 : const double radLat = center_lat * M_PI / 180; // in radians
2026 67 : const double meanRadius = sqrt(pow(semi_minor * cos(radLat), 2) +
2027 67 : pow(semi_major * sin(radLat), 2));
2028 67 : const double localRadius =
2029 67 : (meanRadius == 0.0) ? 0.0
2030 67 : : semi_major * semi_minor / meanRadius;
2031 67 : osSphereName += "_localRadius";
2032 67 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, localRadius,
2033 : 0.0, "Reference_Meridian", 0.0);
2034 : }
2035 : else
2036 : {
2037 : // All other projections: Mercator, Transverse Mercator, Lambert
2038 : // Conformal, etc. Geographic, so set an ellipse
2039 24 : if (bIsGeographic)
2040 : {
2041 0 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2042 : semi_major, iflattening, "Reference_Meridian",
2043 : 0.0);
2044 : }
2045 : else
2046 : {
2047 : // Geocentric, so force a sphere. I hope...
2048 24 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2049 : semi_major, 0.0, "Reference_Meridian", 0.0);
2050 : }
2051 : }
2052 :
2053 : // translate back into a projection string.
2054 113 : poDS->m_oSRS = std::move(oSRS);
2055 113 : poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2056 : }
2057 :
2058 : /* END ISIS3 Label Read */
2059 : /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
2060 :
2061 : /* -------------------------------------------------------------------- */
2062 : /* Did we get the required keywords? If not we return with */
2063 : /* this never having been considered to be a match. This isn't */
2064 : /* an error! */
2065 : /* -------------------------------------------------------------------- */
2066 511 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
2067 255 : !GDALCheckBandCount(nBands, false))
2068 : {
2069 2 : return nullptr;
2070 : }
2071 :
2072 : /* -------------------------------------------------------------------- */
2073 : /* Capture some information from the file that is of interest. */
2074 : /* -------------------------------------------------------------------- */
2075 254 : poDS->nRasterXSize = nCols;
2076 254 : poDS->nRasterYSize = nRows;
2077 :
2078 : /* -------------------------------------------------------------------- */
2079 : /* Open target binary file. */
2080 : /* -------------------------------------------------------------------- */
2081 254 : if (EQUAL(osFormat, "GeoTIFF"))
2082 : {
2083 27 : if (nSkipBytes != 0)
2084 : {
2085 2 : CPLError(CE_Warning, CPLE_NotSupported,
2086 : "Ignoring StartByte=%d for format=GeoTIFF",
2087 : 1 + nSkipBytes);
2088 : }
2089 27 : if (osQubeFile == poOpenInfo->pszFilename)
2090 : {
2091 0 : CPLError(CE_Failure, CPLE_AppDefined, "A ^Core file must be set");
2092 0 : return nullptr;
2093 : }
2094 27 : poDS->m_poExternalDS =
2095 27 : GDALDataset::FromHandle(GDALOpen(osQubeFile, poOpenInfo->eAccess));
2096 27 : if (poDS->m_poExternalDS == nullptr)
2097 : {
2098 2 : return nullptr;
2099 : }
2100 25 : if (poDS->m_poExternalDS->GetRasterXSize() != poDS->nRasterXSize ||
2101 24 : poDS->m_poExternalDS->GetRasterYSize() != poDS->nRasterYSize ||
2102 71 : poDS->m_poExternalDS->GetRasterCount() != nBands ||
2103 22 : poDS->m_poExternalDS->GetRasterBand(1)->GetRasterDataType() !=
2104 : eDataType)
2105 : {
2106 4 : CPLError(CE_Failure, CPLE_AppDefined,
2107 : "%s has incompatible characteristics with the ones "
2108 : "declared in the label.",
2109 : osQubeFile.c_str());
2110 4 : return nullptr;
2111 : }
2112 : }
2113 : else
2114 : {
2115 227 : if (poOpenInfo->eAccess == GA_ReadOnly)
2116 225 : poDS->m_fpImage = VSIFOpenL(osQubeFile, "r");
2117 : else
2118 2 : poDS->m_fpImage = VSIFOpenL(osQubeFile, "r+");
2119 :
2120 227 : if (poDS->m_fpImage == nullptr)
2121 : {
2122 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s: %s.",
2123 2 : osQubeFile.c_str(), VSIStrerror(errno));
2124 2 : return nullptr;
2125 : }
2126 :
2127 : // Sanity checks in case the external raw file appears to be a
2128 : // TIFF file
2129 225 : if (EQUAL(CPLGetExtensionSafe(osQubeFile).c_str(), "tif"))
2130 : {
2131 : GDALDataset *poTIF_DS =
2132 23 : GDALDataset::FromHandle(GDALOpen(osQubeFile, GA_ReadOnly));
2133 23 : if (poTIF_DS)
2134 : {
2135 23 : bool bWarned = false;
2136 23 : if (poTIF_DS->GetRasterXSize() != poDS->nRasterXSize ||
2137 22 : poTIF_DS->GetRasterYSize() != poDS->nRasterYSize ||
2138 21 : poTIF_DS->GetRasterCount() != nBands ||
2139 20 : poTIF_DS->GetRasterBand(1)->GetRasterDataType() !=
2140 45 : eDataType ||
2141 19 : poTIF_DS->GetMetadataItem("COMPRESSION",
2142 19 : "IMAGE_STRUCTURE") != nullptr)
2143 : {
2144 5 : bWarned = true;
2145 5 : CPLError(
2146 : CE_Warning, CPLE_AppDefined,
2147 : "%s has incompatible characteristics with the ones "
2148 : "declared in the label.",
2149 : osQubeFile.c_str());
2150 : }
2151 23 : int nBlockXSize = 1, nBlockYSize = 1;
2152 23 : poTIF_DS->GetRasterBand(1)->GetBlockSize(&nBlockXSize,
2153 : &nBlockYSize);
2154 23 : if ((poDS->m_bIsTiled &&
2155 46 : (nBlockXSize != tileSizeX || nBlockYSize != tileSizeY)) ||
2156 23 : (!poDS->m_bIsTiled && (nBlockXSize != nCols ||
2157 2 : (nBands > 1 && nBlockYSize != 1))))
2158 : {
2159 2 : if (!bWarned)
2160 : {
2161 1 : bWarned = true;
2162 1 : CPLError(
2163 : CE_Warning, CPLE_AppDefined,
2164 : "%s has incompatible characteristics with the ones "
2165 : "declared in the label.",
2166 : osQubeFile.c_str());
2167 : }
2168 : }
2169 : // to please Clang Static Analyzer
2170 23 : nBlockXSize = std::max(1, nBlockXSize);
2171 23 : nBlockYSize = std::max(1, nBlockYSize);
2172 :
2173 : // Check that blocks are effectively written in expected order.
2174 23 : const int nBlockSizeBytes = nBlockXSize * nBlockYSize *
2175 23 : GDALGetDataTypeSizeBytes(eDataType);
2176 23 : bool bGoOn = !bWarned;
2177 23 : const int l_nBlocksPerRow = DIV_ROUND_UP(nCols, nBlockXSize);
2178 23 : const int l_nBlocksPerColumn = DIV_ROUND_UP(nRows, nBlockYSize);
2179 23 : int nBlockNo = 0;
2180 52 : for (int i = 0; i < nBands && bGoOn; i++)
2181 : {
2182 1285 : for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
2183 : {
2184 2950 : for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
2185 : {
2186 : const char *pszBlockOffset =
2187 3388 : poTIF_DS->GetRasterBand(i + 1)->GetMetadataItem(
2188 : CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y),
2189 1694 : "TIFF");
2190 1694 : if (pszBlockOffset)
2191 : {
2192 1694 : GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
2193 1694 : if (nOffset !=
2194 1694 : nSkipBytes + nBlockNo * nBlockSizeBytes)
2195 : {
2196 : // bWarned = true;
2197 2 : CPLError(CE_Warning, CPLE_AppDefined,
2198 : "%s has incompatible "
2199 : "characteristics with the ones "
2200 : "declared in the label.",
2201 : osQubeFile.c_str());
2202 2 : bGoOn = false;
2203 : }
2204 : }
2205 1694 : nBlockNo++;
2206 : }
2207 : }
2208 : }
2209 :
2210 23 : delete poTIF_DS;
2211 : }
2212 : }
2213 : }
2214 :
2215 246 : poDS->eAccess = poOpenInfo->eAccess;
2216 :
2217 : /* -------------------------------------------------------------------- */
2218 : /* Compute the line offset. */
2219 : /* -------------------------------------------------------------------- */
2220 246 : int nLineOffset = 0;
2221 246 : int nPixelOffset = 0;
2222 246 : vsi_l_offset nBandOffset = 0;
2223 :
2224 246 : if (EQUAL(osFormat, "BandSequential"))
2225 : {
2226 200 : const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
2227 200 : nPixelOffset = nItemSize;
2228 : try
2229 : {
2230 200 : nLineOffset = (CPLSM(nPixelOffset) * CPLSM(nCols)).v();
2231 : }
2232 0 : catch (const CPLSafeIntOverflow &)
2233 : {
2234 0 : return nullptr;
2235 : }
2236 200 : nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
2237 :
2238 200 : poDS->m_sLayout.osRawFilename = std::move(osQubeFile);
2239 200 : if (nBands > 1)
2240 16 : poDS->m_sLayout.eInterleaving = RawBinaryLayout::Interleaving::BSQ;
2241 200 : poDS->m_sLayout.eDataType = eDataType;
2242 200 : poDS->m_sLayout.bLittleEndianOrder = bIsLSB;
2243 200 : poDS->m_sLayout.nImageOffset = nSkipBytes;
2244 200 : poDS->m_sLayout.nPixelOffset = nPixelOffset;
2245 200 : poDS->m_sLayout.nLineOffset = nLineOffset;
2246 200 : poDS->m_sLayout.nBandOffset = static_cast<GIntBig>(nBandOffset);
2247 : }
2248 : /* else Tiled or external */
2249 :
2250 : /* -------------------------------------------------------------------- */
2251 : /* Extract BandBin info. */
2252 : /* -------------------------------------------------------------------- */
2253 492 : std::vector<std::string> aosBandNames;
2254 492 : std::vector<std::string> aosBandUnits;
2255 492 : std::vector<double> adfWavelengths;
2256 492 : std::vector<std::string> aosWavelengthsUnit;
2257 492 : std::vector<double> adfBandwidth;
2258 492 : std::vector<std::string> aosBandwidthUnit;
2259 738 : const auto oBandBin = poDS->m_oJSonLabel.GetObj("IsisCube/BandBin");
2260 246 : if (oBandBin.IsValid() && oBandBin.GetType() == CPLJSONObject::Type::Object)
2261 : {
2262 184 : for (const auto &child : oBandBin.GetChildren())
2263 : {
2264 143 : if (CPLString(child.GetName()).ifind("name") != std::string::npos)
2265 : {
2266 : // Use "name" in priority
2267 12 : if (EQUAL(child.GetName().c_str(), "name"))
2268 : {
2269 5 : aosBandNames.clear();
2270 : }
2271 7 : else if (!aosBandNames.empty())
2272 : {
2273 0 : continue;
2274 : }
2275 :
2276 12 : if (child.GetType() == CPLJSONObject::Type::String &&
2277 : nBands == 1)
2278 : {
2279 4 : aosBandNames.push_back(child.ToString());
2280 : }
2281 8 : else if (child.GetType() == CPLJSONObject::Type::Array)
2282 : {
2283 16 : auto oArray = child.ToArray();
2284 8 : if (oArray.Size() == nBands)
2285 : {
2286 28 : for (int i = 0; i < nBands; i++)
2287 : {
2288 20 : if (oArray[i].GetType() ==
2289 : CPLJSONObject::Type::String)
2290 : {
2291 20 : aosBandNames.push_back(oArray[i].ToString());
2292 : }
2293 : else
2294 : {
2295 0 : aosBandNames.clear();
2296 0 : break;
2297 : }
2298 : }
2299 : }
2300 : }
2301 : }
2302 135 : else if (EQUAL(child.GetName().c_str(), "BandSuffixUnit") &&
2303 4 : child.GetType() == CPLJSONObject::Type::Array)
2304 : {
2305 8 : auto oArray = child.ToArray();
2306 4 : if (oArray.Size() == nBands)
2307 : {
2308 12 : for (int i = 0; i < nBands; i++)
2309 : {
2310 8 : if (oArray[i].GetType() == CPLJSONObject::Type::String)
2311 : {
2312 8 : aosBandUnits.push_back(oArray[i].ToString());
2313 : }
2314 : else
2315 : {
2316 0 : aosBandUnits.clear();
2317 0 : break;
2318 : }
2319 : }
2320 : }
2321 : }
2322 377 : else if (EQUAL(child.GetName().c_str(), "BandBinCenter") ||
2323 250 : EQUAL(child.GetName().c_str(), "Center"))
2324 : {
2325 41 : GetValueAndUnits(child, adfWavelengths, aosWavelengthsUnit,
2326 : nBands);
2327 : }
2328 90 : else if (EQUAL(child.GetName().c_str(), "BandBinUnit") &&
2329 4 : child.GetType() == CPLJSONObject::Type::String)
2330 : {
2331 12 : CPLString unit(child.ToString());
2332 4 : if (STARTS_WITH_CI(unit, "micromet") || EQUAL(unit, "um") ||
2333 4 : STARTS_WITH_CI(unit, "nanomet") || EQUAL(unit, "nm"))
2334 : {
2335 4 : aosWavelengthsUnit.push_back(child.ToString());
2336 : }
2337 : }
2338 82 : else if (EQUAL(child.GetName().c_str(), "Width"))
2339 : {
2340 7 : GetValueAndUnits(child, adfBandwidth, aosBandwidthUnit, nBands);
2341 : }
2342 : }
2343 :
2344 41 : if (!adfWavelengths.empty() && aosWavelengthsUnit.size() == 1)
2345 : {
2346 11 : for (int i = 1; i < nBands; i++)
2347 : {
2348 4 : aosWavelengthsUnit.push_back(aosWavelengthsUnit[0]);
2349 : }
2350 : }
2351 41 : if (!adfBandwidth.empty() && aosBandwidthUnit.size() == 1)
2352 : {
2353 7 : for (int i = 1; i < nBands; i++)
2354 : {
2355 2 : aosBandwidthUnit.push_back(aosBandwidthUnit[0]);
2356 : }
2357 : }
2358 : }
2359 :
2360 : /* -------------------------------------------------------------------- */
2361 : /* Create band information objects. */
2362 : /* -------------------------------------------------------------------- */
2363 : #ifdef CPL_LSB
2364 246 : const bool bNativeOrder = bIsLSB;
2365 : #else
2366 : const bool bNativeOrder = !bIsLSB;
2367 : #endif
2368 :
2369 542 : for (int i = 0; i < nBands; i++)
2370 : {
2371 : GDALRasterBand *poBand;
2372 :
2373 296 : if (poDS->m_poExternalDS != nullptr)
2374 : {
2375 : auto poISISBand = std::make_unique<ISIS3WrapperRasterBand>(
2376 21 : poDS->m_poExternalDS->GetRasterBand(i + 1));
2377 42 : poISISBand->SetMaskBand(
2378 42 : std::make_unique<ISISMaskBand>(poISISBand.get()));
2379 21 : poDS->SetBand(i + 1, std::move(poISISBand));
2380 21 : poBand = poDS->GetRasterBand(i + 1);
2381 : }
2382 275 : else if (poDS->m_bIsTiled)
2383 : {
2384 : auto poISISBand = std::make_unique<ISISTiledBand>(
2385 41 : poDS.get(), poDS->m_fpImage, i + 1, eDataType, tileSizeX,
2386 82 : tileSizeY, nSkipBytes, 0, 0, bNativeOrder);
2387 41 : if (!poISISBand->IsValid())
2388 : {
2389 0 : return nullptr;
2390 : }
2391 82 : poISISBand->SetMaskBand(
2392 82 : std::make_unique<ISISMaskBand>(poISISBand.get()));
2393 41 : poDS->SetBand(i + 1, std::move(poISISBand));
2394 41 : poBand = poDS->GetRasterBand(i + 1);
2395 : }
2396 : else
2397 : {
2398 : auto poISISBand = std::make_unique<ISIS3RawRasterBand>(
2399 234 : poDS.get(), i + 1, poDS->m_fpImage,
2400 234 : nSkipBytes + nBandOffset * i, nPixelOffset, nLineOffset,
2401 234 : eDataType, bNativeOrder);
2402 234 : if (!poISISBand->IsValid())
2403 : {
2404 0 : return nullptr;
2405 : }
2406 468 : poISISBand->SetMaskBand(
2407 468 : std::make_unique<ISISMaskBand>(poISISBand.get()));
2408 234 : poDS->SetBand(i + 1, std::move(poISISBand));
2409 234 : poBand = poDS->GetRasterBand(i + 1);
2410 : }
2411 :
2412 296 : if (i < static_cast<int>(aosBandNames.size()))
2413 : {
2414 17 : poBand->SetDescription(aosBandNames[i].c_str());
2415 : }
2416 345 : if (i < static_cast<int>(adfWavelengths.size()) &&
2417 49 : i < static_cast<int>(aosWavelengthsUnit.size()))
2418 : {
2419 11 : poBand->SetMetadataItem("WAVELENGTH",
2420 11 : CPLSPrintf("%f", adfWavelengths[i]));
2421 11 : poBand->SetMetadataItem("WAVELENGTH_UNIT",
2422 11 : aosWavelengthsUnit[i].c_str());
2423 18 : if (i < static_cast<int>(adfBandwidth.size()) &&
2424 7 : i < static_cast<int>(aosBandwidthUnit.size()))
2425 : {
2426 7 : poBand->SetMetadataItem("BANDWIDTH",
2427 7 : CPLSPrintf("%f", adfBandwidth[i]));
2428 7 : poBand->SetMetadataItem("BANDWIDTH_UNIT",
2429 7 : aosBandwidthUnit[i].c_str());
2430 : }
2431 : }
2432 296 : if (i < static_cast<int>(aosBandUnits.size()))
2433 : {
2434 8 : poBand->SetUnitType(aosBandUnits[i].c_str());
2435 : }
2436 :
2437 296 : poBand->SetNoDataValue(dfNoData);
2438 :
2439 : // Set offset/scale values.
2440 : const double dfOffset =
2441 296 : CPLAtofM(poDS->GetKeyword("IsisCube.Core.Pixels.Base", "0.0"));
2442 296 : const double dfScale = CPLAtofM(
2443 : poDS->GetKeyword("IsisCube.Core.Pixels.Multiplier", "1.0"));
2444 296 : if (dfOffset != 0.0 || dfScale != 1.0)
2445 : {
2446 52 : poBand->SetOffset(dfOffset);
2447 52 : poBand->SetScale(dfScale);
2448 : }
2449 : }
2450 :
2451 : /* -------------------------------------------------------------------- */
2452 : /* Check for a .prj file. For ISIS3 I would like to keep this in */
2453 : /* -------------------------------------------------------------------- */
2454 492 : const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
2455 492 : const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
2456 492 : const std::string osPrjFile = CPLFormCIFilenameSafe(osPath, osName, "prj");
2457 :
2458 246 : VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
2459 246 : if (fp != nullptr)
2460 : {
2461 0 : VSIFCloseL(fp);
2462 :
2463 0 : char **papszLines = CSLLoad(osPrjFile.c_str());
2464 :
2465 0 : OGRSpatialReference oSRS2;
2466 0 : if (oSRS2.importFromESRI(papszLines) == OGRERR_NONE)
2467 : {
2468 0 : poDS->m_aosAdditionalFiles.AddString(osPrjFile.c_str());
2469 0 : poDS->m_oSRS = std::move(oSRS2);
2470 0 : poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2471 : }
2472 :
2473 0 : CSLDestroy(papszLines);
2474 : }
2475 :
2476 246 : if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
2477 : {
2478 94 : poDS->m_bGotTransform = true;
2479 94 : poDS->m_adfGeoTransform[0] = dfULXMap;
2480 94 : poDS->m_adfGeoTransform[1] = dfXDim;
2481 94 : poDS->m_adfGeoTransform[2] = 0.0;
2482 94 : poDS->m_adfGeoTransform[3] = dfULYMap;
2483 94 : poDS->m_adfGeoTransform[4] = 0.0;
2484 94 : poDS->m_adfGeoTransform[5] = dfYDim;
2485 : }
2486 :
2487 246 : if (!poDS->m_bGotTransform)
2488 : {
2489 152 : poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
2490 152 : poOpenInfo->pszFilename, "cbw", poDS->m_adfGeoTransform));
2491 152 : if (poDS->m_bGotTransform)
2492 : {
2493 0 : poDS->m_aosAdditionalFiles.AddString(
2494 0 : CPLResetExtensionSafe(poOpenInfo->pszFilename, "cbw").c_str());
2495 : }
2496 : }
2497 :
2498 246 : if (!poDS->m_bGotTransform)
2499 : {
2500 152 : poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
2501 152 : poOpenInfo->pszFilename, "wld", poDS->m_adfGeoTransform));
2502 152 : if (poDS->m_bGotTransform)
2503 : {
2504 0 : poDS->m_aosAdditionalFiles.AddString(
2505 0 : CPLResetExtensionSafe(poOpenInfo->pszFilename, "wld").c_str());
2506 : }
2507 : }
2508 :
2509 : /* -------------------------------------------------------------------- */
2510 : /* Initialize any PAM information. */
2511 : /* -------------------------------------------------------------------- */
2512 246 : poDS->SetDescription(poOpenInfo->pszFilename);
2513 246 : poDS->TryLoadXML();
2514 :
2515 : /* -------------------------------------------------------------------- */
2516 : /* Check for overviews. */
2517 : /* -------------------------------------------------------------------- */
2518 246 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
2519 :
2520 246 : return poDS.release();
2521 : }
2522 :
2523 : /************************************************************************/
2524 : /* GetKeyword() */
2525 : /************************************************************************/
2526 :
2527 6046 : const char *ISIS3Dataset::GetKeyword(const char *pszPath,
2528 : const char *pszDefault)
2529 :
2530 : {
2531 6046 : return m_oKeywords.GetKeyword(pszPath, pszDefault);
2532 : }
2533 :
2534 : /************************************************************************/
2535 : /* FixLong() */
2536 : /************************************************************************/
2537 :
2538 232 : double ISIS3Dataset::FixLong(double dfLong)
2539 : {
2540 232 : if (m_osLongitudeDirection == "PositiveWest")
2541 4 : dfLong = -dfLong;
2542 232 : if (m_bForce360 && dfLong < 0)
2543 2 : dfLong += 360.0;
2544 232 : return dfLong;
2545 : }
2546 :
2547 : /************************************************************************/
2548 : /* BuildLabel() */
2549 : /************************************************************************/
2550 :
2551 126 : void ISIS3Dataset::BuildLabel()
2552 : {
2553 252 : CPLJSONObject oLabel = m_oSrcJSonLabel;
2554 126 : if (!oLabel.IsValid())
2555 : {
2556 102 : oLabel = CPLJSONObject();
2557 : }
2558 : // If we have a source label, then edit it directly
2559 378 : CPLJSONObject oIsisCube = GetOrCreateJSONObject(oLabel, "IsisCube");
2560 126 : oIsisCube.Set("_type", "object");
2561 :
2562 126 : if (!m_osComment.empty())
2563 1 : oIsisCube.Set("_comment", m_osComment);
2564 :
2565 378 : CPLJSONObject oCore = GetOrCreateJSONObject(oIsisCube, "Core");
2566 126 : if (oCore.GetType() != CPLJSONObject::Type::Object)
2567 : {
2568 0 : oIsisCube.Delete("Core");
2569 0 : oCore = CPLJSONObject();
2570 0 : oIsisCube.Add("Core", oCore);
2571 : }
2572 126 : oCore.Set("_type", "object");
2573 :
2574 126 : if (!m_osExternalFilename.empty())
2575 : {
2576 31 : if (m_poExternalDS && m_bGeoTIFFAsRegularExternal)
2577 : {
2578 8 : if (!m_bGeoTIFFInitDone)
2579 : {
2580 1 : reinterpret_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
2581 1 : ->InitFile();
2582 : }
2583 :
2584 : const char *pszOffset =
2585 8 : m_poExternalDS->GetRasterBand(1)->GetMetadataItem(
2586 8 : "BLOCK_OFFSET_0_0", "TIFF");
2587 8 : if (pszOffset)
2588 : {
2589 8 : oCore.Set("StartByte", 1 + atoi(pszOffset));
2590 : }
2591 : else
2592 : {
2593 : // Shouldn't happen normally
2594 0 : CPLError(CE_Warning, CPLE_AppDefined,
2595 : "Missing BLOCK_OFFSET_0_0");
2596 0 : m_bGeoTIFFAsRegularExternal = false;
2597 0 : oCore.Set("StartByte", 1);
2598 8 : }
2599 : }
2600 : else
2601 : {
2602 23 : oCore.Set("StartByte", 1);
2603 : }
2604 31 : if (!m_osExternalFilename.empty())
2605 : {
2606 : const CPLString osExternalFilename =
2607 31 : CPLGetFilename(m_osExternalFilename);
2608 31 : oCore.Set("^Core", osExternalFilename);
2609 : }
2610 : }
2611 : else
2612 : {
2613 95 : oCore.Set("StartByte", pszSTARTBYTE_PLACEHOLDER);
2614 95 : oCore.Delete("^Core");
2615 : }
2616 :
2617 126 : if (m_poExternalDS && !m_bGeoTIFFAsRegularExternal)
2618 : {
2619 10 : oCore.Set("Format", "GeoTIFF");
2620 10 : oCore.Delete("TileSamples");
2621 10 : oCore.Delete("TileLines");
2622 : }
2623 116 : else if (m_bIsTiled)
2624 : {
2625 9 : oCore.Set("Format", "Tile");
2626 9 : int nBlockXSize = 1, nBlockYSize = 1;
2627 9 : GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
2628 9 : oCore.Set("TileSamples", nBlockXSize);
2629 9 : oCore.Set("TileLines", nBlockYSize);
2630 : }
2631 : else
2632 : {
2633 107 : oCore.Set("Format", "BandSequential");
2634 107 : oCore.Delete("TileSamples");
2635 107 : oCore.Delete("TileLines");
2636 : }
2637 :
2638 378 : CPLJSONObject oDimensions = GetOrCreateJSONObject(oCore, "Dimensions");
2639 126 : oDimensions.Set("_type", "group");
2640 126 : oDimensions.Set("Samples", nRasterXSize);
2641 126 : oDimensions.Set("Lines", nRasterYSize);
2642 126 : oDimensions.Set("Bands", nBands);
2643 :
2644 378 : CPLJSONObject oPixels = GetOrCreateJSONObject(oCore, "Pixels");
2645 126 : oPixels.Set("_type", "group");
2646 126 : const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
2647 152 : oPixels.Set("Type", (eDT == GDT_Byte) ? "UnsignedByte"
2648 43 : : (eDT == GDT_UInt16) ? "UnsignedWord"
2649 17 : : (eDT == GDT_Int16) ? "SignedWord"
2650 : : "Real");
2651 :
2652 126 : oPixels.Set("ByteOrder", "Lsb");
2653 126 : oPixels.Set("Base", GetRasterBand(1)->GetOffset());
2654 126 : oPixels.Set("Multiplier", GetRasterBand(1)->GetScale());
2655 :
2656 126 : const OGRSpatialReference &oSRS = m_oSRS;
2657 :
2658 126 : if (!m_bUseSrcMapping)
2659 : {
2660 124 : oIsisCube.Delete("Mapping");
2661 : }
2662 :
2663 378 : CPLJSONObject oMapping = GetOrCreateJSONObject(oIsisCube, "Mapping");
2664 128 : if (m_bUseSrcMapping && oMapping.IsValid() &&
2665 2 : oMapping.GetType() == CPLJSONObject::Type::Object)
2666 : {
2667 2 : if (!m_osTargetName.empty())
2668 1 : oMapping.Set("TargetName", m_osTargetName);
2669 2 : if (!m_osLatitudeType.empty())
2670 1 : oMapping.Set("LatitudeType", m_osLatitudeType);
2671 2 : if (!m_osLongitudeDirection.empty())
2672 1 : oMapping.Set("LongitudeDirection", m_osLongitudeDirection);
2673 : }
2674 124 : else if (!m_bUseSrcMapping && !m_oSRS.IsEmpty())
2675 : {
2676 59 : oMapping.Add("_type", "group");
2677 :
2678 59 : if (oSRS.IsProjected() || oSRS.IsGeographic())
2679 : {
2680 59 : const char *pszDatum = oSRS.GetAttrValue("DATUM");
2681 118 : CPLString osTargetName(m_osTargetName);
2682 59 : if (osTargetName.empty())
2683 : {
2684 58 : if (pszDatum && STARTS_WITH(pszDatum, "D_"))
2685 : {
2686 24 : osTargetName = pszDatum + 2;
2687 : }
2688 34 : else if (pszDatum)
2689 : {
2690 34 : osTargetName = pszDatum;
2691 : }
2692 : }
2693 59 : if (!osTargetName.empty())
2694 59 : oMapping.Add("TargetName", osTargetName);
2695 :
2696 59 : oMapping.Add("EquatorialRadius/value", oSRS.GetSemiMajor());
2697 59 : oMapping.Add("EquatorialRadius/unit", "meters");
2698 59 : oMapping.Add("PolarRadius/value", oSRS.GetSemiMinor());
2699 59 : oMapping.Add("PolarRadius/unit", "meters");
2700 :
2701 59 : if (!m_osLatitudeType.empty())
2702 1 : oMapping.Add("LatitudeType", m_osLatitudeType);
2703 : else
2704 58 : oMapping.Add("LatitudeType", "Planetocentric");
2705 :
2706 59 : if (!m_osLongitudeDirection.empty())
2707 1 : oMapping.Add("LongitudeDirection", m_osLongitudeDirection);
2708 : else
2709 58 : oMapping.Add("LongitudeDirection", "PositiveEast");
2710 :
2711 59 : double adfX[4] = {0};
2712 59 : double adfY[4] = {0};
2713 59 : bool bLongLatCorners = false;
2714 59 : if (m_bGotTransform)
2715 : {
2716 255 : for (int i = 0; i < 4; i++)
2717 : {
2718 204 : adfX[i] = m_adfGeoTransform[0] +
2719 204 : (i % 2) * nRasterXSize * m_adfGeoTransform[1];
2720 408 : adfY[i] = m_adfGeoTransform[3] +
2721 204 : ((i == 0 || i == 3) ? 0 : 1) * nRasterYSize *
2722 204 : m_adfGeoTransform[5];
2723 : }
2724 51 : if (oSRS.IsGeographic())
2725 : {
2726 31 : bLongLatCorners = true;
2727 : }
2728 : else
2729 : {
2730 20 : OGRSpatialReference *poSRSLongLat = oSRS.CloneGeogCS();
2731 20 : if (poSRSLongLat)
2732 : {
2733 20 : poSRSLongLat->SetAxisMappingStrategy(
2734 : OAMS_TRADITIONAL_GIS_ORDER);
2735 : OGRCoordinateTransformation *poCT =
2736 20 : OGRCreateCoordinateTransformation(&oSRS,
2737 : poSRSLongLat);
2738 20 : if (poCT)
2739 : {
2740 20 : if (poCT->Transform(4, adfX, adfY))
2741 : {
2742 20 : bLongLatCorners = true;
2743 : }
2744 20 : delete poCT;
2745 : }
2746 20 : delete poSRSLongLat;
2747 : }
2748 : }
2749 : }
2750 59 : if (bLongLatCorners)
2751 : {
2752 255 : for (int i = 0; i < 4; i++)
2753 : {
2754 204 : adfX[i] = FixLong(adfX[i]);
2755 : }
2756 : }
2757 :
2758 59 : if (bLongLatCorners &&
2759 51 : (m_bForce360 || adfX[0] < -180.0 || adfX[3] > 180.0))
2760 : {
2761 1 : oMapping.Add("LongitudeDomain", 360);
2762 : }
2763 : else
2764 : {
2765 58 : oMapping.Add("LongitudeDomain", 180);
2766 : }
2767 :
2768 59 : if (m_bWriteBoundingDegrees && !m_osBoundingDegrees.empty())
2769 : {
2770 : char **papszTokens =
2771 1 : CSLTokenizeString2(m_osBoundingDegrees, ",", 0);
2772 1 : if (CSLCount(papszTokens) == 4)
2773 : {
2774 1 : oMapping.Add("MinimumLatitude", CPLAtof(papszTokens[1]));
2775 1 : oMapping.Add("MinimumLongitude", CPLAtof(papszTokens[0]));
2776 1 : oMapping.Add("MaximumLatitude", CPLAtof(papszTokens[3]));
2777 1 : oMapping.Add("MaximumLongitude", CPLAtof(papszTokens[2]));
2778 : }
2779 1 : CSLDestroy(papszTokens);
2780 : }
2781 58 : else if (m_bWriteBoundingDegrees && bLongLatCorners)
2782 : {
2783 49 : oMapping.Add("MinimumLatitude",
2784 : std::min(std::min(adfY[0], adfY[1]),
2785 49 : std::min(adfY[2], adfY[3])));
2786 49 : oMapping.Add("MinimumLongitude",
2787 : std::min(std::min(adfX[0], adfX[1]),
2788 49 : std::min(adfX[2], adfX[3])));
2789 49 : oMapping.Add("MaximumLatitude",
2790 : std::max(std::max(adfY[0], adfY[1]),
2791 49 : std::max(adfY[2], adfY[3])));
2792 49 : oMapping.Add("MaximumLongitude",
2793 : std::max(std::max(adfX[0], adfX[1]),
2794 49 : std::max(adfX[2], adfX[3])));
2795 : }
2796 :
2797 59 : const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
2798 59 : if (pszProjection == nullptr)
2799 : {
2800 31 : oMapping.Add("ProjectionName", "SimpleCylindrical");
2801 31 : oMapping.Add("CenterLongitude", 0.0);
2802 31 : oMapping.Add("CenterLatitude", 0.0);
2803 31 : oMapping.Add("CenterLatitudeRadius", oSRS.GetSemiMajor());
2804 : }
2805 28 : else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
2806 : {
2807 14 : oMapping.Add("ProjectionName", "Equirectangular");
2808 14 : if (oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
2809 : {
2810 1 : CPLError(CE_Warning, CPLE_NotSupported,
2811 : "Ignoring %s. Only 0 value supported",
2812 : SRS_PP_LATITUDE_OF_ORIGIN);
2813 : }
2814 14 : oMapping.Add("CenterLongitude",
2815 : FixLong(oSRS.GetNormProjParm(
2816 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2817 : const double dfCenterLat =
2818 14 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
2819 14 : oMapping.Add("CenterLatitude", dfCenterLat);
2820 :
2821 : // in radians
2822 14 : const double radLat = dfCenterLat * M_PI / 180;
2823 14 : const double semi_major = oSRS.GetSemiMajor();
2824 14 : const double semi_minor = oSRS.GetSemiMinor();
2825 : const double localRadius =
2826 14 : semi_major * semi_minor /
2827 14 : sqrt(pow(semi_minor * cos(radLat), 2) +
2828 14 : pow(semi_major * sin(radLat), 2));
2829 14 : oMapping.Add("CenterLatitudeRadius", localRadius);
2830 : }
2831 :
2832 14 : else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
2833 : {
2834 1 : oMapping.Add("ProjectionName", "Orthographic");
2835 1 : oMapping.Add("CenterLongitude",
2836 : FixLong(oSRS.GetNormProjParm(
2837 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2838 1 : oMapping.Add(
2839 : "CenterLatitude",
2840 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2841 : }
2842 :
2843 13 : else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
2844 : {
2845 1 : oMapping.Add("ProjectionName", "Sinusoidal");
2846 1 : oMapping.Add("CenterLongitude",
2847 : FixLong(oSRS.GetNormProjParm(
2848 : SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
2849 : }
2850 :
2851 12 : else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
2852 : {
2853 1 : oMapping.Add("ProjectionName", "Mercator");
2854 1 : oMapping.Add("CenterLongitude",
2855 : FixLong(oSRS.GetNormProjParm(
2856 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2857 1 : oMapping.Add(
2858 : "CenterLatitude",
2859 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2860 1 : oMapping.Add("scaleFactor",
2861 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
2862 : }
2863 :
2864 11 : else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
2865 : {
2866 1 : oMapping.Add("ProjectionName", "PolarStereographic");
2867 1 : oMapping.Add("CenterLongitude",
2868 : FixLong(oSRS.GetNormProjParm(
2869 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2870 1 : oMapping.Add(
2871 : "CenterLatitude",
2872 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2873 1 : oMapping.Add("scaleFactor",
2874 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
2875 : }
2876 :
2877 10 : else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
2878 : {
2879 8 : oMapping.Add("ProjectionName", "TransverseMercator");
2880 8 : oMapping.Add("CenterLongitude",
2881 : FixLong(oSRS.GetNormProjParm(
2882 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2883 8 : oMapping.Add(
2884 : "CenterLatitude",
2885 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2886 8 : oMapping.Add("scaleFactor",
2887 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
2888 : }
2889 :
2890 2 : else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
2891 : {
2892 1 : oMapping.Add("ProjectionName", "LambertConformal");
2893 1 : oMapping.Add("CenterLongitude",
2894 : FixLong(oSRS.GetNormProjParm(
2895 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2896 1 : oMapping.Add(
2897 : "CenterLatitude",
2898 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2899 1 : oMapping.Add(
2900 : "FirstStandardParallel",
2901 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0));
2902 1 : oMapping.Add(
2903 : "SecondStandardParallel",
2904 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0));
2905 : }
2906 :
2907 1 : else if (EQUAL(pszProjection,
2908 : "Vertical Perspective")) // PROJ 7 required
2909 : {
2910 0 : oMapping.Add("ProjectionName", "PointPerspective");
2911 0 : oMapping.Add("CenterLongitude",
2912 : FixLong(oSRS.GetNormProjParm(
2913 : "Longitude of topocentric origin", 0.0)));
2914 0 : oMapping.Add("CenterLatitude",
2915 : oSRS.GetNormProjParm(
2916 : "Latitude of topocentric origin", 0.0));
2917 : // ISIS3 value is the distance from center of ellipsoid, in km
2918 0 : oMapping.Add("Distance",
2919 0 : (oSRS.GetNormProjParm("Viewpoint height", 0.0) +
2920 0 : oSRS.GetSemiMajor()) /
2921 : 1000.0);
2922 : }
2923 :
2924 1 : else if (EQUAL(pszProjection, "custom_proj4"))
2925 : {
2926 : const char *pszProj4 =
2927 1 : oSRS.GetExtension("PROJCS", "PROJ4", nullptr);
2928 1 : if (pszProj4 && strstr(pszProj4, "+proj=ob_tran") &&
2929 1 : strstr(pszProj4, "+o_proj=eqc"))
2930 : {
2931 : const auto FetchParam =
2932 3 : [](const char *pszProj4Str, const char *pszKey)
2933 : {
2934 6 : CPLString needle;
2935 3 : needle.Printf("+%s=", pszKey);
2936 : const char *pszVal =
2937 3 : strstr(pszProj4Str, needle.c_str());
2938 3 : if (pszVal)
2939 3 : return CPLAtof(pszVal + needle.size());
2940 0 : return 0.0;
2941 : };
2942 :
2943 1 : double dfLonP = FetchParam(pszProj4, "o_lon_p");
2944 1 : double dfLatP = FetchParam(pszProj4, "o_lat_p");
2945 1 : double dfLon0 = FetchParam(pszProj4, "lon_0");
2946 1 : double dfPoleRotation = -dfLonP;
2947 1 : double dfPoleLatitude = 180 - dfLatP;
2948 1 : double dfPoleLongitude = dfLon0;
2949 1 : oMapping.Add("ProjectionName", "ObliqueCylindrical");
2950 1 : oMapping.Add("PoleLatitude", dfPoleLatitude);
2951 1 : oMapping.Add("PoleLongitude", FixLong(dfPoleLongitude));
2952 1 : oMapping.Add("PoleRotation", dfPoleRotation);
2953 : }
2954 : else
2955 : {
2956 0 : CPLError(CE_Warning, CPLE_NotSupported,
2957 : "Projection %s not supported", pszProjection);
2958 : }
2959 : }
2960 : else
2961 : {
2962 0 : CPLError(CE_Warning, CPLE_NotSupported,
2963 : "Projection %s not supported", pszProjection);
2964 : }
2965 :
2966 59 : if (oMapping["ProjectionName"].IsValid())
2967 : {
2968 59 : if (oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
2969 : {
2970 6 : CPLError(CE_Warning, CPLE_NotSupported,
2971 : "Ignoring %s. Only 0 value supported",
2972 : SRS_PP_FALSE_EASTING);
2973 : }
2974 59 : if (oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
2975 : {
2976 1 : CPLError(CE_Warning, CPLE_AppDefined,
2977 : "Ignoring %s. Only 0 value supported",
2978 : SRS_PP_FALSE_NORTHING);
2979 : }
2980 : }
2981 : }
2982 : else
2983 : {
2984 0 : CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
2985 : }
2986 : }
2987 :
2988 126 : if (!m_bUseSrcMapping && m_bGotTransform)
2989 : {
2990 51 : oMapping.Add("_type", "group");
2991 :
2992 51 : const double dfDegToMeter = oSRS.GetSemiMajor() * M_PI / 180.0;
2993 51 : if (!m_oSRS.IsEmpty() && oSRS.IsProjected())
2994 : {
2995 20 : const double dfLinearUnits = oSRS.GetLinearUnits();
2996 : // Maybe we should deal differently with non meter units ?
2997 20 : const double dfRes = m_adfGeoTransform[1] * dfLinearUnits;
2998 20 : const double dfScale = dfDegToMeter / dfRes;
2999 20 : oMapping.Add("UpperLeftCornerX", m_adfGeoTransform[0]);
3000 20 : oMapping.Add("UpperLeftCornerY", m_adfGeoTransform[3]);
3001 20 : oMapping.Add("PixelResolution/value", dfRes);
3002 20 : oMapping.Add("PixelResolution/unit", "meters/pixel");
3003 20 : oMapping.Add("Scale/value", dfScale);
3004 20 : oMapping.Add("Scale/unit", "pixels/degree");
3005 : }
3006 31 : else if (!m_oSRS.IsEmpty() && oSRS.IsGeographic())
3007 : {
3008 31 : const double dfScale = 1.0 / m_adfGeoTransform[1];
3009 31 : const double dfRes = m_adfGeoTransform[1] * dfDegToMeter;
3010 31 : oMapping.Add("UpperLeftCornerX",
3011 31 : m_adfGeoTransform[0] * dfDegToMeter);
3012 31 : oMapping.Add("UpperLeftCornerY",
3013 31 : m_adfGeoTransform[3] * dfDegToMeter);
3014 31 : oMapping.Add("PixelResolution/value", dfRes);
3015 31 : oMapping.Add("PixelResolution/unit", "meters/pixel");
3016 31 : oMapping.Add("Scale/value", dfScale);
3017 31 : oMapping.Add("Scale/unit", "pixels/degree");
3018 : }
3019 : else
3020 : {
3021 0 : oMapping.Add("UpperLeftCornerX", m_adfGeoTransform[0]);
3022 0 : oMapping.Add("UpperLeftCornerY", m_adfGeoTransform[3]);
3023 0 : oMapping.Add("PixelResolution", m_adfGeoTransform[1]);
3024 : }
3025 : }
3026 :
3027 378 : CPLJSONObject oLabelLabel = GetOrCreateJSONObject(oLabel, "Label");
3028 126 : oLabelLabel.Set("_type", "object");
3029 126 : oLabelLabel.Set("Bytes", pszLABEL_BYTES_PLACEHOLDER);
3030 :
3031 : // Deal with History object
3032 126 : BuildHistory();
3033 :
3034 126 : oLabel.Delete("History");
3035 126 : if (!m_osHistory.empty())
3036 : {
3037 124 : CPLJSONObject oHistory;
3038 124 : oHistory.Add("_type", "object");
3039 124 : oHistory.Add("Name", "IsisCube");
3040 124 : if (m_osExternalFilename.empty())
3041 94 : oHistory.Add("StartByte", pszHISTORY_STARTBYTE_PLACEHOLDER);
3042 : else
3043 30 : oHistory.Add("StartByte", 1);
3044 124 : oHistory.Add("Bytes", static_cast<GIntBig>(m_osHistory.size()));
3045 124 : if (!m_osExternalFilename.empty())
3046 : {
3047 30 : CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
3048 30 : osFilename += ".History.IsisCube";
3049 30 : oHistory.Add("^History", osFilename);
3050 : }
3051 124 : oLabel.Add("History", oHistory);
3052 : }
3053 :
3054 : // Deal with other objects that have StartByte & Bytes
3055 126 : m_aoNonPixelSections.clear();
3056 126 : if (m_oSrcJSonLabel.IsValid())
3057 : {
3058 48 : CPLString osLabelSrcFilename;
3059 72 : CPLJSONObject oFilename = oLabel["_filename"];
3060 24 : if (oFilename.GetType() == CPLJSONObject::Type::String)
3061 : {
3062 19 : osLabelSrcFilename = oFilename.ToString();
3063 : }
3064 :
3065 128 : for (CPLJSONObject &oObj : oLabel.GetChildren())
3066 : {
3067 104 : CPLString osKey = oObj.GetName();
3068 104 : if (osKey == "History")
3069 : {
3070 22 : continue;
3071 : }
3072 :
3073 164 : CPLJSONObject oBytes = oObj.GetObj("Bytes");
3074 92 : if (oBytes.GetType() != CPLJSONObject::Type::Integer ||
3075 10 : oBytes.ToInteger() <= 0)
3076 : {
3077 72 : continue;
3078 : }
3079 :
3080 20 : CPLJSONObject oStartByte = oObj.GetObj("StartByte");
3081 20 : if (oStartByte.GetType() != CPLJSONObject::Type::Integer ||
3082 10 : oStartByte.ToInteger() <= 0)
3083 : {
3084 0 : continue;
3085 : }
3086 :
3087 10 : if (osLabelSrcFilename.empty())
3088 : {
3089 0 : CPLError(CE_Warning, CPLE_AppDefined,
3090 : "Cannot find _filename attribute in "
3091 : "source ISIS3 metadata. Removing object "
3092 : "%s from the label.",
3093 : osKey.c_str());
3094 0 : oLabel.Delete(osKey);
3095 0 : continue;
3096 : }
3097 :
3098 10 : NonPixelSection oSection;
3099 10 : oSection.osSrcFilename = osLabelSrcFilename;
3100 10 : oSection.nSrcOffset =
3101 10 : static_cast<vsi_l_offset>(oObj.GetInteger("StartByte")) - 1U;
3102 10 : oSection.nSize =
3103 10 : static_cast<vsi_l_offset>(oObj.GetInteger("Bytes"));
3104 :
3105 10 : CPLString osName;
3106 20 : CPLJSONObject oName = oObj.GetObj("Name");
3107 10 : if (oName.GetType() == CPLJSONObject::Type::String)
3108 : {
3109 10 : osName = oName.ToString();
3110 : }
3111 :
3112 10 : CPLString osContainerName(osKey);
3113 20 : CPLJSONObject oContainerName = oObj.GetObj("_container_name");
3114 10 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
3115 : {
3116 5 : osContainerName = oContainerName.ToString();
3117 : }
3118 :
3119 10 : const CPLString osKeyFilename("^" + osContainerName);
3120 10 : CPLJSONObject oFilenameCap = oObj.GetObj(osKeyFilename);
3121 10 : if (oFilenameCap.GetType() == CPLJSONObject::Type::String)
3122 : {
3123 : VSIStatBufL sStat;
3124 24 : CPLString osSrcFilename(CPLFormFilenameSafe(
3125 16 : CPLGetPathSafe(osLabelSrcFilename).c_str(),
3126 24 : oFilenameCap.ToString().c_str(), nullptr));
3127 8 : if (VSIStatL(osSrcFilename, &sStat) == 0)
3128 : {
3129 3 : oSection.osSrcFilename = std::move(osSrcFilename);
3130 : }
3131 : else
3132 : {
3133 5 : CPLError(CE_Warning, CPLE_AppDefined,
3134 : "Object %s points to %s, which does "
3135 : "not exist. Removing this section "
3136 : "from the label",
3137 : osKey.c_str(), osSrcFilename.c_str());
3138 5 : oLabel.Delete(osKey);
3139 5 : continue;
3140 : }
3141 : }
3142 :
3143 5 : if (!m_osExternalFilename.empty())
3144 : {
3145 2 : oObj.Set("StartByte", 1);
3146 : }
3147 : else
3148 : {
3149 6 : CPLString osPlaceHolder;
3150 : osPlaceHolder.Printf(
3151 : "!*^PLACEHOLDER_%d_STARTBYTE^*!",
3152 3 : static_cast<int>(m_aoNonPixelSections.size()) + 1);
3153 3 : oObj.Set("StartByte", osPlaceHolder);
3154 3 : oSection.osPlaceHolder = std::move(osPlaceHolder);
3155 : }
3156 :
3157 5 : if (!m_osExternalFilename.empty())
3158 : {
3159 4 : CPLString osDstFilename(CPLGetBasenameSafe(GetDescription()));
3160 2 : osDstFilename += ".";
3161 2 : osDstFilename += osContainerName;
3162 2 : if (!osName.empty())
3163 : {
3164 2 : osDstFilename += ".";
3165 2 : osDstFilename += osName;
3166 : }
3167 :
3168 4 : oSection.osDstFilename = CPLFormFilenameSafe(
3169 4 : CPLGetPathSafe(GetDescription()).c_str(), osDstFilename,
3170 2 : nullptr);
3171 :
3172 2 : oObj.Set(osKeyFilename, osDstFilename);
3173 : }
3174 : else
3175 : {
3176 3 : oObj.Delete(osKeyFilename);
3177 : }
3178 :
3179 5 : m_aoNonPixelSections.push_back(std::move(oSection));
3180 : }
3181 : }
3182 126 : m_oJSonLabel = std::move(oLabel);
3183 126 : }
3184 :
3185 : /************************************************************************/
3186 : /* BuildHistory() */
3187 : /************************************************************************/
3188 :
3189 126 : void ISIS3Dataset::BuildHistory()
3190 : {
3191 252 : CPLString osHistory;
3192 :
3193 126 : if (m_oSrcJSonLabel.IsValid() && m_bUseSrcHistory)
3194 : {
3195 22 : vsi_l_offset nHistoryOffset = 0;
3196 22 : int nHistorySize = 0;
3197 44 : CPLString osSrcFilename;
3198 :
3199 66 : CPLJSONObject oFilename = m_oSrcJSonLabel["_filename"];
3200 22 : if (oFilename.GetType() == CPLJSONObject::Type::String)
3201 : {
3202 17 : osSrcFilename = oFilename.ToString();
3203 : }
3204 44 : CPLString osHistoryFilename(osSrcFilename);
3205 66 : CPLJSONObject oHistory = m_oSrcJSonLabel["History"];
3206 22 : if (oHistory.GetType() == CPLJSONObject::Type::Object)
3207 : {
3208 33 : CPLJSONObject oHistoryFilename = oHistory["^History"];
3209 11 : if (oHistoryFilename.GetType() == CPLJSONObject::Type::String)
3210 : {
3211 14 : osHistoryFilename = CPLFormFilenameSafe(
3212 14 : CPLGetPathSafe(osSrcFilename).c_str(),
3213 21 : oHistoryFilename.ToString().c_str(), nullptr);
3214 : }
3215 :
3216 33 : CPLJSONObject oStartByte = oHistory["StartByte"];
3217 11 : if (oStartByte.GetType() == CPLJSONObject::Type::Integer)
3218 : {
3219 11 : if (oStartByte.ToInteger() > 0)
3220 : {
3221 11 : nHistoryOffset =
3222 11 : static_cast<vsi_l_offset>(oStartByte.ToInteger()) - 1U;
3223 : }
3224 : }
3225 :
3226 33 : CPLJSONObject oBytes = oHistory["Bytes"];
3227 11 : if (oBytes.GetType() == CPLJSONObject::Type::Integer)
3228 : {
3229 11 : nHistorySize = static_cast<int>(oBytes.ToInteger());
3230 : }
3231 : }
3232 :
3233 22 : if (osHistoryFilename.empty())
3234 : {
3235 5 : CPLDebug("ISIS3", "Cannot find filename for source history");
3236 : }
3237 17 : else if (nHistorySize <= 0 || nHistorySize > 1000000)
3238 : {
3239 6 : CPLDebug("ISIS3", "Invalid or missing value for History.Bytes "
3240 : "for source history");
3241 : }
3242 : else
3243 : {
3244 11 : VSILFILE *fpHistory = VSIFOpenL(osHistoryFilename, "rb");
3245 11 : if (fpHistory != nullptr)
3246 : {
3247 6 : VSIFSeekL(fpHistory, nHistoryOffset, SEEK_SET);
3248 6 : osHistory.resize(nHistorySize);
3249 6 : if (VSIFReadL(&osHistory[0], nHistorySize, 1, fpHistory) != 1)
3250 : {
3251 0 : CPLError(CE_Warning, CPLE_FileIO,
3252 : "Cannot read %d bytes at offset " CPL_FRMT_GUIB
3253 : "of %s: history will not be preserved",
3254 : nHistorySize, nHistoryOffset,
3255 : osHistoryFilename.c_str());
3256 0 : osHistory.clear();
3257 : }
3258 6 : VSIFCloseL(fpHistory);
3259 : }
3260 : else
3261 : {
3262 5 : CPLError(CE_Warning, CPLE_FileIO,
3263 : "Cannot open %s: history will not be preserved",
3264 : osHistoryFilename.c_str());
3265 : }
3266 : }
3267 : }
3268 :
3269 126 : if (m_bAddGDALHistory && !m_osGDALHistory.empty())
3270 : {
3271 1 : if (!osHistory.empty())
3272 0 : osHistory += "\n";
3273 1 : osHistory += m_osGDALHistory;
3274 : }
3275 125 : else if (m_bAddGDALHistory)
3276 : {
3277 123 : if (!osHistory.empty())
3278 6 : osHistory += "\n";
3279 :
3280 246 : CPLJSONObject oHistoryObj;
3281 123 : char szFullFilename[2048] = {0};
3282 123 : if (!CPLGetExecPath(szFullFilename, sizeof(szFullFilename) - 1))
3283 0 : strcpy(szFullFilename, "unknown_program");
3284 246 : const CPLString osProgram(CPLGetBasenameSafe(szFullFilename));
3285 246 : const CPLString osPath(CPLGetPathSafe(szFullFilename));
3286 :
3287 246 : CPLJSONObject oObj;
3288 123 : oHistoryObj.Add(osProgram, oObj);
3289 :
3290 123 : oObj.Add("_type", "object");
3291 123 : oObj.Add("GdalVersion", GDALVersionInfo("RELEASE_NAME"));
3292 123 : if (osPath != ".")
3293 123 : oObj.Add("ProgramPath", osPath);
3294 123 : time_t nCurTime = time(nullptr);
3295 123 : if (nCurTime != -1)
3296 : {
3297 : struct tm mytm;
3298 123 : CPLUnixTimeToYMDHMS(nCurTime, &mytm);
3299 123 : oObj.Add("ExecutionDateTime",
3300 : CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
3301 123 : mytm.tm_year + 1900, mytm.tm_mon + 1,
3302 : mytm.tm_mday, mytm.tm_hour, mytm.tm_min,
3303 : mytm.tm_sec));
3304 : }
3305 123 : char szHostname[256] = {0};
3306 123 : if (gethostname(szHostname, sizeof(szHostname) - 1) == 0)
3307 : {
3308 123 : oObj.Add("HostName", std::string(szHostname));
3309 : }
3310 123 : const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
3311 123 : if (pszUsername == nullptr)
3312 123 : pszUsername = CPLGetConfigOption("USER", nullptr);
3313 123 : if (pszUsername != nullptr)
3314 : {
3315 0 : oObj.Add("UserName", pszUsername);
3316 : }
3317 123 : oObj.Add("Description", "GDAL conversion");
3318 :
3319 123 : CPLJSONObject oUserParameters;
3320 123 : oObj.Add("UserParameters", oUserParameters);
3321 :
3322 123 : oUserParameters.Add("_type", "group");
3323 123 : if (!m_osFromFilename.empty())
3324 : {
3325 32 : const CPLString osFromFilename = CPLGetFilename(m_osFromFilename);
3326 32 : oUserParameters.Add("FROM", osFromFilename);
3327 : }
3328 123 : if (nullptr != GetDescription())
3329 : {
3330 123 : const CPLString osToFileName = CPLGetFilename(GetDescription());
3331 123 : oUserParameters.Add("TO", osToFileName);
3332 : }
3333 123 : if (m_bForce360)
3334 1 : oUserParameters.Add("Force_360", "true");
3335 :
3336 123 : osHistory += SerializeAsPDL(oHistoryObj);
3337 : }
3338 :
3339 126 : m_osHistory = std::move(osHistory);
3340 126 : }
3341 :
3342 : /************************************************************************/
3343 : /* WriteLabel() */
3344 : /************************************************************************/
3345 :
3346 125 : void ISIS3Dataset::WriteLabel()
3347 : {
3348 125 : m_bIsLabelWritten = true;
3349 :
3350 125 : if (!m_oJSonLabel.IsValid())
3351 125 : BuildLabel();
3352 :
3353 : // Serialize label
3354 250 : CPLString osLabel(SerializeAsPDL(m_oJSonLabel));
3355 125 : osLabel += "End\n";
3356 125 : if (m_osExternalFilename.empty() && osLabel.size() < 65536)
3357 : {
3358 : // In-line labels have conventionally a minimize size of 65536 bytes
3359 : // See #2741
3360 94 : osLabel.resize(65536);
3361 : }
3362 125 : char *pszLabel = &osLabel[0];
3363 125 : const int nLabelSize = static_cast<int>(osLabel.size());
3364 :
3365 : // Hack back StartByte value
3366 : {
3367 125 : char *pszStartByte = strstr(pszLabel, pszSTARTBYTE_PLACEHOLDER);
3368 125 : if (pszStartByte != nullptr)
3369 : {
3370 94 : const char *pszOffset = CPLSPrintf("%d", 1 + nLabelSize);
3371 94 : memcpy(pszStartByte, pszOffset, strlen(pszOffset));
3372 94 : memset(pszStartByte + strlen(pszOffset), ' ',
3373 94 : strlen(pszSTARTBYTE_PLACEHOLDER) - strlen(pszOffset));
3374 : }
3375 : }
3376 :
3377 : // Hack back Label.Bytes value
3378 : {
3379 125 : char *pszLabelBytes = strstr(pszLabel, pszLABEL_BYTES_PLACEHOLDER);
3380 125 : if (pszLabelBytes != nullptr)
3381 : {
3382 125 : const char *pszBytes = CPLSPrintf("%d", nLabelSize);
3383 125 : memcpy(pszLabelBytes, pszBytes, strlen(pszBytes));
3384 125 : memset(pszLabelBytes + strlen(pszBytes), ' ',
3385 125 : strlen(pszLABEL_BYTES_PLACEHOLDER) - strlen(pszBytes));
3386 : }
3387 : }
3388 :
3389 125 : const GDALDataType eType = GetRasterBand(1)->GetRasterDataType();
3390 125 : const int nDTSize = GDALGetDataTypeSizeBytes(eType);
3391 125 : vsi_l_offset nImagePixels = 0;
3392 125 : if (m_poExternalDS == nullptr)
3393 : {
3394 107 : if (m_bIsTiled)
3395 : {
3396 7 : int nBlockXSize = 1, nBlockYSize = 1;
3397 7 : GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
3398 7 : nImagePixels = static_cast<vsi_l_offset>(nBlockXSize) *
3399 14 : nBlockYSize * nBands *
3400 7 : DIV_ROUND_UP(nRasterXSize, nBlockXSize) *
3401 7 : DIV_ROUND_UP(nRasterYSize, nBlockYSize);
3402 : }
3403 : else
3404 : {
3405 100 : nImagePixels =
3406 100 : static_cast<vsi_l_offset>(nRasterXSize) * nRasterYSize * nBands;
3407 : }
3408 : }
3409 :
3410 : // Hack back History.StartBytes value
3411 : char *pszHistoryStartBytes =
3412 125 : strstr(pszLabel, pszHISTORY_STARTBYTE_PLACEHOLDER);
3413 :
3414 125 : vsi_l_offset nHistoryOffset = 0;
3415 125 : vsi_l_offset nLastOffset = 0;
3416 125 : if (pszHistoryStartBytes != nullptr)
3417 : {
3418 93 : CPLAssert(m_osExternalFilename.empty());
3419 93 : nHistoryOffset = nLabelSize + nImagePixels * nDTSize;
3420 93 : nLastOffset = nHistoryOffset + m_osHistory.size();
3421 : const char *pszStartByte =
3422 93 : CPLSPrintf(CPL_FRMT_GUIB, nHistoryOffset + 1);
3423 93 : CPLAssert(strlen(pszStartByte) <
3424 : strlen(pszHISTORY_STARTBYTE_PLACEHOLDER));
3425 93 : memcpy(pszHistoryStartBytes, pszStartByte, strlen(pszStartByte));
3426 93 : memset(pszHistoryStartBytes + strlen(pszStartByte), ' ',
3427 93 : strlen(pszHISTORY_STARTBYTE_PLACEHOLDER) - strlen(pszStartByte));
3428 : }
3429 :
3430 : // Replace placeholders in other sections
3431 130 : for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
3432 : {
3433 5 : if (!m_aoNonPixelSections[i].osPlaceHolder.empty())
3434 : {
3435 : char *pszPlaceHolder =
3436 3 : strstr(pszLabel, m_aoNonPixelSections[i].osPlaceHolder.c_str());
3437 3 : CPLAssert(pszPlaceHolder != nullptr);
3438 : const char *pszStartByte =
3439 3 : CPLSPrintf(CPL_FRMT_GUIB, nLastOffset + 1);
3440 3 : nLastOffset += m_aoNonPixelSections[i].nSize;
3441 3 : CPLAssert(strlen(pszStartByte) <
3442 : m_aoNonPixelSections[i].osPlaceHolder.size());
3443 :
3444 3 : memcpy(pszPlaceHolder, pszStartByte, strlen(pszStartByte));
3445 3 : memset(pszPlaceHolder + strlen(pszStartByte), ' ',
3446 3 : m_aoNonPixelSections[i].osPlaceHolder.size() -
3447 3 : strlen(pszStartByte));
3448 : }
3449 : }
3450 :
3451 : // Write to final file
3452 125 : VSIFSeekL(m_fpLabel, 0, SEEK_SET);
3453 125 : VSIFWriteL(pszLabel, 1, osLabel.size(), m_fpLabel);
3454 :
3455 125 : if (m_osExternalFilename.empty())
3456 : {
3457 : // Update image offset in bands
3458 94 : if (m_bIsTiled)
3459 : {
3460 15 : for (int i = 0; i < nBands; i++)
3461 : {
3462 : ISISTiledBand *poBand =
3463 9 : reinterpret_cast<ISISTiledBand *>(GetRasterBand(i + 1));
3464 9 : poBand->m_nFirstTileOffset += nLabelSize;
3465 : }
3466 : }
3467 : else
3468 : {
3469 206 : for (int i = 0; i < nBands; i++)
3470 : {
3471 : ISIS3RawRasterBand *poBand =
3472 : reinterpret_cast<ISIS3RawRasterBand *>(
3473 118 : GetRasterBand(i + 1));
3474 118 : poBand->nImgOffset += nLabelSize;
3475 : }
3476 : }
3477 : }
3478 :
3479 125 : if (m_bInitToNodata)
3480 : {
3481 : // Initialize the image to nodata
3482 55 : const double dfNoData = GetRasterBand(1)->GetNoDataValue();
3483 55 : if (dfNoData == 0.0)
3484 : {
3485 43 : VSIFTruncateL(m_fpImage,
3486 43 : VSIFTellL(m_fpImage) + nImagePixels * nDTSize);
3487 : }
3488 12 : else if (nDTSize != 0) // to make Coverity not warn about div by 0
3489 : {
3490 12 : const int nPageSize = 4096; // Must be multiple of 4 since
3491 : // Float32 is the largest type
3492 12 : CPLAssert((nPageSize % nDTSize) == 0);
3493 12 : const int nMaxPerPage = nPageSize / nDTSize;
3494 12 : GByte *pabyTemp = static_cast<GByte *>(CPLMalloc(nPageSize));
3495 12 : GDALCopyWords(&dfNoData, GDT_Float64, 0, pabyTemp, eType, nDTSize,
3496 : nMaxPerPage);
3497 : #ifdef CPL_MSB
3498 : GDALSwapWords(pabyTemp, nDTSize, nMaxPerPage, nDTSize);
3499 : #endif
3500 80 : for (vsi_l_offset i = 0; i < nImagePixels; i += nMaxPerPage)
3501 : {
3502 : int n;
3503 68 : if (i + nMaxPerPage <= nImagePixels)
3504 56 : n = nMaxPerPage;
3505 : else
3506 12 : n = static_cast<int>(nImagePixels - i);
3507 68 : if (VSIFWriteL(pabyTemp, static_cast<size_t>(n) * nDTSize, 1,
3508 68 : m_fpImage) != 1)
3509 : {
3510 0 : CPLError(CE_Failure, CPLE_FileIO,
3511 : "Cannot initialize imagery to null");
3512 0 : break;
3513 : }
3514 : }
3515 :
3516 12 : CPLFree(pabyTemp);
3517 : }
3518 : }
3519 :
3520 : // Write history
3521 125 : if (!m_osHistory.empty())
3522 : {
3523 123 : if (m_osExternalFilename.empty())
3524 : {
3525 93 : VSIFSeekL(m_fpLabel, nHistoryOffset, SEEK_SET);
3526 93 : VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), m_fpLabel);
3527 : }
3528 : else
3529 : {
3530 60 : CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
3531 30 : osFilename += ".History.IsisCube";
3532 60 : osFilename = CPLFormFilenameSafe(
3533 60 : CPLGetPathSafe(GetDescription()).c_str(), osFilename, nullptr);
3534 30 : VSILFILE *fp = VSIFOpenL(osFilename, "wb");
3535 30 : if (fp)
3536 : {
3537 30 : m_aosAdditionalFiles.AddString(osFilename);
3538 :
3539 30 : VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), fp);
3540 30 : VSIFCloseL(fp);
3541 : }
3542 : else
3543 : {
3544 0 : CPLError(CE_Warning, CPLE_FileIO, "Cannot write %s",
3545 : osFilename.c_str());
3546 : }
3547 : }
3548 : }
3549 :
3550 : // Write other non pixel sections
3551 130 : for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
3552 : {
3553 : VSILFILE *fpSrc =
3554 5 : VSIFOpenL(m_aoNonPixelSections[i].osSrcFilename, "rb");
3555 5 : if (fpSrc == nullptr)
3556 : {
3557 0 : CPLError(CE_Warning, CPLE_FileIO, "Cannot open %s",
3558 0 : m_aoNonPixelSections[i].osSrcFilename.c_str());
3559 0 : continue;
3560 : }
3561 :
3562 5 : VSILFILE *fpDest = m_fpLabel;
3563 5 : if (!m_aoNonPixelSections[i].osDstFilename.empty())
3564 : {
3565 2 : fpDest = VSIFOpenL(m_aoNonPixelSections[i].osDstFilename, "wb");
3566 2 : if (fpDest == nullptr)
3567 : {
3568 0 : CPLError(CE_Warning, CPLE_FileIO, "Cannot create %s",
3569 0 : m_aoNonPixelSections[i].osDstFilename.c_str());
3570 0 : VSIFCloseL(fpSrc);
3571 0 : continue;
3572 : }
3573 :
3574 : m_aosAdditionalFiles.AddString(
3575 2 : m_aoNonPixelSections[i].osDstFilename);
3576 : }
3577 :
3578 5 : VSIFSeekL(fpSrc, m_aoNonPixelSections[i].nSrcOffset, SEEK_SET);
3579 : GByte abyBuffer[4096];
3580 5 : vsi_l_offset nRemaining = m_aoNonPixelSections[i].nSize;
3581 10 : while (nRemaining)
3582 : {
3583 5 : size_t nToRead = 4096;
3584 5 : if (nRemaining < nToRead)
3585 5 : nToRead = static_cast<size_t>(nRemaining);
3586 5 : size_t nRead = VSIFReadL(abyBuffer, 1, nToRead, fpSrc);
3587 5 : if (nRead != nToRead)
3588 : {
3589 0 : CPLError(CE_Warning, CPLE_FileIO,
3590 : "Could not read " CPL_FRMT_GUIB " bytes from %s",
3591 0 : m_aoNonPixelSections[i].nSize,
3592 0 : m_aoNonPixelSections[i].osSrcFilename.c_str());
3593 0 : break;
3594 : }
3595 5 : VSIFWriteL(abyBuffer, 1, nRead, fpDest);
3596 5 : nRemaining -= nRead;
3597 : }
3598 :
3599 5 : VSIFCloseL(fpSrc);
3600 5 : if (fpDest != m_fpLabel)
3601 2 : VSIFCloseL(fpDest);
3602 : }
3603 125 : }
3604 :
3605 : /************************************************************************/
3606 : /* SerializeAsPDL() */
3607 : /************************************************************************/
3608 :
3609 248 : CPLString ISIS3Dataset::SerializeAsPDL(const CPLJSONObject &oObj)
3610 : {
3611 496 : const CPLString osTmpFile(VSIMemGenerateHiddenFilename("isis3_pdl"));
3612 248 : VSILFILE *fpTmp = VSIFOpenL(osTmpFile, "wb+");
3613 248 : SerializeAsPDL(fpTmp, oObj);
3614 248 : VSIFCloseL(fpTmp);
3615 : CPLString osContent(reinterpret_cast<char *>(
3616 248 : VSIGetMemFileBuffer(osTmpFile, nullptr, FALSE)));
3617 248 : VSIUnlink(osTmpFile);
3618 496 : return osContent;
3619 : }
3620 :
3621 : /************************************************************************/
3622 : /* SerializeAsPDL() */
3623 : /************************************************************************/
3624 :
3625 1329 : void ISIS3Dataset::SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
3626 : int nDepth)
3627 : {
3628 2658 : CPLString osIndentation;
3629 3235 : for (int i = 0; i < nDepth; i++)
3630 1906 : osIndentation += " ";
3631 1329 : const size_t WIDTH = 79;
3632 :
3633 2658 : std::vector<CPLJSONObject> aoChildren = oObj.GetChildren();
3634 1329 : size_t nMaxKeyLength = 0;
3635 7175 : for (const CPLJSONObject &oChild : aoChildren)
3636 : {
3637 5846 : const CPLString osKey = oChild.GetName();
3638 10602 : if (EQUAL(osKey, "_type") || EQUAL(osKey, "_container_name") ||
3639 4756 : EQUAL(osKey, "_filename"))
3640 : {
3641 1109 : continue;
3642 : }
3643 :
3644 4737 : const auto eType = oChild.GetType();
3645 4737 : if (eType == CPLJSONObject::Type::String ||
3646 2140 : eType == CPLJSONObject::Type::Integer ||
3647 1385 : eType == CPLJSONObject::Type::Double ||
3648 : eType == CPLJSONObject::Type::Array)
3649 : {
3650 3361 : if (osKey.size() > nMaxKeyLength)
3651 : {
3652 1597 : nMaxKeyLength = osKey.size();
3653 : }
3654 : }
3655 1376 : else if (eType == CPLJSONObject::Type::Object)
3656 : {
3657 4128 : CPLJSONObject oValue = oChild.GetObj("value");
3658 4128 : CPLJSONObject oUnit = oChild.GetObj("unit");
3659 1607 : if (oValue.IsValid() &&
3660 231 : oUnit.GetType() == CPLJSONObject::Type::String)
3661 : {
3662 231 : if (osKey.size() > nMaxKeyLength)
3663 : {
3664 61 : nMaxKeyLength = osKey.size();
3665 : }
3666 : }
3667 : }
3668 : }
3669 :
3670 7175 : for (const CPLJSONObject &oChild : aoChildren)
3671 : {
3672 5846 : const CPLString osKey = oChild.GetName();
3673 10602 : if (EQUAL(osKey, "_type") || EQUAL(osKey, "_container_name") ||
3674 4756 : EQUAL(osKey, "_filename"))
3675 : {
3676 1109 : continue;
3677 : }
3678 4737 : if (STARTS_WITH(osKey, "_comment"))
3679 : {
3680 1 : if (oChild.GetType() == CPLJSONObject::Type::String)
3681 : {
3682 1 : VSIFPrintfL(fp, "#%s\n", oChild.ToString().c_str());
3683 : }
3684 1 : continue;
3685 : }
3686 9472 : CPLString osPadding;
3687 4736 : size_t nLen = osKey.size();
3688 4736 : if (nLen < nMaxKeyLength)
3689 : {
3690 2894 : osPadding.append(nMaxKeyLength - nLen, ' ');
3691 : }
3692 :
3693 4736 : const auto eType = oChild.GetType();
3694 4736 : if (eType == CPLJSONObject::Type::Object)
3695 : {
3696 4128 : CPLJSONObject oType = oChild.GetObj("_type");
3697 4128 : CPLJSONObject oContainerName = oChild.GetObj("_container_name");
3698 2752 : CPLString osContainerName = osKey;
3699 1376 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
3700 : {
3701 9 : osContainerName = oContainerName.ToString();
3702 : }
3703 1376 : if (oType.GetType() == CPLJSONObject::Type::String)
3704 : {
3705 3243 : const CPLString osType = oType.ToString();
3706 1081 : if (EQUAL(osType, "Object"))
3707 : {
3708 631 : if (nDepth == 0 && VSIFTellL(fp) != 0)
3709 258 : VSIFPrintfL(fp, "\n");
3710 631 : VSIFPrintfL(fp, "%sObject = %s\n", osIndentation.c_str(),
3711 : osContainerName.c_str());
3712 631 : SerializeAsPDL(fp, oChild, nDepth + 1);
3713 631 : VSIFPrintfL(fp, "%sEnd_Object\n", osIndentation.c_str());
3714 : }
3715 450 : else if (EQUAL(osType, "Group"))
3716 : {
3717 450 : VSIFPrintfL(fp, "\n");
3718 450 : VSIFPrintfL(fp, "%sGroup = %s\n", osIndentation.c_str(),
3719 : osContainerName.c_str());
3720 450 : SerializeAsPDL(fp, oChild, nDepth + 1);
3721 450 : VSIFPrintfL(fp, "%sEnd_Group\n", osIndentation.c_str());
3722 : }
3723 : }
3724 : else
3725 : {
3726 885 : CPLJSONObject oValue = oChild.GetObj("value");
3727 885 : CPLJSONObject oUnit = oChild.GetObj("unit");
3728 526 : if (oValue.IsValid() &&
3729 231 : oUnit.GetType() == CPLJSONObject::Type::String)
3730 : {
3731 693 : const CPLString osUnit = oUnit.ToString();
3732 231 : const auto eValueType = oValue.GetType();
3733 231 : if (eValueType == CPLJSONObject::Type::Integer)
3734 : {
3735 1 : VSIFPrintfL(fp, "%s%s%s = %d <%s>\n",
3736 : osIndentation.c_str(), osKey.c_str(),
3737 : osPadding.c_str(), oValue.ToInteger(),
3738 : osUnit.c_str());
3739 : }
3740 230 : else if (eValueType == CPLJSONObject::Type::Double)
3741 : {
3742 229 : const double dfVal = oValue.ToDouble();
3743 229 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
3744 229 : static_cast<int>(dfVal) == dfVal)
3745 : {
3746 87 : VSIFPrintfL(fp, "%s%s%s = %d.0 <%s>\n",
3747 : osIndentation.c_str(), osKey.c_str(),
3748 : osPadding.c_str(),
3749 : static_cast<int>(dfVal),
3750 : osUnit.c_str());
3751 : }
3752 : else
3753 : {
3754 142 : VSIFPrintfL(fp, "%s%s%s = %.17g <%s>\n",
3755 : osIndentation.c_str(), osKey.c_str(),
3756 : osPadding.c_str(), dfVal,
3757 : osUnit.c_str());
3758 : }
3759 : }
3760 : }
3761 : }
3762 : }
3763 3360 : else if (eType == CPLJSONObject::Type::String)
3764 : {
3765 5811 : CPLString osVal = oChild.ToString();
3766 1937 : const char *pszVal = osVal.c_str();
3767 1937 : if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
3768 1809 : strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
3769 : {
3770 128 : osVal.replaceAll("\\n", "\n");
3771 128 : osVal.replaceAll("\\r", "\r");
3772 128 : VSIFPrintfL(fp, "%s%s%s = \"%s\"\n", osIndentation.c_str(),
3773 : osKey.c_str(), osPadding.c_str(), osVal.c_str());
3774 : }
3775 : else
3776 : {
3777 1809 : if (osIndentation.size() + osKey.size() + osPadding.size() +
3778 1809 : strlen(" = ") + strlen(pszVal) >
3779 1810 : WIDTH &&
3780 1 : osIndentation.size() + osKey.size() + osPadding.size() +
3781 : strlen(" = ") <
3782 : WIDTH)
3783 : {
3784 1 : size_t nFirstPos = osIndentation.size() + osKey.size() +
3785 1 : osPadding.size() + strlen(" = ");
3786 1 : VSIFPrintfL(fp, "%s%s%s = ", osIndentation.c_str(),
3787 : osKey.c_str(), osPadding.c_str());
3788 1 : size_t nCurPos = nFirstPos;
3789 96 : for (int j = 0; pszVal[j] != '\0'; j++)
3790 : {
3791 95 : nCurPos++;
3792 95 : if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
3793 : {
3794 1 : VSIFPrintfL(fp, "-\n");
3795 15 : for (size_t k = 0; k < nFirstPos; k++)
3796 : {
3797 14 : const char chSpace = ' ';
3798 14 : VSIFWriteL(&chSpace, 1, 1, fp);
3799 : }
3800 1 : nCurPos = nFirstPos + 1;
3801 : }
3802 95 : VSIFWriteL(&pszVal[j], 1, 1, fp);
3803 : }
3804 1 : VSIFPrintfL(fp, "\n");
3805 : }
3806 : else
3807 : {
3808 1808 : VSIFPrintfL(fp, "%s%s%s = %s\n", osIndentation.c_str(),
3809 : osKey.c_str(), osPadding.c_str(), pszVal);
3810 : }
3811 : }
3812 : }
3813 1423 : else if (eType == CPLJSONObject::Type::Integer)
3814 : {
3815 659 : const int nVal = oChild.ToInteger();
3816 659 : VSIFPrintfL(fp, "%s%s%s = %d\n", osIndentation.c_str(),
3817 : osKey.c_str(), osPadding.c_str(), nVal);
3818 : }
3819 764 : else if (eType == CPLJSONObject::Type::Double)
3820 : {
3821 755 : const double dfVal = oChild.ToDouble();
3822 755 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
3823 755 : static_cast<int>(dfVal) == dfVal)
3824 : {
3825 526 : VSIFPrintfL(fp, "%s%s%s = %d.0\n", osIndentation.c_str(),
3826 : osKey.c_str(), osPadding.c_str(),
3827 : static_cast<int>(dfVal));
3828 : }
3829 : else
3830 : {
3831 229 : VSIFPrintfL(fp, "%s%s%s = %.17g\n", osIndentation.c_str(),
3832 : osKey.c_str(), osPadding.c_str(), dfVal);
3833 : }
3834 : }
3835 9 : else if (eType == CPLJSONObject::Type::Array)
3836 : {
3837 18 : CPLJSONArray oArrayItem(oChild);
3838 9 : const int nLength = oArrayItem.Size();
3839 9 : size_t nFirstPos = osIndentation.size() + osKey.size() +
3840 9 : osPadding.size() + strlen(" = (");
3841 9 : VSIFPrintfL(fp, "%s%s%s = (", osIndentation.c_str(), osKey.c_str(),
3842 : osPadding.c_str());
3843 9 : size_t nCurPos = nFirstPos;
3844 46 : for (int idx = 0; idx < nLength; idx++)
3845 : {
3846 74 : CPLJSONObject oItem = oArrayItem[idx];
3847 37 : const auto eArrayItemType = oItem.GetType();
3848 37 : if (eArrayItemType == CPLJSONObject::Type::String)
3849 : {
3850 36 : CPLString osVal = oItem.ToString();
3851 12 : const char *pszVal = osVal.c_str();
3852 12 : if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
3853 7 : strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
3854 : {
3855 5 : osVal.replaceAll("\\n", "\n");
3856 5 : osVal.replaceAll("\\r", "\r");
3857 5 : VSIFPrintfL(fp, "\"%s\"", osVal.c_str());
3858 : }
3859 7 : else if (nFirstPos < WIDTH &&
3860 7 : nCurPos + strlen(pszVal) > WIDTH)
3861 : {
3862 1 : if (idx > 0)
3863 : {
3864 1 : VSIFPrintfL(fp, "\n");
3865 16 : for (size_t j = 0; j < nFirstPos; j++)
3866 : {
3867 15 : const char chSpace = ' ';
3868 15 : VSIFWriteL(&chSpace, 1, 1, fp);
3869 : }
3870 1 : nCurPos = nFirstPos;
3871 : }
3872 :
3873 102 : for (int j = 0; pszVal[j] != '\0'; j++)
3874 : {
3875 101 : nCurPos++;
3876 101 : if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
3877 : {
3878 1 : VSIFPrintfL(fp, "-\n");
3879 16 : for (size_t k = 0; k < nFirstPos; k++)
3880 : {
3881 15 : const char chSpace = ' ';
3882 15 : VSIFWriteL(&chSpace, 1, 1, fp);
3883 : }
3884 1 : nCurPos = nFirstPos + 1;
3885 : }
3886 101 : VSIFWriteL(&pszVal[j], 1, 1, fp);
3887 1 : }
3888 : }
3889 : else
3890 : {
3891 6 : VSIFPrintfL(fp, "%s", pszVal);
3892 6 : nCurPos += strlen(pszVal);
3893 : }
3894 : }
3895 25 : else if (eArrayItemType == CPLJSONObject::Type::Integer)
3896 : {
3897 16 : const int nVal = oItem.ToInteger();
3898 16 : const char *pszVal = CPLSPrintf("%d", nVal);
3899 16 : const size_t nValLen = strlen(pszVal);
3900 16 : if (nFirstPos < WIDTH && idx > 0 &&
3901 12 : nCurPos + nValLen > WIDTH)
3902 : {
3903 1 : VSIFPrintfL(fp, "\n");
3904 16 : for (size_t j = 0; j < nFirstPos; j++)
3905 : {
3906 15 : const char chSpace = ' ';
3907 15 : VSIFWriteL(&chSpace, 1, 1, fp);
3908 : }
3909 1 : nCurPos = nFirstPos;
3910 : }
3911 16 : VSIFPrintfL(fp, "%d", nVal);
3912 16 : nCurPos += nValLen;
3913 : }
3914 9 : else if (eArrayItemType == CPLJSONObject::Type::Double)
3915 : {
3916 9 : const double dfVal = oItem.ToDouble();
3917 9 : CPLString osVal;
3918 9 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
3919 9 : static_cast<int>(dfVal) == dfVal)
3920 : {
3921 8 : osVal = CPLSPrintf("%d.0", static_cast<int>(dfVal));
3922 : }
3923 : else
3924 : {
3925 1 : osVal = CPLSPrintf("%.17g", dfVal);
3926 : }
3927 9 : const size_t nValLen = osVal.size();
3928 9 : if (nFirstPos < WIDTH && idx > 0 &&
3929 8 : nCurPos + nValLen > WIDTH)
3930 : {
3931 1 : VSIFPrintfL(fp, "\n");
3932 16 : for (size_t j = 0; j < nFirstPos; j++)
3933 : {
3934 15 : const char chSpace = ' ';
3935 15 : VSIFWriteL(&chSpace, 1, 1, fp);
3936 : }
3937 1 : nCurPos = nFirstPos;
3938 : }
3939 9 : VSIFPrintfL(fp, "%s", osVal.c_str());
3940 9 : nCurPos += nValLen;
3941 : }
3942 37 : if (idx < nLength - 1)
3943 : {
3944 28 : VSIFPrintfL(fp, ", ");
3945 28 : nCurPos += 2;
3946 : }
3947 : }
3948 9 : VSIFPrintfL(fp, ")\n");
3949 : }
3950 : }
3951 1329 : }
3952 :
3953 : /************************************************************************/
3954 : /* Create() */
3955 : /************************************************************************/
3956 :
3957 159 : GDALDataset *ISIS3Dataset::Create(const char *pszFilename, int nXSize,
3958 : int nYSize, int nBandsIn, GDALDataType eType,
3959 : char **papszOptions)
3960 : {
3961 159 : if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
3962 : eType != GDT_Float32)
3963 : {
3964 27 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
3965 27 : return nullptr;
3966 : }
3967 132 : if (nBandsIn == 0 || nBandsIn > 32767)
3968 : {
3969 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
3970 1 : return nullptr;
3971 : }
3972 :
3973 : const char *pszDataLocation =
3974 131 : CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
3975 131 : const bool bIsTiled = CPLFetchBool(papszOptions, "TILED", false);
3976 : const int nBlockXSize = std::max(
3977 131 : 1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256")));
3978 : const int nBlockYSize = std::max(
3979 131 : 1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256")));
3980 165 : if (!EQUAL(pszDataLocation, "LABEL") &&
3981 165 : !EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "LBL"))
3982 : {
3983 1 : CPLError(CE_Failure, CPLE_NotSupported,
3984 : "For DATA_LOCATION=%s, "
3985 : "the main filename should have a .lbl extension",
3986 : pszDataLocation);
3987 1 : return nullptr;
3988 : }
3989 :
3990 : const char *pszPermission =
3991 130 : VSISupportsRandomWrite(pszFilename, true) ? "wb+" : "wb";
3992 :
3993 130 : VSILFILE *fp = VSIFOpenExL(pszFilename, pszPermission, true);
3994 130 : if (fp == nullptr)
3995 : {
3996 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
3997 : VSIGetLastErrorMsg());
3998 3 : return nullptr;
3999 : }
4000 127 : VSILFILE *fpImage = nullptr;
4001 254 : std::string osExternalFilename;
4002 127 : GDALDataset *poExternalDS = nullptr;
4003 127 : bool bGeoTIFFAsRegularExternal = false;
4004 127 : if (EQUAL(pszDataLocation, "EXTERNAL"))
4005 : {
4006 : osExternalFilename = CSLFetchNameValueDef(
4007 : papszOptions, "EXTERNAL_FILENAME",
4008 14 : CPLResetExtensionSafe(pszFilename, "cub").c_str());
4009 14 : fpImage = VSIFOpenExL(osExternalFilename.c_str(), pszPermission, true);
4010 14 : if (fpImage == nullptr)
4011 : {
4012 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
4013 : osExternalFilename.c_str(), VSIGetLastErrorMsg());
4014 1 : VSIFCloseL(fp);
4015 1 : return nullptr;
4016 : }
4017 : }
4018 113 : else if (EQUAL(pszDataLocation, "GEOTIFF"))
4019 : {
4020 : osExternalFilename = CSLFetchNameValueDef(
4021 : papszOptions, "EXTERNAL_FILENAME",
4022 19 : CPLResetExtensionSafe(pszFilename, "tif").c_str());
4023 : GDALDriver *poDrv =
4024 19 : static_cast<GDALDriver *>(GDALGetDriverByName("GTiff"));
4025 19 : if (poDrv == nullptr)
4026 : {
4027 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GTiff driver");
4028 0 : VSIFCloseL(fp);
4029 0 : return nullptr;
4030 : }
4031 19 : char **papszGTiffOptions = nullptr;
4032 : papszGTiffOptions =
4033 19 : CSLSetNameValue(papszGTiffOptions, "ENDIANNESS", "LITTLE");
4034 19 : if (bIsTiled)
4035 : {
4036 : papszGTiffOptions =
4037 3 : CSLSetNameValue(papszGTiffOptions, "TILED", "YES");
4038 3 : papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKXSIZE",
4039 : CPLSPrintf("%d", nBlockXSize));
4040 3 : papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE",
4041 : CPLSPrintf("%d", nBlockYSize));
4042 : }
4043 : const char *pszGTiffOptions =
4044 19 : CSLFetchNameValueDef(papszOptions, "GEOTIFF_OPTIONS", "");
4045 19 : char **papszTokens = CSLTokenizeString2(pszGTiffOptions, ",", 0);
4046 28 : for (int i = 0; papszTokens[i] != nullptr; i++)
4047 : {
4048 9 : papszGTiffOptions = CSLAddString(papszGTiffOptions, papszTokens[i]);
4049 : }
4050 19 : CSLDestroy(papszTokens);
4051 :
4052 : // If the user didn't specify any compression and
4053 : // GEOTIFF_AS_REGULAR_EXTERNAL is set (or unspecified), then the
4054 : // GeoTIFF file can be seen as a regular external raw file, provided
4055 : // we make some provision on its organization.
4056 29 : if (CSLFetchNameValue(papszGTiffOptions, "COMPRESS") == nullptr &&
4057 10 : CPLFetchBool(papszOptions, "GEOTIFF_AS_REGULAR_EXTERNAL", true))
4058 : {
4059 9 : bGeoTIFFAsRegularExternal = true;
4060 : papszGTiffOptions =
4061 9 : CSLSetNameValue(papszGTiffOptions, "INTERLEAVE", "BAND");
4062 : // Will make sure that our blocks at nodata are not optimized
4063 : // away but indeed well written
4064 9 : papszGTiffOptions = CSLSetNameValue(
4065 : papszGTiffOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "YES");
4066 9 : if (!bIsTiled && nBandsIn > 1)
4067 : {
4068 : papszGTiffOptions =
4069 1 : CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE", "1");
4070 : }
4071 : }
4072 :
4073 19 : poExternalDS = poDrv->Create(osExternalFilename.c_str(), nXSize, nYSize,
4074 : nBandsIn, eType, papszGTiffOptions);
4075 19 : CSLDestroy(papszGTiffOptions);
4076 19 : if (poExternalDS == nullptr)
4077 : {
4078 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
4079 : osExternalFilename.c_str());
4080 1 : VSIFCloseL(fp);
4081 1 : return nullptr;
4082 : }
4083 : }
4084 :
4085 125 : ISIS3Dataset *poDS = new ISIS3Dataset();
4086 125 : poDS->SetDescription(pszFilename);
4087 125 : poDS->eAccess = GA_Update;
4088 125 : poDS->nRasterXSize = nXSize;
4089 125 : poDS->nRasterYSize = nYSize;
4090 125 : poDS->m_osExternalFilename = std::move(osExternalFilename);
4091 125 : poDS->m_poExternalDS = poExternalDS;
4092 125 : poDS->m_bGeoTIFFAsRegularExternal = bGeoTIFFAsRegularExternal;
4093 125 : if (bGeoTIFFAsRegularExternal)
4094 8 : poDS->m_bGeoTIFFInitDone = false;
4095 125 : poDS->m_fpLabel = fp;
4096 125 : poDS->m_fpImage = fpImage ? fpImage : fp;
4097 125 : poDS->m_bIsLabelWritten = false;
4098 125 : poDS->m_bIsTiled = bIsTiled;
4099 125 : poDS->m_bInitToNodata = (poDS->m_poExternalDS == nullptr);
4100 125 : poDS->m_osComment = CSLFetchNameValueDef(papszOptions, "COMMENT", "");
4101 : poDS->m_osLatitudeType =
4102 125 : CSLFetchNameValueDef(papszOptions, "LATITUDE_TYPE", "");
4103 : poDS->m_osLongitudeDirection =
4104 125 : CSLFetchNameValueDef(papszOptions, "LONGITUDE_DIRECTION", "");
4105 : poDS->m_osTargetName =
4106 125 : CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
4107 125 : poDS->m_bForce360 = CPLFetchBool(papszOptions, "FORCE_360", false);
4108 125 : poDS->m_bWriteBoundingDegrees =
4109 125 : CPLFetchBool(papszOptions, "WRITE_BOUNDING_DEGREES", true);
4110 : poDS->m_osBoundingDegrees =
4111 125 : CSLFetchNameValueDef(papszOptions, "BOUNDING_DEGREES", "");
4112 125 : poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
4113 125 : poDS->m_bUseSrcMapping =
4114 125 : CPLFetchBool(papszOptions, "USE_SRC_MAPPING", false);
4115 125 : poDS->m_bUseSrcHistory =
4116 125 : CPLFetchBool(papszOptions, "USE_SRC_HISTORY", true);
4117 125 : poDS->m_bAddGDALHistory =
4118 125 : CPLFetchBool(papszOptions, "ADD_GDAL_HISTORY", true);
4119 125 : if (poDS->m_bAddGDALHistory)
4120 : {
4121 : poDS->m_osGDALHistory =
4122 123 : CSLFetchNameValueDef(papszOptions, "GDAL_HISTORY", "");
4123 : }
4124 125 : const double dfNoData = (eType == GDT_Byte) ? ISIS3_NULL1
4125 : : (eType == GDT_UInt16) ? ISIS3_NULLU2
4126 : : (eType == GDT_Int16)
4127 : ? ISIS3_NULL2
4128 : :
4129 : /*(eType == GDT_Float32) ?*/ ISIS3_NULL4;
4130 :
4131 291 : for (int i = 0; i < nBandsIn; i++)
4132 : {
4133 166 : GDALRasterBand *poBand = nullptr;
4134 :
4135 166 : if (poDS->m_poExternalDS != nullptr)
4136 : {
4137 : ISIS3WrapperRasterBand *poISISBand = new ISIS3WrapperRasterBand(
4138 24 : poDS->m_poExternalDS->GetRasterBand(i + 1));
4139 24 : poBand = poISISBand;
4140 : }
4141 142 : else if (bIsTiled)
4142 : {
4143 : ISISTiledBand *poISISBand = new ISISTiledBand(
4144 : poDS, poDS->m_fpImage, i + 1, eType, nBlockXSize, nBlockYSize,
4145 : 0, // nSkipBytes, to be hacked
4146 : // afterwards for in-label imagery
4147 10 : 0, 0, CPL_IS_LSB);
4148 :
4149 10 : poBand = poISISBand;
4150 : }
4151 : else
4152 : {
4153 132 : const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
4154 132 : const int nLineOffset = nPixelOffset * nXSize;
4155 132 : const vsi_l_offset nBandOffset =
4156 132 : static_cast<vsi_l_offset>(nLineOffset) * nYSize;
4157 : ISIS3RawRasterBand *poISISBand = new ISIS3RawRasterBand(
4158 : poDS, i + 1, poDS->m_fpImage,
4159 132 : nBandOffset * i, // nImgOffset, to be
4160 : // hacked afterwards for in-label imagery
4161 132 : nPixelOffset, nLineOffset, eType, CPL_IS_LSB);
4162 :
4163 132 : poBand = poISISBand;
4164 : }
4165 166 : poDS->SetBand(i + 1, poBand);
4166 166 : poBand->SetNoDataValue(dfNoData);
4167 : }
4168 :
4169 125 : return poDS;
4170 : }
4171 :
4172 : /************************************************************************/
4173 : /* GetUnderlyingDataset() */
4174 : /************************************************************************/
4175 :
4176 73 : static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
4177 : {
4178 146 : if (poSrcDS->GetDriver() != nullptr &&
4179 73 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
4180 : {
4181 2 : VRTDataset *poVRTDS = reinterpret_cast<VRTDataset *>(poSrcDS);
4182 2 : poSrcDS = poVRTDS->GetSingleSimpleSource();
4183 : }
4184 :
4185 73 : return poSrcDS;
4186 : }
4187 :
4188 : /************************************************************************/
4189 : /* CreateCopy() */
4190 : /************************************************************************/
4191 :
4192 73 : GDALDataset *ISIS3Dataset::CreateCopy(const char *pszFilename,
4193 : GDALDataset *poSrcDS, int /*bStrict*/,
4194 : char **papszOptions,
4195 : GDALProgressFunc pfnProgress,
4196 : void *pProgressData)
4197 : {
4198 : const char *pszDataLocation =
4199 73 : CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
4200 73 : GDALDataset *poSrcUnderlyingDS = GetUnderlyingDataset(poSrcDS);
4201 73 : if (poSrcUnderlyingDS == nullptr)
4202 2 : poSrcUnderlyingDS = poSrcDS;
4203 83 : if (EQUAL(pszDataLocation, "GEOTIFF") &&
4204 10 : strcmp(poSrcUnderlyingDS->GetDescription(),
4205 : CSLFetchNameValueDef(
4206 : papszOptions, "EXTERNAL_FILENAME",
4207 83 : CPLResetExtensionSafe(pszFilename, "tif").c_str())) == 0)
4208 : {
4209 1 : CPLError(CE_Failure, CPLE_NotSupported,
4210 : "Output file has same name as input file");
4211 1 : return nullptr;
4212 : }
4213 72 : if (poSrcDS->GetRasterCount() == 0)
4214 : {
4215 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
4216 1 : return nullptr;
4217 : }
4218 :
4219 71 : const int nXSize = poSrcDS->GetRasterXSize();
4220 71 : const int nYSize = poSrcDS->GetRasterYSize();
4221 71 : const int nBands = poSrcDS->GetRasterCount();
4222 71 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
4223 : ISIS3Dataset *poDS = reinterpret_cast<ISIS3Dataset *>(
4224 71 : Create(pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
4225 71 : if (poDS == nullptr)
4226 10 : return nullptr;
4227 61 : poDS->m_osFromFilename = poSrcUnderlyingDS->GetDescription();
4228 :
4229 61 : double adfGeoTransform[6] = {0.0};
4230 104 : if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None &&
4231 43 : (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 ||
4232 6 : adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 ||
4233 6 : adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0))
4234 : {
4235 37 : poDS->SetGeoTransform(adfGeoTransform);
4236 : }
4237 :
4238 61 : auto poSrcSRS = poSrcDS->GetSpatialRef();
4239 61 : if (poSrcSRS)
4240 : {
4241 37 : poDS->SetSpatialRef(poSrcSRS);
4242 : }
4243 :
4244 147 : for (int i = 1; i <= nBands; i++)
4245 : {
4246 86 : const double dfOffset = poSrcDS->GetRasterBand(i)->GetOffset();
4247 86 : if (dfOffset != 0.0)
4248 1 : poDS->GetRasterBand(i)->SetOffset(dfOffset);
4249 :
4250 86 : const double dfScale = poSrcDS->GetRasterBand(i)->GetScale();
4251 86 : if (dfScale != 1.0)
4252 1 : poDS->GetRasterBand(i)->SetScale(dfScale);
4253 : }
4254 :
4255 : // Do we need to remap nodata ?
4256 61 : int bHasNoData = FALSE;
4257 61 : poDS->m_dfSrcNoData =
4258 61 : poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHasNoData);
4259 61 : poDS->m_bHasSrcNoData = CPL_TO_BOOL(bHasNoData);
4260 :
4261 61 : if (poDS->m_bUseSrcLabel)
4262 : {
4263 53 : char **papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
4264 53 : if (papszMD_ISIS3 != nullptr)
4265 : {
4266 18 : poDS->SetMetadata(papszMD_ISIS3, "json:ISIS3");
4267 : }
4268 : }
4269 :
4270 : // We don't need to initialize the imagery as we are going to copy it
4271 : // completely
4272 61 : poDS->m_bInitToNodata = false;
4273 61 : CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS, nullptr,
4274 : pfnProgress, pProgressData);
4275 61 : poDS->FlushCache(false);
4276 61 : poDS->m_bHasSrcNoData = false;
4277 61 : if (eErr != CE_None)
4278 : {
4279 11 : delete poDS;
4280 11 : return nullptr;
4281 : }
4282 :
4283 50 : return poDS;
4284 : }
4285 :
4286 : /************************************************************************/
4287 : /* GDALRegister_ISIS3() */
4288 : /************************************************************************/
4289 :
4290 1911 : void GDALRegister_ISIS3()
4291 :
4292 : {
4293 1911 : if (GDALGetDriverByName(ISIS3_DRIVER_NAME) != nullptr)
4294 282 : return;
4295 :
4296 1629 : GDALDriver *poDriver = new GDALDriver();
4297 1629 : ISIS3DriverSetCommonMetadata(poDriver);
4298 :
4299 1629 : poDriver->pfnOpen = ISIS3Dataset::Open;
4300 1629 : poDriver->pfnCreate = ISIS3Dataset::Create;
4301 1629 : poDriver->pfnCreateCopy = ISIS3Dataset::CreateCopy;
4302 :
4303 1629 : GetGDALDriverManager()->RegisterDriver(poDriver);
4304 : }
|