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