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