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.xscale <= 0.0 || gt.xscale != -gt.yscale || gt.xrot != 0.0 ||
1414 56 : gt.yrot != 0.0)
1415 : {
1416 0 : CPLError(CE_Failure, CPLE_NotSupported,
1417 : "Only north-up geotransform with square pixels supported");
1418 0 : return CE_Failure;
1419 : }
1420 56 : m_bGotTransform = true;
1421 56 : m_gt = gt;
1422 56 : if (m_poExternalDS)
1423 6 : m_poExternalDS->SetGeoTransform(m_gt);
1424 56 : InvalidateLabel();
1425 56 : return CE_None;
1426 : }
1427 :
1428 : /************************************************************************/
1429 : /* GetMetadataDomainList() */
1430 : /************************************************************************/
1431 :
1432 1 : char **ISIS3Dataset::GetMetadataDomainList()
1433 : {
1434 1 : return BuildMetadataDomainList(nullptr, FALSE, "", "json:ISIS3", nullptr);
1435 : }
1436 :
1437 : /************************************************************************/
1438 : /* GetMetadata() */
1439 : /************************************************************************/
1440 :
1441 127 : CSLConstList ISIS3Dataset::GetMetadata(const char *pszDomain)
1442 : {
1443 127 : if (pszDomain != nullptr && EQUAL(pszDomain, "json:ISIS3"))
1444 : {
1445 65 : if (m_aosISIS3MD.empty())
1446 : {
1447 59 : if (eAccess == GA_Update && !m_oJSonLabel.IsValid())
1448 : {
1449 1 : BuildLabel();
1450 : }
1451 59 : CPLAssert(m_oJSonLabel.IsValid());
1452 59 : if (m_bResolveOfflineContent && !m_bHasResolvedOfflineContent)
1453 : {
1454 58 : ResolveOfflineContentOfLabel();
1455 : }
1456 : const CPLString osJson =
1457 118 : m_oJSonLabel.Format(CPLJSONObject::PrettyFormat::Pretty);
1458 59 : m_aosISIS3MD.InsertString(0, osJson.c_str());
1459 : }
1460 65 : return m_aosISIS3MD.List();
1461 : }
1462 62 : return GDALPamDataset::GetMetadata(pszDomain);
1463 : }
1464 :
1465 : /************************************************************************/
1466 : /* ResolveOfflineContentOfLabel() */
1467 : /************************************************************************/
1468 :
1469 58 : void ISIS3Dataset::ResolveOfflineContentOfLabel()
1470 : {
1471 58 : m_bHasResolvedOfflineContent = true;
1472 :
1473 116 : std::vector<GByte> abyData;
1474 :
1475 293 : for (CPLJSONObject &oObj : m_oJSonLabel.GetChildren())
1476 : {
1477 235 : if (oObj.GetType() == CPLJSONObject::Type::Object)
1478 : {
1479 178 : CPLString osContainerName = oObj.GetName();
1480 356 : CPLJSONObject oContainerName = oObj.GetObj("_container_name");
1481 178 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
1482 : {
1483 72 : osContainerName = oContainerName.ToString();
1484 : }
1485 :
1486 178 : std::string osFilename;
1487 178 : CPLJSONObject oFilename = oObj.GetObj("^" + osContainerName);
1488 178 : if (oFilename.GetType() == CPLJSONObject::Type::String)
1489 : {
1490 : VSIStatBufL sStat;
1491 108 : osFilename = CPLFormFilenameSafe(
1492 72 : CPLGetPathSafe(GetDescription()).c_str(),
1493 108 : oFilename.ToString().c_str(), nullptr);
1494 36 : if (CPLHasPathTraversal(oFilename.ToString().c_str()))
1495 : {
1496 0 : CPLError(CE_Warning, CPLE_AppDefined,
1497 : "Path traversal detected for ^%s: %s",
1498 : osContainerName.c_str(),
1499 0 : oFilename.ToString().c_str());
1500 17 : continue;
1501 : }
1502 36 : else if (VSIStatL(osFilename.c_str(), &sStat) != 0)
1503 : {
1504 17 : CPLDebug("ISIS3", "File %s referenced but not foud",
1505 : osFilename.c_str());
1506 17 : continue;
1507 : }
1508 : }
1509 : else
1510 : {
1511 142 : osFilename = GetDescription();
1512 : }
1513 :
1514 322 : CPLJSONObject oBytes = oObj.GetObj("Bytes");
1515 161 : if (oBytes.GetType() == CPLJSONObject::Type::Long)
1516 : {
1517 0 : CPLError(CE_Warning, CPLE_AppDefined,
1518 : "Too large content reference by %s to be captured in "
1519 : "json:ISIS3 metadata domain",
1520 0 : oObj.GetName().c_str());
1521 0 : continue;
1522 : }
1523 256 : else if (oBytes.GetType() != CPLJSONObject::Type::Integer ||
1524 95 : oBytes.ToInteger() <= 0)
1525 : {
1526 66 : continue;
1527 : }
1528 95 : if (oBytes.ToInteger() > m_nMaxOfflineContentSize)
1529 : {
1530 1 : CPLError(CE_Warning, CPLE_AppDefined,
1531 : "Too large content reference by %s to be captured in "
1532 : "json:ISIS3 metadata domain",
1533 2 : oObj.GetName().c_str());
1534 1 : continue;
1535 : }
1536 :
1537 188 : CPLJSONObject oStartByte = oObj.GetObj("StartByte");
1538 94 : if ((oStartByte.GetType() != CPLJSONObject::Type::Integer &&
1539 143 : oStartByte.GetType() != CPLJSONObject::Type::Long) ||
1540 49 : oStartByte.ToLong() <= 0)
1541 : {
1542 45 : continue;
1543 : }
1544 :
1545 : // 1-based offsets
1546 : const auto nOffset =
1547 49 : static_cast<vsi_l_offset>(oStartByte.ToLong() - 1);
1548 :
1549 49 : const auto nSize = static_cast<size_t>(oBytes.ToInteger());
1550 : try
1551 : {
1552 49 : abyData.resize(nSize);
1553 : }
1554 0 : catch (const std::exception &)
1555 : {
1556 0 : CPLError(CE_Warning, CPLE_AppDefined,
1557 : "Out of memory: too large content referenced by %s "
1558 : "to be captured in json:ISIS3 metadata domain",
1559 0 : oObj.GetName().c_str());
1560 0 : continue;
1561 : }
1562 :
1563 49 : VSIVirtualHandleUniquePtr fp(VSIFOpenL(osFilename.c_str(), "rb"));
1564 49 : if (!fp)
1565 : {
1566 0 : CPLError(CE_Warning, CPLE_FileIO,
1567 : "Cannot open %s referenced by %s", osFilename.c_str(),
1568 0 : oObj.GetName().c_str());
1569 0 : continue;
1570 : }
1571 :
1572 98 : if (fp->Seek(nOffset, SEEK_SET) != 0 ||
1573 49 : fp->Read(abyData.data(), abyData.size(), 1) != 1)
1574 : {
1575 0 : CPLError(CE_Warning, CPLE_FileIO,
1576 : "Cannot read %u bytes at offset %" PRIu64
1577 : " in %s, referenced by %s",
1578 0 : static_cast<unsigned>(abyData.size()),
1579 : static_cast<uint64_t>(nOffset), osFilename.c_str(),
1580 0 : oObj.GetName().c_str());
1581 0 : continue;
1582 : }
1583 :
1584 98 : CPLJSONObject oData;
1585 49 : char *pszHex = CPLBinaryToHex(static_cast<int>(abyData.size()),
1586 49 : abyData.data());
1587 49 : oObj.Add("_data", pszHex);
1588 49 : CPLFree(pszHex);
1589 : }
1590 : }
1591 58 : }
1592 :
1593 : /************************************************************************/
1594 : /* InvalidateLabel() */
1595 : /************************************************************************/
1596 :
1597 148 : void ISIS3Dataset::InvalidateLabel()
1598 : {
1599 148 : m_oJSonLabel.Deinit();
1600 148 : m_aosISIS3MD.Clear();
1601 148 : m_bHasResolvedOfflineContent = false;
1602 148 : }
1603 :
1604 : /************************************************************************/
1605 : /* SetMetadata() */
1606 : /************************************************************************/
1607 :
1608 28 : CPLErr ISIS3Dataset::SetMetadata(CSLConstList papszMD, const char *pszDomain)
1609 : {
1610 28 : if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr &&
1611 28 : EQUAL(pszDomain, "json:ISIS3"))
1612 : {
1613 28 : m_oSrcJSonLabel.Deinit();
1614 28 : InvalidateLabel();
1615 28 : if (papszMD != nullptr && papszMD[0] != nullptr)
1616 : {
1617 28 : CPLJSONDocument oJSONDocument;
1618 28 : const GByte *pabyData = reinterpret_cast<const GByte *>(papszMD[0]);
1619 28 : if (!oJSONDocument.LoadMemory(pabyData))
1620 : {
1621 1 : return CE_Failure;
1622 : }
1623 :
1624 27 : m_oSrcJSonLabel = oJSONDocument.GetRoot();
1625 27 : if (!m_oSrcJSonLabel.IsValid())
1626 : {
1627 0 : return CE_Failure;
1628 : }
1629 : }
1630 27 : return CE_None;
1631 : }
1632 0 : return GDALPamDataset::SetMetadata(papszMD, pszDomain);
1633 : }
1634 :
1635 : /************************************************************************/
1636 : /* GetRawBinaryLayout() */
1637 : /************************************************************************/
1638 :
1639 2 : bool ISIS3Dataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1640 : {
1641 2 : if (m_sLayout.osRawFilename.empty())
1642 0 : return false;
1643 2 : sLayout = m_sLayout;
1644 2 : return true;
1645 : }
1646 :
1647 : /************************************************************************/
1648 : /* GetValueAndUnits() */
1649 : /************************************************************************/
1650 :
1651 50 : static void GetValueAndUnits(const CPLJSONObject &obj,
1652 : std::vector<double> &adfValues,
1653 : std::vector<std::string> &aosUnits,
1654 : int nExpectedVals)
1655 : {
1656 100 : if (obj.GetType() == CPLJSONObject::Type::Integer ||
1657 50 : obj.GetType() == CPLJSONObject::Type::Double)
1658 : {
1659 32 : adfValues.push_back(obj.ToDouble());
1660 : }
1661 18 : else if (obj.GetType() == CPLJSONObject::Type::Object)
1662 : {
1663 24 : auto oValue = obj.GetObj("value");
1664 24 : auto oUnit = obj.GetObj("unit");
1665 8 : if (oValue.IsValid() &&
1666 8 : (oValue.GetType() == CPLJSONObject::Type::Integer ||
1667 2 : oValue.GetType() == CPLJSONObject::Type::Double ||
1668 2 : oValue.GetType() == CPLJSONObject::Type::Array) &&
1669 16 : oUnit.IsValid() && oUnit.GetType() == CPLJSONObject::Type::String)
1670 : {
1671 8 : if (oValue.GetType() == CPLJSONObject::Type::Array)
1672 : {
1673 2 : GetValueAndUnits(oValue, adfValues, aosUnits, nExpectedVals);
1674 : }
1675 : else
1676 : {
1677 6 : adfValues.push_back(oValue.ToDouble());
1678 : }
1679 8 : aosUnits.push_back(oUnit.ToString());
1680 : }
1681 : }
1682 10 : else if (obj.GetType() == CPLJSONObject::Type::Array)
1683 : {
1684 10 : auto oArray = obj.ToArray();
1685 10 : if (oArray.Size() == nExpectedVals)
1686 : {
1687 34 : for (int i = 0; i < nExpectedVals; i++)
1688 : {
1689 60 : if (oArray[i].GetType() == CPLJSONObject::Type::Integer ||
1690 36 : oArray[i].GetType() == CPLJSONObject::Type::Double)
1691 : {
1692 24 : adfValues.push_back(oArray[i].ToDouble());
1693 : }
1694 : else
1695 : {
1696 0 : adfValues.clear();
1697 0 : return;
1698 : }
1699 : }
1700 : }
1701 : }
1702 : }
1703 :
1704 : /************************************************************************/
1705 : /* Open() */
1706 : /************************************************************************/
1707 :
1708 265 : GDALDataset *ISIS3Dataset::Open(GDALOpenInfo *poOpenInfo)
1709 :
1710 : {
1711 : /* -------------------------------------------------------------------- */
1712 : /* Does this look like a CUBE dataset? */
1713 : /* -------------------------------------------------------------------- */
1714 265 : if (!ISIS3DriverIdentify(poOpenInfo))
1715 0 : return nullptr;
1716 :
1717 : /* -------------------------------------------------------------------- */
1718 : /* Open the file using the large file API. */
1719 : /* -------------------------------------------------------------------- */
1720 530 : auto poDS = std::make_unique<ISIS3Dataset>();
1721 :
1722 265 : if (!poDS->m_oKeywords.Ingest(poOpenInfo->fpL, 0))
1723 : {
1724 1 : VSIFCloseL(poOpenInfo->fpL);
1725 1 : poOpenInfo->fpL = nullptr;
1726 1 : return nullptr;
1727 : }
1728 :
1729 264 : poDS->m_bResolveOfflineContent = CPLTestBool(CSLFetchNameValueDef(
1730 264 : poOpenInfo->papszOpenOptions, "INCLUDE_OFFLINE_CONTENT", "YES"));
1731 264 : poDS->m_nMaxOfflineContentSize = std::max(
1732 264 : 0, atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
1733 264 : "MAX_SIZE_OFFLINE_CONTENT", "100000000")));
1734 :
1735 264 : poDS->m_oJSonLabel = poDS->m_oKeywords.GetJsonObject();
1736 264 : poDS->m_oJSonLabel.Add("_filename", poOpenInfo->pszFilename);
1737 :
1738 : // Find additional files from the label
1739 1317 : for (const CPLJSONObject &oObj : poDS->m_oJSonLabel.GetChildren())
1740 : {
1741 1053 : if (oObj.GetType() == CPLJSONObject::Type::Object)
1742 : {
1743 1578 : CPLString osContainerName = oObj.GetName();
1744 2367 : CPLJSONObject oContainerName = oObj.GetObj("_container_name");
1745 789 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
1746 : {
1747 286 : osContainerName = oContainerName.ToString();
1748 : }
1749 :
1750 1578 : CPLJSONObject oFilename = oObj.GetObj("^" + osContainerName);
1751 789 : if (oFilename.GetType() == CPLJSONObject::Type::String)
1752 : {
1753 : VSIStatBufL sStat;
1754 369 : const CPLString osFilename(CPLFormFilenameSafe(
1755 246 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
1756 492 : oFilename.ToString().c_str(), nullptr));
1757 123 : if (CPLHasPathTraversal(oFilename.ToString().c_str()))
1758 : {
1759 0 : CPLError(CE_Warning, CPLE_AppDefined,
1760 : "Path traversal detected for ^%s: %s",
1761 : osContainerName.c_str(),
1762 0 : oFilename.ToString().c_str());
1763 : }
1764 123 : else if (VSIStatL(osFilename, &sStat) == 0)
1765 : {
1766 81 : poDS->m_aosAdditionalFiles.AddString(osFilename);
1767 : }
1768 : else
1769 : {
1770 42 : CPLDebug("ISIS3", "File %s referenced but not foud",
1771 : osFilename.c_str());
1772 : }
1773 : }
1774 : }
1775 : }
1776 :
1777 264 : VSIFCloseL(poOpenInfo->fpL);
1778 264 : poOpenInfo->fpL = nullptr;
1779 :
1780 : /* -------------------------------------------------------------------- */
1781 : /* Assume user is pointing to label (i.e. .lbl) file for detached option */
1782 : /* -------------------------------------------------------------------- */
1783 : // Image can be inline or detached and point to an image name
1784 : // the Format can be Tiled or Raw
1785 : // Object = Core
1786 : // StartByte = 65537
1787 : // Format = Tile
1788 : // TileSamples = 128
1789 : // TileLines = 128
1790 : // OR-----
1791 : // Object = Core
1792 : // StartByte = 1
1793 : // ^Core = r0200357_detatched.cub
1794 : // Format = BandSequential
1795 : // OR-----
1796 : // Object = Core
1797 : // StartByte = 1
1798 : // ^Core = r0200357_detached_tiled.cub
1799 : // Format = Tile
1800 : // TileSamples = 128
1801 : // TileLines = 128
1802 : // OR-----
1803 : // Object = Core
1804 : // StartByte = 1
1805 : // ^Core = some.tif
1806 : // Format = GeoTIFF
1807 :
1808 : /* -------------------------------------------------------------------- */
1809 : /* What file contains the actual data? */
1810 : /* -------------------------------------------------------------------- */
1811 264 : const char *pszCore = poDS->GetKeyword("IsisCube.Core.^Core");
1812 : CPLString osQubeFile(
1813 264 : EQUAL(pszCore, "")
1814 357 : ? CPLString(poOpenInfo->pszFilename)
1815 : : CPLFormFilenameSafe(
1816 450 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str(), pszCore,
1817 528 : nullptr));
1818 264 : if (CPLHasPathTraversal(pszCore))
1819 : {
1820 0 : CPLError(CE_Failure, CPLE_NotSupported,
1821 : "Path traversal detected in IsisCube.Core.^Core: %s", pszCore);
1822 0 : return nullptr;
1823 : }
1824 264 : if (!EQUAL(pszCore, ""))
1825 : {
1826 93 : poDS->m_osExternalFilename = osQubeFile;
1827 : }
1828 :
1829 : /* -------------------------------------------------------------------- */
1830 : /* Check if file an ISIS3 header file? Read a few lines of text */
1831 : /* searching for something starting with nrows or ncols. */
1832 : /* -------------------------------------------------------------------- */
1833 :
1834 : /************* Skipbytes *****************************/
1835 264 : int nSkipBytes = atoi(poDS->GetKeyword("IsisCube.Core.StartByte", "1"));
1836 264 : if (nSkipBytes <= 1)
1837 95 : nSkipBytes = 0;
1838 : else
1839 169 : nSkipBytes -= 1;
1840 :
1841 : /******* Grab format type (BandSequential, Tiled) *******/
1842 528 : CPLString osFormat = poDS->GetKeyword("IsisCube.Core.Format");
1843 :
1844 264 : int tileSizeX = 0;
1845 264 : int tileSizeY = 0;
1846 :
1847 264 : if (EQUAL(osFormat, "Tile"))
1848 : {
1849 26 : poDS->m_bIsTiled = true;
1850 : /******* Get Tile Sizes *********/
1851 26 : tileSizeX = atoi(poDS->GetKeyword("IsisCube.Core.TileSamples"));
1852 26 : tileSizeY = atoi(poDS->GetKeyword("IsisCube.Core.TileLines"));
1853 26 : if (tileSizeX <= 0 || tileSizeY <= 0)
1854 : {
1855 1 : CPLError(CE_Failure, CPLE_OpenFailed,
1856 : "Wrong tile dimensions : %d x %d", tileSizeX, tileSizeY);
1857 1 : return nullptr;
1858 : }
1859 : }
1860 238 : else if (!EQUAL(osFormat, "BandSequential") && !EQUAL(osFormat, "GeoTIFF"))
1861 : {
1862 1 : CPLError(CE_Failure, CPLE_OpenFailed, "%s format not supported.",
1863 : osFormat.c_str());
1864 1 : return nullptr;
1865 : }
1866 :
1867 : /*********** Grab samples lines band ************/
1868 : const int nCols =
1869 262 : atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Samples"));
1870 262 : const int nRows = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Lines"));
1871 262 : const int nBands = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Bands"));
1872 :
1873 : /****** Grab format type - ISIS3 only supports 8,U16,S16,32 *****/
1874 262 : GDALDataType eDataType = GDT_UInt8;
1875 262 : double dfNoData = 0.0;
1876 :
1877 262 : const char *itype = poDS->GetKeyword("IsisCube.Core.Pixels.Type");
1878 262 : if (EQUAL(itype, "UnsignedByte"))
1879 : {
1880 211 : eDataType = GDT_UInt8;
1881 211 : dfNoData = ISIS3_NULL1;
1882 : }
1883 51 : else if (EQUAL(itype, "UnsignedWord"))
1884 : {
1885 17 : eDataType = GDT_UInt16;
1886 17 : dfNoData = ISIS3_NULLU2;
1887 : }
1888 34 : else if (EQUAL(itype, "SignedWord"))
1889 : {
1890 14 : eDataType = GDT_Int16;
1891 14 : dfNoData = ISIS3_NULL2;
1892 : }
1893 20 : else if (EQUAL(itype, "Real") || EQUAL(itype, ""))
1894 : {
1895 19 : eDataType = GDT_Float32;
1896 19 : dfNoData = ISIS3_NULL4;
1897 : }
1898 : else
1899 : {
1900 1 : CPLError(CE_Failure, CPLE_OpenFailed, "%s pixel type not supported.",
1901 : itype);
1902 1 : return nullptr;
1903 : }
1904 :
1905 : /*********** Grab samples lines band ************/
1906 :
1907 : // default to MSB
1908 : const bool bIsLSB =
1909 261 : EQUAL(poDS->GetKeyword("IsisCube.Core.Pixels.ByteOrder"), "Lsb");
1910 :
1911 : /*********** Grab Cellsize ************/
1912 261 : double dfXDim = 1.0;
1913 261 : double dfYDim = 1.0;
1914 :
1915 261 : const char *pszRes = poDS->GetKeyword("IsisCube.Mapping.PixelResolution");
1916 261 : if (strlen(pszRes) > 0)
1917 : {
1918 102 : dfXDim = CPLAtof(pszRes); /* values are in meters */
1919 102 : dfYDim = -CPLAtof(pszRes);
1920 : }
1921 :
1922 : /*********** Grab UpperLeftCornerY ************/
1923 261 : double dfULYMap = 0.5;
1924 :
1925 261 : const char *pszULY = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerY");
1926 261 : if (strlen(pszULY) > 0)
1927 : {
1928 102 : dfULYMap = CPLAtof(pszULY);
1929 : }
1930 :
1931 : /*********** Grab UpperLeftCornerX ************/
1932 261 : double dfULXMap = 0.5;
1933 :
1934 261 : const char *pszULX = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerX");
1935 261 : if (strlen(pszULX) > 0)
1936 : {
1937 102 : dfULXMap = CPLAtof(pszULX);
1938 : }
1939 :
1940 : /*********** Grab TARGET_NAME ************/
1941 : /**** This is the planets name i.e. Mars ***/
1942 261 : const char *target_name = poDS->GetKeyword("IsisCube.Mapping.TargetName");
1943 :
1944 : #ifdef notdef
1945 : const double dfLongitudeMulFactor =
1946 : EQUAL(poDS->GetKeyword("IsisCube.Mapping.LongitudeDirection",
1947 : "PositiveEast"),
1948 : "PositiveEast")
1949 : ? 1
1950 : : -1;
1951 : #else
1952 261 : const double dfLongitudeMulFactor = 1;
1953 : #endif
1954 :
1955 : /*********** Grab MAP_PROJECTION_TYPE ************/
1956 : const char *map_proj_name =
1957 261 : poDS->GetKeyword("IsisCube.Mapping.ProjectionName");
1958 :
1959 : /*********** Grab SEMI-MAJOR ************/
1960 : const double semi_major =
1961 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.EquatorialRadius"));
1962 :
1963 : /*********** Grab semi-minor ************/
1964 : const double semi_minor =
1965 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PolarRadius"));
1966 :
1967 : /*********** Grab CENTER_LAT ************/
1968 : const double center_lat =
1969 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.CenterLatitude"));
1970 :
1971 : /*********** Grab CENTER_LON ************/
1972 : const double center_lon =
1973 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.CenterLongitude")) *
1974 : dfLongitudeMulFactor;
1975 :
1976 : /*********** Grab 1st std parallel ************/
1977 : const double first_std_parallel =
1978 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.FirstStandardParallel"));
1979 :
1980 : /*********** Grab 2nd std parallel ************/
1981 : const double second_std_parallel =
1982 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.SecondStandardParallel"));
1983 :
1984 : /*********** Grab scaleFactor ************/
1985 : const double scaleFactor =
1986 261 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.scaleFactor", "1.0"));
1987 :
1988 : /*** grab LatitudeType = Planetographic ****/
1989 : // Need to further study how ocentric/ographic will effect the gdal library
1990 : // So far we will use this fact to define a sphere or ellipse for some
1991 : // projections
1992 :
1993 : // Frank - may need to talk this over
1994 261 : bool bIsGeographic = true;
1995 261 : if (EQUAL(poDS->GetKeyword("IsisCube.Mapping.LatitudeType"),
1996 : "Planetocentric"))
1997 92 : bIsGeographic = false;
1998 :
1999 : // Set oSRS projection and parameters
2000 : // ############################################################
2001 : // ISIS3 Projection types
2002 : // Equirectangular
2003 : // LambertConformal
2004 : // Mercator
2005 : // ObliqueCylindrical
2006 : // Orthographic
2007 : // PolarStereographic
2008 : // SimpleCylindrical
2009 : // Sinusoidal
2010 : // TransverseMercator
2011 :
2012 : #ifdef DEBUG
2013 261 : CPLDebug("ISIS3", "using projection %s", map_proj_name);
2014 : #endif
2015 :
2016 522 : OGRSpatialReference oSRS;
2017 261 : bool bProjectionSet = true;
2018 :
2019 261 : if ((EQUAL(map_proj_name, "Equirectangular")) ||
2020 194 : (EQUAL(map_proj_name, "SimpleCylindrical")))
2021 : {
2022 82 : oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
2023 : }
2024 179 : else if (EQUAL(map_proj_name, "Orthographic"))
2025 : {
2026 2 : oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
2027 : }
2028 177 : else if (EQUAL(map_proj_name, "Sinusoidal"))
2029 : {
2030 2 : oSRS.SetSinusoidal(center_lon, 0, 0);
2031 : }
2032 175 : else if (EQUAL(map_proj_name, "Mercator"))
2033 : {
2034 2 : oSRS.SetMercator(center_lat, center_lon, scaleFactor, 0, 0);
2035 : }
2036 173 : else if (EQUAL(map_proj_name, "PolarStereographic"))
2037 : {
2038 2 : oSRS.SetPS(center_lat, center_lon, scaleFactor, 0, 0);
2039 : }
2040 171 : else if (EQUAL(map_proj_name, "TransverseMercator"))
2041 : {
2042 21 : oSRS.SetTM(center_lat, center_lon, scaleFactor, 0, 0);
2043 : }
2044 150 : else if (EQUAL(map_proj_name, "LambertConformal"))
2045 : {
2046 2 : oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
2047 : center_lon, 0, 0);
2048 : }
2049 148 : else if (EQUAL(map_proj_name, "PointPerspective"))
2050 : {
2051 : // Distance parameter is the distance to the center of the body, and is
2052 : // given in km
2053 : const double distance =
2054 1 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.Distance")) * 1000.0;
2055 1 : const double height_above_ground = distance - semi_major;
2056 1 : oSRS.SetVerticalPerspective(center_lat, center_lon, 0,
2057 : height_above_ground, 0, 0);
2058 : }
2059 147 : else if (EQUAL(map_proj_name, "ObliqueCylindrical"))
2060 : {
2061 : const double poleLatitude =
2062 4 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleLatitude"));
2063 : const double poleLongitude =
2064 4 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleLongitude")) *
2065 : dfLongitudeMulFactor;
2066 : const double poleRotation =
2067 4 : CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleRotation"));
2068 8 : CPLString oProj4String;
2069 : // ISIS3 rotated pole doesn't use the same conventions than PROJ ob_tran
2070 : // Compare the sign difference in
2071 : // https://github.com/USGS-Astrogeology/ISIS3/blob/3.8.0/isis/src/base/objs/ObliqueCylindrical/ObliqueCylindrical.cpp#L244
2072 : // and
2073 : // https://github.com/OSGeo/PROJ/blob/6.2/src/projections/ob_tran.cpp#L34
2074 : // They can be compensated by modifying the poleLatitude to
2075 : // 180-poleLatitude There's also a sign difference for the poleRotation
2076 : // parameter The existence of those different conventions is
2077 : // acknowledged in
2078 : // https://pds-imaging.jpl.nasa.gov/documentation/Cassini_BIDRSIS.PDF in
2079 : // the middle of page 10
2080 : oProj4String.Printf("+proj=ob_tran +o_proj=eqc +o_lon_p=%.17g "
2081 : "+o_lat_p=%.17g +lon_0=%.17g",
2082 4 : -poleRotation, 180 - poleLatitude, poleLongitude);
2083 4 : oSRS.SetFromUserInput(oProj4String);
2084 : }
2085 : else
2086 : {
2087 143 : CPLDebug("ISIS3",
2088 : "Dataset projection %s is not supported. Continuing...",
2089 : map_proj_name);
2090 143 : bProjectionSet = false;
2091 : }
2092 :
2093 261 : if (bProjectionSet)
2094 : {
2095 : // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
2096 236 : CPLString osProjTargetName(map_proj_name);
2097 118 : osProjTargetName += " ";
2098 118 : osProjTargetName += target_name;
2099 118 : oSRS.SetProjCS(osProjTargetName); // set ProjCS keyword
2100 :
2101 : // The geographic/geocentric name will be the same basic name as the
2102 : // body name 'GCS' = Geographic/Geocentric Coordinate System
2103 236 : CPLString osGeogName("GCS_");
2104 118 : osGeogName += target_name;
2105 :
2106 : // The datum name will be the same basic name as the planet
2107 236 : CPLString osDatumName("D_");
2108 118 : osDatumName += target_name;
2109 :
2110 236 : CPLString osSphereName(target_name);
2111 : // strcat(osSphereName, "_IAU_IAG"); //Might not be IAU defined so
2112 : // don't add
2113 :
2114 : // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
2115 118 : double iflattening = 0.0;
2116 118 : if ((semi_major - semi_minor) < 0.0000001)
2117 38 : iflattening = 0;
2118 : else
2119 80 : iflattening = semi_major / (semi_major - semi_minor);
2120 :
2121 : // Set the body size but take into consideration which proj is being
2122 : // used to help w/ proj4 compatibility The use of a Sphere, polar radius
2123 : // or ellipse here is based on how ISIS does it internally
2124 118 : if (((EQUAL(map_proj_name, "Stereographic") &&
2125 0 : (fabs(center_lat) == 90))) ||
2126 118 : (EQUAL(map_proj_name, "PolarStereographic")))
2127 : {
2128 2 : if (bIsGeographic)
2129 : {
2130 : // Geograpraphic, so set an ellipse
2131 0 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2132 : semi_major, iflattening, "Reference_Meridian",
2133 : 0.0);
2134 : }
2135 : else
2136 : {
2137 : // Geocentric, so force a sphere using the semi-minor axis. I
2138 : // hope...
2139 2 : osSphereName += "_polarRadius";
2140 2 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2141 : semi_minor, 0.0, "Reference_Meridian", 0.0);
2142 : }
2143 : }
2144 116 : else if ((EQUAL(map_proj_name, "SimpleCylindrical")) ||
2145 101 : (EQUAL(map_proj_name, "Orthographic")) ||
2146 99 : (EQUAL(map_proj_name, "Stereographic")) ||
2147 99 : (EQUAL(map_proj_name, "Sinusoidal")) ||
2148 97 : (EQUAL(map_proj_name, "PointPerspective")))
2149 : {
2150 : // ISIS uses the spherical equation for these projections
2151 : // so force a sphere.
2152 20 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, semi_major,
2153 : 0.0, "Reference_Meridian", 0.0);
2154 : }
2155 96 : else if (EQUAL(map_proj_name, "Equirectangular"))
2156 : {
2157 : // Calculate localRadius using ISIS3 simple elliptical method
2158 : // not the more standard Radius of Curvature method
2159 : // PI = 4 * atan(1);
2160 67 : const double radLat = center_lat * M_PI / 180; // in radians
2161 67 : const double meanRadius = sqrt(pow(semi_minor * cos(radLat), 2) +
2162 67 : pow(semi_major * sin(radLat), 2));
2163 67 : const double localRadius =
2164 67 : (meanRadius == 0.0) ? 0.0
2165 67 : : semi_major * semi_minor / meanRadius;
2166 67 : osSphereName += "_localRadius";
2167 67 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, localRadius,
2168 : 0.0, "Reference_Meridian", 0.0);
2169 : }
2170 : else
2171 : {
2172 : // All other projections: Mercator, Transverse Mercator, Lambert
2173 : // Conformal, etc. Geographic, so set an ellipse
2174 29 : if (bIsGeographic)
2175 : {
2176 0 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2177 : semi_major, iflattening, "Reference_Meridian",
2178 : 0.0);
2179 : }
2180 : else
2181 : {
2182 : // Geocentric, so force a sphere. I hope...
2183 29 : oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2184 : semi_major, 0.0, "Reference_Meridian", 0.0);
2185 : }
2186 : }
2187 :
2188 : // translate back into a projection string.
2189 118 : poDS->m_oSRS = std::move(oSRS);
2190 118 : poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2191 : }
2192 :
2193 : /* END ISIS3 Label Read */
2194 : /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
2195 :
2196 : /* -------------------------------------------------------------------- */
2197 : /* Did we get the required keywords? If not we return with */
2198 : /* this never having been considered to be a match. This isn't */
2199 : /* an error! */
2200 : /* -------------------------------------------------------------------- */
2201 521 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
2202 260 : !GDALCheckBandCount(nBands, false))
2203 : {
2204 2 : return nullptr;
2205 : }
2206 :
2207 : /* -------------------------------------------------------------------- */
2208 : /* Capture some information from the file that is of interest. */
2209 : /* -------------------------------------------------------------------- */
2210 259 : poDS->nRasterXSize = nCols;
2211 259 : poDS->nRasterYSize = nRows;
2212 :
2213 : /* -------------------------------------------------------------------- */
2214 : /* Open target binary file. */
2215 : /* -------------------------------------------------------------------- */
2216 259 : if (EQUAL(osFormat, "GeoTIFF"))
2217 : {
2218 27 : if (nSkipBytes != 0)
2219 : {
2220 2 : CPLError(CE_Warning, CPLE_NotSupported,
2221 : "Ignoring StartByte=%d for format=GeoTIFF",
2222 : 1 + nSkipBytes);
2223 : }
2224 27 : if (osQubeFile == poOpenInfo->pszFilename)
2225 : {
2226 0 : CPLError(CE_Failure, CPLE_AppDefined, "A ^Core file must be set");
2227 0 : return nullptr;
2228 : }
2229 27 : poDS->m_poExternalDS =
2230 27 : GDALDataset::FromHandle(GDALOpen(osQubeFile, poOpenInfo->eAccess));
2231 27 : if (poDS->m_poExternalDS == nullptr)
2232 : {
2233 2 : return nullptr;
2234 : }
2235 25 : if (poDS->m_poExternalDS->GetRasterXSize() != poDS->nRasterXSize ||
2236 24 : poDS->m_poExternalDS->GetRasterYSize() != poDS->nRasterYSize ||
2237 71 : poDS->m_poExternalDS->GetRasterCount() != nBands ||
2238 22 : poDS->m_poExternalDS->GetRasterBand(1)->GetRasterDataType() !=
2239 : eDataType)
2240 : {
2241 4 : CPLError(CE_Failure, CPLE_AppDefined,
2242 : "%s has incompatible characteristics with the ones "
2243 : "declared in the label.",
2244 : osQubeFile.c_str());
2245 4 : return nullptr;
2246 : }
2247 : }
2248 : else
2249 : {
2250 232 : if (poOpenInfo->eAccess == GA_ReadOnly)
2251 230 : poDS->m_fpImage = VSIFOpenL(osQubeFile, "r");
2252 : else
2253 2 : poDS->m_fpImage = VSIFOpenL(osQubeFile, "r+");
2254 :
2255 232 : if (poDS->m_fpImage == nullptr)
2256 : {
2257 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s: %s.",
2258 2 : osQubeFile.c_str(), VSIStrerror(errno));
2259 2 : return nullptr;
2260 : }
2261 :
2262 : // Sanity checks in case the external raw file appears to be a
2263 : // TIFF file
2264 230 : if (EQUAL(CPLGetExtensionSafe(osQubeFile).c_str(), "tif"))
2265 : {
2266 : GDALDataset *poTIF_DS =
2267 23 : GDALDataset::FromHandle(GDALOpen(osQubeFile, GA_ReadOnly));
2268 23 : if (poTIF_DS)
2269 : {
2270 23 : bool bWarned = false;
2271 23 : if (poTIF_DS->GetRasterXSize() != poDS->nRasterXSize ||
2272 22 : poTIF_DS->GetRasterYSize() != poDS->nRasterYSize ||
2273 21 : poTIF_DS->GetRasterCount() != nBands ||
2274 20 : poTIF_DS->GetRasterBand(1)->GetRasterDataType() !=
2275 45 : eDataType ||
2276 19 : poTIF_DS->GetMetadataItem("COMPRESSION",
2277 19 : "IMAGE_STRUCTURE") != nullptr)
2278 : {
2279 5 : bWarned = true;
2280 5 : CPLError(
2281 : CE_Warning, CPLE_AppDefined,
2282 : "%s has incompatible characteristics with the ones "
2283 : "declared in the label.",
2284 : osQubeFile.c_str());
2285 : }
2286 23 : int nBlockXSize = 1, nBlockYSize = 1;
2287 23 : poTIF_DS->GetRasterBand(1)->GetBlockSize(&nBlockXSize,
2288 : &nBlockYSize);
2289 23 : if ((poDS->m_bIsTiled &&
2290 46 : (nBlockXSize != tileSizeX || nBlockYSize != tileSizeY)) ||
2291 23 : (!poDS->m_bIsTiled && (nBlockXSize != nCols ||
2292 2 : (nBands > 1 && nBlockYSize != 1))))
2293 : {
2294 2 : if (!bWarned)
2295 : {
2296 1 : bWarned = true;
2297 1 : CPLError(
2298 : CE_Warning, CPLE_AppDefined,
2299 : "%s has incompatible characteristics with the ones "
2300 : "declared in the label.",
2301 : osQubeFile.c_str());
2302 : }
2303 : }
2304 : // to please Clang Static Analyzer
2305 23 : nBlockXSize = std::max(1, nBlockXSize);
2306 23 : nBlockYSize = std::max(1, nBlockYSize);
2307 :
2308 : // Check that blocks are effectively written in expected order.
2309 23 : const int nBlockSizeBytes = nBlockXSize * nBlockYSize *
2310 23 : GDALGetDataTypeSizeBytes(eDataType);
2311 23 : bool bGoOn = !bWarned;
2312 23 : const int l_nBlocksPerRow = DIV_ROUND_UP(nCols, nBlockXSize);
2313 23 : const int l_nBlocksPerColumn = DIV_ROUND_UP(nRows, nBlockYSize);
2314 23 : int nBlockNo = 0;
2315 52 : for (int i = 0; i < nBands && bGoOn; i++)
2316 : {
2317 1285 : for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
2318 : {
2319 2950 : for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
2320 : {
2321 : const char *pszBlockOffset =
2322 3388 : poTIF_DS->GetRasterBand(i + 1)->GetMetadataItem(
2323 : CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y),
2324 1694 : "TIFF");
2325 1694 : if (pszBlockOffset)
2326 : {
2327 1694 : GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
2328 1694 : if (nOffset !=
2329 1694 : nSkipBytes + nBlockNo * nBlockSizeBytes)
2330 : {
2331 : // bWarned = true;
2332 2 : CPLError(CE_Warning, CPLE_AppDefined,
2333 : "%s has incompatible "
2334 : "characteristics with the ones "
2335 : "declared in the label.",
2336 : osQubeFile.c_str());
2337 2 : bGoOn = false;
2338 : }
2339 : }
2340 1694 : nBlockNo++;
2341 : }
2342 : }
2343 : }
2344 :
2345 23 : delete poTIF_DS;
2346 : }
2347 : }
2348 : }
2349 :
2350 251 : poDS->eAccess = poOpenInfo->eAccess;
2351 :
2352 : /* -------------------------------------------------------------------- */
2353 : /* Compute the line offset. */
2354 : /* -------------------------------------------------------------------- */
2355 251 : int nLineOffset = 0;
2356 251 : int nPixelOffset = 0;
2357 251 : vsi_l_offset nBandOffset = 0;
2358 :
2359 251 : if (EQUAL(osFormat, "BandSequential"))
2360 : {
2361 205 : const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
2362 205 : nPixelOffset = nItemSize;
2363 : try
2364 : {
2365 205 : nLineOffset = (CPLSM(nPixelOffset) * CPLSM(nCols)).v();
2366 : }
2367 0 : catch (const CPLSafeIntOverflow &)
2368 : {
2369 0 : return nullptr;
2370 : }
2371 205 : nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
2372 :
2373 205 : poDS->m_sLayout.osRawFilename = std::move(osQubeFile);
2374 205 : if (nBands > 1)
2375 16 : poDS->m_sLayout.eInterleaving = RawBinaryLayout::Interleaving::BSQ;
2376 205 : poDS->m_sLayout.eDataType = eDataType;
2377 205 : poDS->m_sLayout.bLittleEndianOrder = bIsLSB;
2378 205 : poDS->m_sLayout.nImageOffset = nSkipBytes;
2379 205 : poDS->m_sLayout.nPixelOffset = nPixelOffset;
2380 205 : poDS->m_sLayout.nLineOffset = nLineOffset;
2381 205 : poDS->m_sLayout.nBandOffset = static_cast<GIntBig>(nBandOffset);
2382 : }
2383 : /* else Tiled or external */
2384 :
2385 : /* -------------------------------------------------------------------- */
2386 : /* Extract BandBin info. */
2387 : /* -------------------------------------------------------------------- */
2388 502 : std::vector<std::string> aosBandNames;
2389 502 : std::vector<std::string> aosBandUnits;
2390 502 : std::vector<double> adfWavelengths;
2391 502 : std::vector<std::string> aosWavelengthsUnit;
2392 502 : std::vector<double> adfBandwidth;
2393 502 : std::vector<std::string> aosBandwidthUnit;
2394 753 : const auto oIsisCube = poDS->m_oJSonLabel.GetObj("IsisCube");
2395 251 : if (oIsisCube.GetType() == CPLJSONObject::Type::Object)
2396 : {
2397 963 : for (const auto &oChildIsisCube : oIsisCube.GetChildren())
2398 : {
2399 453 : if (oChildIsisCube.GetType() == CPLJSONObject::Type::Object &&
2400 2039 : oChildIsisCube.GetString("_type") == "group" &&
2401 1074 : (oChildIsisCube.GetName() == "BandBin" ||
2402 874 : oChildIsisCube.GetString("_container_name") == "BandBin"))
2403 : {
2404 187 : for (const auto &child : oChildIsisCube.GetChildren())
2405 : {
2406 146 : if (CPLString(child.GetName()).ifind("name") !=
2407 : std::string::npos)
2408 : {
2409 : // Use "name" in priority
2410 15 : if (EQUAL(child.GetName().c_str(), "name"))
2411 : {
2412 5 : aosBandNames.clear();
2413 : }
2414 10 : else if (!aosBandNames.empty())
2415 : {
2416 3 : continue;
2417 : }
2418 :
2419 12 : if (child.GetType() == CPLJSONObject::Type::String &&
2420 : nBands == 1)
2421 : {
2422 4 : aosBandNames.push_back(child.ToString());
2423 : }
2424 8 : else if (child.GetType() == CPLJSONObject::Type::Array)
2425 : {
2426 16 : auto oArray = child.ToArray();
2427 8 : if (oArray.Size() == nBands)
2428 : {
2429 28 : for (int i = 0; i < nBands; i++)
2430 : {
2431 20 : if (oArray[i].GetType() ==
2432 : CPLJSONObject::Type::String)
2433 : {
2434 20 : aosBandNames.push_back(
2435 40 : oArray[i].ToString());
2436 : }
2437 : else
2438 : {
2439 0 : aosBandNames.clear();
2440 0 : break;
2441 : }
2442 : }
2443 : }
2444 : }
2445 : }
2446 135 : else if (EQUAL(child.GetName().c_str(), "BandSuffixUnit") &&
2447 4 : child.GetType() == CPLJSONObject::Type::Array)
2448 : {
2449 8 : auto oArray = child.ToArray();
2450 4 : if (oArray.Size() == nBands)
2451 : {
2452 12 : for (int i = 0; i < nBands; i++)
2453 : {
2454 8 : if (oArray[i].GetType() ==
2455 : CPLJSONObject::Type::String)
2456 : {
2457 8 : aosBandUnits.push_back(
2458 16 : oArray[i].ToString());
2459 : }
2460 : else
2461 : {
2462 0 : aosBandUnits.clear();
2463 0 : break;
2464 : }
2465 : }
2466 : }
2467 : }
2468 377 : else if (EQUAL(child.GetName().c_str(), "BandBinCenter") ||
2469 250 : EQUAL(child.GetName().c_str(), "Center"))
2470 : {
2471 41 : GetValueAndUnits(child, adfWavelengths,
2472 : aosWavelengthsUnit, nBands);
2473 : }
2474 90 : else if (EQUAL(child.GetName().c_str(), "BandBinUnit") &&
2475 4 : child.GetType() == CPLJSONObject::Type::String)
2476 : {
2477 12 : CPLString unit(child.ToString());
2478 4 : if (STARTS_WITH_CI(unit, "micromet") ||
2479 0 : EQUAL(unit, "um") ||
2480 4 : STARTS_WITH_CI(unit, "nanomet") ||
2481 0 : EQUAL(unit, "nm"))
2482 : {
2483 4 : aosWavelengthsUnit.push_back(child.ToString());
2484 : }
2485 : }
2486 82 : else if (EQUAL(child.GetName().c_str(), "Width"))
2487 : {
2488 7 : GetValueAndUnits(child, adfBandwidth, aosBandwidthUnit,
2489 : nBands);
2490 : }
2491 : }
2492 :
2493 41 : if (!adfWavelengths.empty() && aosWavelengthsUnit.size() == 1)
2494 : {
2495 11 : for (int i = 1; i < nBands; i++)
2496 : {
2497 4 : aosWavelengthsUnit.push_back(aosWavelengthsUnit[0]);
2498 : }
2499 : }
2500 41 : if (!adfBandwidth.empty() && aosBandwidthUnit.size() == 1)
2501 : {
2502 7 : for (int i = 1; i < nBands; i++)
2503 : {
2504 2 : aosBandwidthUnit.push_back(aosBandwidthUnit[0]);
2505 : }
2506 : }
2507 : }
2508 : }
2509 : }
2510 :
2511 : /* -------------------------------------------------------------------- */
2512 : /* Create band information objects. */
2513 : /* -------------------------------------------------------------------- */
2514 : #ifdef CPL_LSB
2515 251 : const bool bNativeOrder = bIsLSB;
2516 : #else
2517 : const bool bNativeOrder = !bIsLSB;
2518 : #endif
2519 :
2520 552 : for (int i = 0; i < nBands; i++)
2521 : {
2522 : GDALRasterBand *poBand;
2523 :
2524 301 : if (poDS->m_poExternalDS != nullptr)
2525 : {
2526 : auto poISISBand = std::make_unique<ISIS3WrapperRasterBand>(
2527 21 : poDS->m_poExternalDS->GetRasterBand(i + 1));
2528 42 : poISISBand->SetMaskBand(
2529 42 : std::make_unique<ISISMaskBand>(poISISBand.get()));
2530 21 : poDS->SetBand(i + 1, std::move(poISISBand));
2531 21 : poBand = poDS->GetRasterBand(i + 1);
2532 : }
2533 280 : else if (poDS->m_bIsTiled)
2534 : {
2535 : auto poISISBand = std::make_unique<ISISTiledBand>(
2536 41 : poDS.get(), poDS->m_fpImage, i + 1, eDataType, tileSizeX,
2537 82 : tileSizeY, nSkipBytes, 0, 0, bNativeOrder);
2538 41 : if (!poISISBand->IsValid())
2539 : {
2540 0 : return nullptr;
2541 : }
2542 82 : poISISBand->SetMaskBand(
2543 82 : std::make_unique<ISISMaskBand>(poISISBand.get()));
2544 41 : poDS->SetBand(i + 1, std::move(poISISBand));
2545 41 : poBand = poDS->GetRasterBand(i + 1);
2546 : }
2547 : else
2548 : {
2549 : auto poISISBand = std::make_unique<ISIS3RawRasterBand>(
2550 239 : poDS.get(), i + 1, poDS->m_fpImage,
2551 239 : nSkipBytes + nBandOffset * i, nPixelOffset, nLineOffset,
2552 239 : eDataType, bNativeOrder);
2553 239 : if (!poISISBand->IsValid())
2554 : {
2555 0 : return nullptr;
2556 : }
2557 478 : poISISBand->SetMaskBand(
2558 478 : std::make_unique<ISISMaskBand>(poISISBand.get()));
2559 239 : poDS->SetBand(i + 1, std::move(poISISBand));
2560 239 : poBand = poDS->GetRasterBand(i + 1);
2561 : }
2562 :
2563 301 : if (i < static_cast<int>(aosBandNames.size()))
2564 : {
2565 17 : poBand->SetDescription(aosBandNames[i].c_str());
2566 : }
2567 350 : if (i < static_cast<int>(adfWavelengths.size()) &&
2568 49 : i < static_cast<int>(aosWavelengthsUnit.size()))
2569 : {
2570 11 : poBand->SetMetadataItem("WAVELENGTH",
2571 11 : CPLSPrintf("%f", adfWavelengths[i]));
2572 11 : poBand->SetMetadataItem("WAVELENGTH_UNIT",
2573 11 : aosWavelengthsUnit[i].c_str());
2574 18 : if (i < static_cast<int>(adfBandwidth.size()) &&
2575 7 : i < static_cast<int>(aosBandwidthUnit.size()))
2576 : {
2577 7 : poBand->SetMetadataItem("BANDWIDTH",
2578 7 : CPLSPrintf("%f", adfBandwidth[i]));
2579 7 : poBand->SetMetadataItem("BANDWIDTH_UNIT",
2580 7 : aosBandwidthUnit[i].c_str());
2581 : }
2582 : }
2583 301 : if (i < static_cast<int>(aosBandUnits.size()))
2584 : {
2585 8 : poBand->SetUnitType(aosBandUnits[i].c_str());
2586 : }
2587 :
2588 301 : poBand->SetNoDataValue(dfNoData);
2589 :
2590 : // Set offset/scale values.
2591 : const double dfOffset =
2592 301 : CPLAtofM(poDS->GetKeyword("IsisCube.Core.Pixels.Base", "0.0"));
2593 301 : const double dfScale = CPLAtofM(
2594 : poDS->GetKeyword("IsisCube.Core.Pixels.Multiplier", "1.0"));
2595 301 : if (dfOffset != 0.0 || dfScale != 1.0)
2596 : {
2597 52 : poBand->SetOffset(dfOffset);
2598 52 : poBand->SetScale(dfScale);
2599 : }
2600 : }
2601 :
2602 : /* -------------------------------------------------------------------- */
2603 : /* Check for a .prj file. For ISIS3 I would like to keep this in */
2604 : /* -------------------------------------------------------------------- */
2605 502 : const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
2606 502 : const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
2607 502 : const std::string osPrjFile = CPLFormCIFilenameSafe(osPath, osName, "prj");
2608 :
2609 251 : VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
2610 251 : if (fp != nullptr)
2611 : {
2612 0 : VSIFCloseL(fp);
2613 :
2614 0 : char **papszLines = CSLLoad(osPrjFile.c_str());
2615 :
2616 0 : OGRSpatialReference oSRS2;
2617 0 : if (oSRS2.importFromESRI(papszLines) == OGRERR_NONE)
2618 : {
2619 0 : poDS->m_aosAdditionalFiles.AddString(osPrjFile.c_str());
2620 0 : poDS->m_oSRS = std::move(oSRS2);
2621 0 : poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2622 : }
2623 :
2624 0 : CSLDestroy(papszLines);
2625 : }
2626 :
2627 251 : if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
2628 : {
2629 99 : poDS->m_bGotTransform = true;
2630 99 : poDS->m_gt.xorig = dfULXMap;
2631 99 : poDS->m_gt.xscale = dfXDim;
2632 99 : poDS->m_gt.xrot = 0.0;
2633 99 : poDS->m_gt.yorig = dfULYMap;
2634 99 : poDS->m_gt.yrot = 0.0;
2635 99 : poDS->m_gt.yscale = dfYDim;
2636 : }
2637 :
2638 251 : if (!poDS->m_bGotTransform)
2639 : {
2640 304 : poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
2641 304 : poOpenInfo->pszFilename, "cbw", poDS->m_gt.data()));
2642 152 : if (poDS->m_bGotTransform)
2643 : {
2644 0 : poDS->m_aosAdditionalFiles.AddString(
2645 0 : CPLResetExtensionSafe(poOpenInfo->pszFilename, "cbw").c_str());
2646 : }
2647 : }
2648 :
2649 251 : if (!poDS->m_bGotTransform)
2650 : {
2651 304 : poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
2652 304 : poOpenInfo->pszFilename, "wld", poDS->m_gt.data()));
2653 152 : if (poDS->m_bGotTransform)
2654 : {
2655 0 : poDS->m_aosAdditionalFiles.AddString(
2656 0 : CPLResetExtensionSafe(poOpenInfo->pszFilename, "wld").c_str());
2657 : }
2658 : }
2659 :
2660 : /* -------------------------------------------------------------------- */
2661 : /* Initialize any PAM information. */
2662 : /* -------------------------------------------------------------------- */
2663 251 : poDS->SetDescription(poOpenInfo->pszFilename);
2664 251 : poDS->TryLoadXML();
2665 :
2666 : /* -------------------------------------------------------------------- */
2667 : /* Check for overviews. */
2668 : /* -------------------------------------------------------------------- */
2669 251 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
2670 :
2671 251 : return poDS.release();
2672 : }
2673 :
2674 : /************************************************************************/
2675 : /* GetKeyword() */
2676 : /************************************************************************/
2677 :
2678 6161 : const char *ISIS3Dataset::GetKeyword(const char *pszPath,
2679 : const char *pszDefault)
2680 :
2681 : {
2682 6161 : return m_oKeywords.GetKeyword(pszPath, pszDefault);
2683 : }
2684 :
2685 : /************************************************************************/
2686 : /* FixLong() */
2687 : /************************************************************************/
2688 :
2689 247 : double ISIS3Dataset::FixLong(double dfLong)
2690 : {
2691 247 : if (m_osLongitudeDirection == "PositiveWest")
2692 4 : dfLong = -dfLong;
2693 247 : if (m_bForce360 && dfLong < 0)
2694 2 : dfLong += 360.0;
2695 247 : return dfLong;
2696 : }
2697 :
2698 : /************************************************************************/
2699 : /* BuildLabel() */
2700 : /************************************************************************/
2701 :
2702 132 : void ISIS3Dataset::BuildLabel()
2703 : {
2704 264 : CPLJSONObject oLabel = m_oSrcJSonLabel;
2705 132 : if (!oLabel.IsValid())
2706 : {
2707 105 : oLabel = CPLJSONObject();
2708 : }
2709 : // If we have a source label, then edit it directly
2710 396 : CPLJSONObject oIsisCube = GetOrCreateJSONObject(oLabel, "IsisCube");
2711 132 : oIsisCube.Set("_type", "object");
2712 :
2713 132 : if (!m_osComment.empty())
2714 1 : oIsisCube.Set("_comment", m_osComment);
2715 :
2716 396 : CPLJSONObject oCore = GetOrCreateJSONObject(oIsisCube, "Core");
2717 132 : if (oCore.GetType() != CPLJSONObject::Type::Object)
2718 : {
2719 0 : oIsisCube.Delete("Core");
2720 0 : oCore = CPLJSONObject();
2721 0 : oIsisCube.Add("Core", oCore);
2722 : }
2723 132 : oCore.Set("_type", "object");
2724 :
2725 132 : if (!m_osExternalFilename.empty())
2726 : {
2727 31 : if (m_poExternalDS && m_bGeoTIFFAsRegularExternal)
2728 : {
2729 8 : if (!m_bGeoTIFFInitDone)
2730 : {
2731 : cpl::down_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
2732 1 : ->InitFile();
2733 : }
2734 :
2735 : const char *pszOffset =
2736 8 : m_poExternalDS->GetRasterBand(1)->GetMetadataItem(
2737 8 : "BLOCK_OFFSET_0_0", "TIFF");
2738 8 : if (pszOffset)
2739 : {
2740 8 : oCore.Set("StartByte", 1 + atoi(pszOffset));
2741 : }
2742 : else
2743 : {
2744 : // Shouldn't happen normally
2745 0 : CPLError(CE_Warning, CPLE_AppDefined,
2746 : "Missing BLOCK_OFFSET_0_0");
2747 0 : m_bGeoTIFFAsRegularExternal = false;
2748 0 : oCore.Set("StartByte", 1);
2749 8 : }
2750 : }
2751 : else
2752 : {
2753 23 : oCore.Set("StartByte", 1);
2754 : }
2755 31 : if (!m_osExternalFilename.empty())
2756 : {
2757 : const CPLString osExternalFilename =
2758 31 : CPLGetFilename(m_osExternalFilename);
2759 31 : oCore.Set("^Core", osExternalFilename);
2760 : }
2761 : }
2762 : else
2763 : {
2764 101 : oCore.Set("StartByte", pszSTARTBYTE_PLACEHOLDER);
2765 101 : oCore.Delete("^Core");
2766 : }
2767 :
2768 132 : if (m_poExternalDS && !m_bGeoTIFFAsRegularExternal)
2769 : {
2770 10 : oCore.Set("Format", "GeoTIFF");
2771 10 : oCore.Delete("TileSamples");
2772 10 : oCore.Delete("TileLines");
2773 : }
2774 122 : else if (m_bIsTiled)
2775 : {
2776 9 : oCore.Set("Format", "Tile");
2777 9 : int nBlockXSize = 1, nBlockYSize = 1;
2778 9 : GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
2779 9 : oCore.Set("TileSamples", nBlockXSize);
2780 9 : oCore.Set("TileLines", nBlockYSize);
2781 : }
2782 : else
2783 : {
2784 113 : oCore.Set("Format", "BandSequential");
2785 113 : oCore.Delete("TileSamples");
2786 113 : oCore.Delete("TileLines");
2787 : }
2788 :
2789 396 : CPLJSONObject oDimensions = GetOrCreateJSONObject(oCore, "Dimensions");
2790 132 : oDimensions.Set("_type", "group");
2791 132 : oDimensions.Set("Samples", nRasterXSize);
2792 132 : oDimensions.Set("Lines", nRasterYSize);
2793 132 : oDimensions.Set("Bands", nBands);
2794 :
2795 396 : CPLJSONObject oPixels = GetOrCreateJSONObject(oCore, "Pixels");
2796 132 : oPixels.Set("_type", "group");
2797 132 : const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
2798 159 : oPixels.Set("Type", (eDT == GDT_UInt8) ? "UnsignedByte"
2799 44 : : (eDT == GDT_UInt16) ? "UnsignedWord"
2800 17 : : (eDT == GDT_Int16) ? "SignedWord"
2801 : : "Real");
2802 :
2803 132 : oPixels.Set("ByteOrder", "Lsb");
2804 132 : oPixels.Set("Base", GetRasterBand(1)->GetOffset());
2805 132 : oPixels.Set("Multiplier", GetRasterBand(1)->GetScale());
2806 :
2807 132 : const OGRSpatialReference &oSRS = m_oSRS;
2808 :
2809 132 : if (!m_bUseSrcMapping)
2810 : {
2811 130 : oIsisCube.Delete("Mapping");
2812 : }
2813 :
2814 396 : CPLJSONObject oMapping = GetOrCreateJSONObject(oIsisCube, "Mapping");
2815 134 : if (m_bUseSrcMapping && oMapping.IsValid() &&
2816 2 : oMapping.GetType() == CPLJSONObject::Type::Object)
2817 : {
2818 2 : if (!m_osTargetName.empty())
2819 1 : oMapping.Set("TargetName", m_osTargetName);
2820 2 : if (!m_osLatitudeType.empty())
2821 1 : oMapping.Set("LatitudeType", m_osLatitudeType);
2822 2 : if (!m_osLongitudeDirection.empty())
2823 1 : oMapping.Set("LongitudeDirection", m_osLongitudeDirection);
2824 : }
2825 130 : else if (!m_bUseSrcMapping && !m_oSRS.IsEmpty())
2826 : {
2827 62 : oMapping.Add("_type", "group");
2828 :
2829 62 : if (oSRS.IsProjected() || oSRS.IsGeographic())
2830 : {
2831 62 : const char *pszDatum = oSRS.GetAttrValue("DATUM");
2832 124 : CPLString osTargetName(m_osTargetName);
2833 62 : if (osTargetName.empty())
2834 : {
2835 61 : if (pszDatum && STARTS_WITH(pszDatum, "D_"))
2836 : {
2837 24 : osTargetName = pszDatum + 2;
2838 : }
2839 37 : else if (pszDatum)
2840 : {
2841 37 : osTargetName = pszDatum;
2842 : }
2843 : }
2844 62 : if (!osTargetName.empty())
2845 62 : oMapping.Add("TargetName", osTargetName);
2846 :
2847 62 : oMapping.Add("EquatorialRadius/value", oSRS.GetSemiMajor());
2848 62 : oMapping.Add("EquatorialRadius/unit", "meters");
2849 62 : oMapping.Add("PolarRadius/value", oSRS.GetSemiMinor());
2850 62 : oMapping.Add("PolarRadius/unit", "meters");
2851 :
2852 62 : if (!m_osLatitudeType.empty())
2853 1 : oMapping.Add("LatitudeType", m_osLatitudeType);
2854 : else
2855 61 : oMapping.Add("LatitudeType", "Planetocentric");
2856 :
2857 62 : if (!m_osLongitudeDirection.empty())
2858 1 : oMapping.Add("LongitudeDirection", m_osLongitudeDirection);
2859 : else
2860 61 : oMapping.Add("LongitudeDirection", "PositiveEast");
2861 :
2862 62 : double adfX[4] = {0};
2863 62 : double adfY[4] = {0};
2864 62 : bool bLongLatCorners = false;
2865 62 : if (m_bGotTransform)
2866 : {
2867 270 : for (int i = 0; i < 4; i++)
2868 : {
2869 216 : adfX[i] = m_gt.xorig + (i % 2) * nRasterXSize * m_gt.xscale;
2870 216 : adfY[i] = m_gt.yorig + ((i == 0 || i == 3) ? 0 : 1) *
2871 216 : nRasterYSize * m_gt.yscale;
2872 : }
2873 54 : if (oSRS.IsGeographic())
2874 : {
2875 31 : bLongLatCorners = true;
2876 : }
2877 : else
2878 : {
2879 23 : OGRSpatialReference *poSRSLongLat = oSRS.CloneGeogCS();
2880 23 : if (poSRSLongLat)
2881 : {
2882 23 : poSRSLongLat->SetAxisMappingStrategy(
2883 : OAMS_TRADITIONAL_GIS_ORDER);
2884 : OGRCoordinateTransformation *poCT =
2885 23 : OGRCreateCoordinateTransformation(&oSRS,
2886 : poSRSLongLat);
2887 23 : if (poCT)
2888 : {
2889 23 : if (poCT->Transform(4, adfX, adfY))
2890 : {
2891 23 : bLongLatCorners = true;
2892 : }
2893 23 : delete poCT;
2894 : }
2895 23 : delete poSRSLongLat;
2896 : }
2897 : }
2898 : }
2899 62 : if (bLongLatCorners)
2900 : {
2901 270 : for (int i = 0; i < 4; i++)
2902 : {
2903 216 : adfX[i] = FixLong(adfX[i]);
2904 : }
2905 : }
2906 :
2907 62 : if (bLongLatCorners &&
2908 54 : (m_bForce360 || adfX[0] < -180.0 || adfX[3] > 180.0))
2909 : {
2910 1 : oMapping.Add("LongitudeDomain", 360);
2911 : }
2912 : else
2913 : {
2914 61 : oMapping.Add("LongitudeDomain", 180);
2915 : }
2916 :
2917 62 : if (m_bWriteBoundingDegrees && !m_osBoundingDegrees.empty())
2918 : {
2919 : char **papszTokens =
2920 1 : CSLTokenizeString2(m_osBoundingDegrees, ",", 0);
2921 1 : if (CSLCount(papszTokens) == 4)
2922 : {
2923 1 : oMapping.Add("MinimumLatitude", CPLAtof(papszTokens[1]));
2924 1 : oMapping.Add("MinimumLongitude", CPLAtof(papszTokens[0]));
2925 1 : oMapping.Add("MaximumLatitude", CPLAtof(papszTokens[3]));
2926 1 : oMapping.Add("MaximumLongitude", CPLAtof(papszTokens[2]));
2927 : }
2928 1 : CSLDestroy(papszTokens);
2929 : }
2930 61 : else if (m_bWriteBoundingDegrees && bLongLatCorners)
2931 : {
2932 52 : oMapping.Add("MinimumLatitude",
2933 : std::min(std::min(adfY[0], adfY[1]),
2934 52 : std::min(adfY[2], adfY[3])));
2935 52 : oMapping.Add("MinimumLongitude",
2936 : std::min(std::min(adfX[0], adfX[1]),
2937 52 : std::min(adfX[2], adfX[3])));
2938 52 : oMapping.Add("MaximumLatitude",
2939 : std::max(std::max(adfY[0], adfY[1]),
2940 52 : std::max(adfY[2], adfY[3])));
2941 52 : oMapping.Add("MaximumLongitude",
2942 : std::max(std::max(adfX[0], adfX[1]),
2943 52 : std::max(adfX[2], adfX[3])));
2944 : }
2945 :
2946 62 : const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
2947 62 : if (pszProjection == nullptr)
2948 : {
2949 31 : oMapping.Add("ProjectionName", "SimpleCylindrical");
2950 31 : oMapping.Add("CenterLongitude", 0.0);
2951 31 : oMapping.Add("CenterLatitude", 0.0);
2952 31 : oMapping.Add("CenterLatitudeRadius", oSRS.GetSemiMajor());
2953 : }
2954 31 : else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
2955 : {
2956 14 : oMapping.Add("ProjectionName", "Equirectangular");
2957 14 : if (oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
2958 : {
2959 1 : CPLError(CE_Warning, CPLE_NotSupported,
2960 : "Ignoring %s. Only 0 value supported",
2961 : SRS_PP_LATITUDE_OF_ORIGIN);
2962 : }
2963 14 : oMapping.Add("CenterLongitude",
2964 : FixLong(oSRS.GetNormProjParm(
2965 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2966 : const double dfCenterLat =
2967 14 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
2968 14 : oMapping.Add("CenterLatitude", dfCenterLat);
2969 :
2970 : // in radians
2971 14 : const double radLat = dfCenterLat * M_PI / 180;
2972 14 : const double semi_major = oSRS.GetSemiMajor();
2973 14 : const double semi_minor = oSRS.GetSemiMinor();
2974 : const double localRadius =
2975 14 : semi_major * semi_minor /
2976 14 : sqrt(pow(semi_minor * cos(radLat), 2) +
2977 14 : pow(semi_major * sin(radLat), 2));
2978 14 : oMapping.Add("CenterLatitudeRadius", localRadius);
2979 : }
2980 :
2981 17 : else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
2982 : {
2983 1 : oMapping.Add("ProjectionName", "Orthographic");
2984 1 : oMapping.Add("CenterLongitude",
2985 : FixLong(oSRS.GetNormProjParm(
2986 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2987 1 : oMapping.Add(
2988 : "CenterLatitude",
2989 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2990 : }
2991 :
2992 16 : else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
2993 : {
2994 1 : oMapping.Add("ProjectionName", "Sinusoidal");
2995 1 : oMapping.Add("CenterLongitude",
2996 : FixLong(oSRS.GetNormProjParm(
2997 : SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
2998 : }
2999 :
3000 15 : else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
3001 : {
3002 1 : oMapping.Add("ProjectionName", "Mercator");
3003 1 : oMapping.Add("CenterLongitude",
3004 : FixLong(oSRS.GetNormProjParm(
3005 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
3006 1 : oMapping.Add(
3007 : "CenterLatitude",
3008 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
3009 1 : oMapping.Add("scaleFactor",
3010 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
3011 : }
3012 :
3013 14 : else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
3014 : {
3015 1 : oMapping.Add("ProjectionName", "PolarStereographic");
3016 1 : oMapping.Add("CenterLongitude",
3017 : FixLong(oSRS.GetNormProjParm(
3018 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
3019 1 : oMapping.Add(
3020 : "CenterLatitude",
3021 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
3022 1 : oMapping.Add("scaleFactor",
3023 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
3024 : }
3025 :
3026 13 : else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
3027 : {
3028 11 : oMapping.Add("ProjectionName", "TransverseMercator");
3029 11 : oMapping.Add("CenterLongitude",
3030 : FixLong(oSRS.GetNormProjParm(
3031 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
3032 11 : oMapping.Add(
3033 : "CenterLatitude",
3034 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
3035 11 : oMapping.Add("scaleFactor",
3036 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
3037 : }
3038 :
3039 2 : else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
3040 : {
3041 1 : oMapping.Add("ProjectionName", "LambertConformal");
3042 1 : oMapping.Add("CenterLongitude",
3043 : FixLong(oSRS.GetNormProjParm(
3044 : SRS_PP_CENTRAL_MERIDIAN, 0.0)));
3045 1 : oMapping.Add(
3046 : "CenterLatitude",
3047 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
3048 1 : oMapping.Add(
3049 : "FirstStandardParallel",
3050 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0));
3051 1 : oMapping.Add(
3052 : "SecondStandardParallel",
3053 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0));
3054 : }
3055 :
3056 1 : else if (EQUAL(pszProjection,
3057 : "Vertical Perspective")) // PROJ 7 required
3058 : {
3059 0 : oMapping.Add("ProjectionName", "PointPerspective");
3060 0 : oMapping.Add("CenterLongitude",
3061 : FixLong(oSRS.GetNormProjParm(
3062 : "Longitude of topocentric origin", 0.0)));
3063 0 : oMapping.Add("CenterLatitude",
3064 : oSRS.GetNormProjParm(
3065 : "Latitude of topocentric origin", 0.0));
3066 : // ISIS3 value is the distance from center of ellipsoid, in km
3067 0 : oMapping.Add("Distance",
3068 0 : (oSRS.GetNormProjParm("Viewpoint height", 0.0) +
3069 0 : oSRS.GetSemiMajor()) /
3070 : 1000.0);
3071 : }
3072 :
3073 1 : else if (EQUAL(pszProjection, "custom_proj4"))
3074 : {
3075 : const char *pszProj4 =
3076 1 : oSRS.GetExtension("PROJCS", "PROJ4", nullptr);
3077 1 : if (pszProj4 && strstr(pszProj4, "+proj=ob_tran") &&
3078 1 : strstr(pszProj4, "+o_proj=eqc"))
3079 : {
3080 : const auto FetchParam =
3081 3 : [](const char *pszProj4Str, const char *pszKey)
3082 : {
3083 6 : CPLString needle;
3084 3 : needle.Printf("+%s=", pszKey);
3085 : const char *pszVal =
3086 3 : strstr(pszProj4Str, needle.c_str());
3087 3 : if (pszVal)
3088 3 : return CPLAtof(pszVal + needle.size());
3089 0 : return 0.0;
3090 : };
3091 :
3092 1 : double dfLonP = FetchParam(pszProj4, "o_lon_p");
3093 1 : double dfLatP = FetchParam(pszProj4, "o_lat_p");
3094 1 : double dfLon0 = FetchParam(pszProj4, "lon_0");
3095 1 : double dfPoleRotation = -dfLonP;
3096 1 : double dfPoleLatitude = 180 - dfLatP;
3097 1 : double dfPoleLongitude = dfLon0;
3098 1 : oMapping.Add("ProjectionName", "ObliqueCylindrical");
3099 1 : oMapping.Add("PoleLatitude", dfPoleLatitude);
3100 1 : oMapping.Add("PoleLongitude", FixLong(dfPoleLongitude));
3101 1 : oMapping.Add("PoleRotation", dfPoleRotation);
3102 : }
3103 : else
3104 : {
3105 0 : CPLError(CE_Warning, CPLE_NotSupported,
3106 : "Projection %s not supported", pszProjection);
3107 : }
3108 : }
3109 : else
3110 : {
3111 0 : CPLError(CE_Warning, CPLE_NotSupported,
3112 : "Projection %s not supported", pszProjection);
3113 : }
3114 :
3115 62 : if (oMapping["ProjectionName"].IsValid())
3116 : {
3117 62 : if (oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
3118 : {
3119 9 : CPLError(CE_Warning, CPLE_NotSupported,
3120 : "Ignoring %s. Only 0 value supported",
3121 : SRS_PP_FALSE_EASTING);
3122 : }
3123 62 : if (oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
3124 : {
3125 1 : CPLError(CE_Warning, CPLE_AppDefined,
3126 : "Ignoring %s. Only 0 value supported",
3127 : SRS_PP_FALSE_NORTHING);
3128 : }
3129 : }
3130 : }
3131 : else
3132 : {
3133 0 : CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
3134 : }
3135 : }
3136 :
3137 132 : if (!m_bUseSrcMapping && m_bGotTransform)
3138 : {
3139 54 : oMapping.Add("_type", "group");
3140 :
3141 54 : const double dfDegToMeter = oSRS.GetSemiMajor() * M_PI / 180.0;
3142 54 : if (!m_oSRS.IsEmpty() && oSRS.IsProjected())
3143 : {
3144 23 : const double dfLinearUnits = oSRS.GetLinearUnits();
3145 : // Maybe we should deal differently with non meter units ?
3146 23 : const double dfRes = m_gt.xscale * dfLinearUnits;
3147 23 : const double dfScale = dfDegToMeter / dfRes;
3148 23 : oMapping.Add("UpperLeftCornerX", m_gt.xorig);
3149 23 : oMapping.Add("UpperLeftCornerY", m_gt.yorig);
3150 23 : oMapping.Add("PixelResolution/value", dfRes);
3151 23 : oMapping.Add("PixelResolution/unit", "meters/pixel");
3152 23 : oMapping.Add("Scale/value", dfScale);
3153 23 : oMapping.Add("Scale/unit", "pixels/degree");
3154 : }
3155 31 : else if (!m_oSRS.IsEmpty() && oSRS.IsGeographic())
3156 : {
3157 31 : const double dfScale = 1.0 / m_gt.xscale;
3158 31 : const double dfRes = m_gt.xscale * dfDegToMeter;
3159 31 : oMapping.Add("UpperLeftCornerX", m_gt.xorig * dfDegToMeter);
3160 31 : oMapping.Add("UpperLeftCornerY", m_gt.yorig * dfDegToMeter);
3161 31 : oMapping.Add("PixelResolution/value", dfRes);
3162 31 : oMapping.Add("PixelResolution/unit", "meters/pixel");
3163 31 : oMapping.Add("Scale/value", dfScale);
3164 31 : oMapping.Add("Scale/unit", "pixels/degree");
3165 : }
3166 : else
3167 : {
3168 0 : oMapping.Add("UpperLeftCornerX", m_gt.xorig);
3169 0 : oMapping.Add("UpperLeftCornerY", m_gt.yorig);
3170 0 : oMapping.Add("PixelResolution", m_gt.xscale);
3171 : }
3172 : }
3173 :
3174 396 : CPLJSONObject oLabelLabel = GetOrCreateJSONObject(oLabel, "Label");
3175 132 : oLabelLabel.Set("_type", "object");
3176 132 : oLabelLabel.Set("Bytes", pszLABEL_BYTES_PLACEHOLDER);
3177 :
3178 : // Deal with History object
3179 132 : BuildHistory();
3180 :
3181 132 : oLabel.Delete("History_IsisCube");
3182 132 : if (!m_osHistory.empty())
3183 : {
3184 130 : CPLJSONObject oHistory;
3185 130 : oHistory.Add("_type", "object");
3186 130 : oHistory.Add("_container_name", "History");
3187 130 : oHistory.Add("Name", "IsisCube");
3188 130 : if (m_osExternalFilename.empty())
3189 100 : oHistory.Add("StartByte", pszHISTORY_STARTBYTE_PLACEHOLDER);
3190 : else
3191 30 : oHistory.Add("StartByte", 1);
3192 130 : oHistory.Add("Bytes", static_cast<GIntBig>(m_osHistory.size()));
3193 130 : if (!m_osExternalFilename.empty())
3194 : {
3195 30 : CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
3196 30 : osFilename += ".History.IsisCube";
3197 30 : oHistory.Add("^History", osFilename);
3198 : }
3199 130 : oLabel.Add("History_IsisCube", oHistory);
3200 : }
3201 :
3202 : // Deal with other objects that have StartByte & Bytes
3203 132 : m_aoNonPixelSections.clear();
3204 132 : if (m_oSrcJSonLabel.IsValid())
3205 : {
3206 54 : CPLString osLabelSrcFilename;
3207 81 : CPLJSONObject oFilename = oLabel["_filename"];
3208 27 : if (oFilename.GetType() == CPLJSONObject::Type::String)
3209 : {
3210 22 : osLabelSrcFilename = oFilename.ToString();
3211 : }
3212 :
3213 148 : for (CPLJSONObject &oObj : oLabel.GetChildren())
3214 : {
3215 121 : CPLString osKey = oObj.GetName();
3216 121 : if (osKey == "History_IsisCube")
3217 : {
3218 25 : continue;
3219 : }
3220 :
3221 192 : CPLJSONObject oBytes = oObj.GetObj("Bytes");
3222 109 : if (oBytes.GetType() != CPLJSONObject::Type::Integer ||
3223 13 : oBytes.ToInteger() <= 0)
3224 : {
3225 83 : continue;
3226 : }
3227 :
3228 26 : CPLJSONObject oStartByte = oObj.GetObj("StartByte");
3229 26 : if (oStartByte.GetType() != CPLJSONObject::Type::Integer ||
3230 13 : oStartByte.ToInteger() <= 0)
3231 : {
3232 0 : continue;
3233 : }
3234 :
3235 13 : if (osLabelSrcFilename.empty())
3236 : {
3237 0 : CPLError(CE_Warning, CPLE_AppDefined,
3238 : "Cannot find _filename attribute in "
3239 : "source ISIS3 metadata. Removing object "
3240 : "%s from the label.",
3241 : osKey.c_str());
3242 0 : oLabel.Delete(osKey);
3243 0 : continue;
3244 : }
3245 :
3246 13 : NonPixelSection oSection;
3247 13 : oSection.osSrcFilename = osLabelSrcFilename;
3248 13 : oSection.nSrcOffset =
3249 13 : static_cast<vsi_l_offset>(oObj.GetInteger("StartByte")) - 1U;
3250 13 : oSection.nSize =
3251 13 : static_cast<vsi_l_offset>(oObj.GetInteger("Bytes"));
3252 :
3253 13 : CPLString osName;
3254 26 : CPLJSONObject oName = oObj.GetObj("Name");
3255 13 : if (oName.GetType() == CPLJSONObject::Type::String)
3256 : {
3257 13 : osName = oName.ToString();
3258 : }
3259 :
3260 13 : CPLString osContainerName(osKey);
3261 26 : CPLJSONObject oContainerName = oObj.GetObj("_container_name");
3262 13 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
3263 : {
3264 13 : osContainerName = oContainerName.ToString();
3265 : }
3266 :
3267 13 : const CPLString osKeyFilename("^" + osContainerName);
3268 13 : CPLJSONObject oFilenameCap = oObj.GetObj(osKeyFilename);
3269 13 : if (oFilenameCap.GetType() == CPLJSONObject::Type::String)
3270 : {
3271 : VSIStatBufL sStat;
3272 30 : CPLString osSrcFilename(CPLFormFilenameSafe(
3273 20 : CPLGetPathSafe(osLabelSrcFilename).c_str(),
3274 30 : oFilenameCap.ToString().c_str(), nullptr));
3275 :
3276 10 : if (CPLHasPathTraversal(oFilenameCap.ToString().c_str()))
3277 : {
3278 0 : CPLError(CE_Warning, CPLE_AppDefined,
3279 : "Path traversal detected for %s: %s. Removing "
3280 : "this section from the label",
3281 0 : osKey.c_str(), oFilenameCap.ToString().c_str());
3282 0 : oLabel.Delete(osKey);
3283 0 : continue;
3284 : }
3285 10 : else if (VSIStatL(osSrcFilename, &sStat) == 0)
3286 : {
3287 4 : oSection.osSrcFilename = std::move(osSrcFilename);
3288 : }
3289 : else
3290 : {
3291 6 : CPLError(CE_Warning, CPLE_AppDefined,
3292 : "Object %s points to %s, which does "
3293 : "not exist. Removing this section "
3294 : "from the label",
3295 : osKey.c_str(), osSrcFilename.c_str());
3296 6 : oLabel.Delete(osKey);
3297 6 : continue;
3298 : }
3299 : }
3300 :
3301 7 : if (!m_osExternalFilename.empty())
3302 : {
3303 2 : oObj.Set("StartByte", 1);
3304 : }
3305 : else
3306 : {
3307 10 : CPLString osPlaceHolder;
3308 : osPlaceHolder.Printf(
3309 : "!*^PLACEHOLDER_%d_STARTBYTE^*!",
3310 5 : static_cast<int>(m_aoNonPixelSections.size()) + 1);
3311 5 : oObj.Set("StartByte", osPlaceHolder);
3312 5 : oSection.osPlaceHolder = std::move(osPlaceHolder);
3313 : }
3314 :
3315 7 : if (!m_osExternalFilename.empty())
3316 : {
3317 4 : CPLString osDstFilename(CPLGetBasenameSafe(GetDescription()));
3318 2 : osDstFilename += ".";
3319 2 : osDstFilename += osContainerName;
3320 2 : if (!osName.empty())
3321 : {
3322 2 : osDstFilename += ".";
3323 2 : osDstFilename += osName;
3324 : }
3325 :
3326 4 : oSection.osDstFilename = CPLFormFilenameSafe(
3327 4 : CPLGetPathSafe(GetDescription()).c_str(), osDstFilename,
3328 2 : nullptr);
3329 :
3330 2 : oObj.Set(osKeyFilename, osDstFilename);
3331 : }
3332 : else
3333 : {
3334 5 : oObj.Delete(osKeyFilename);
3335 : }
3336 :
3337 7 : m_aoNonPixelSections.push_back(std::move(oSection));
3338 : }
3339 : }
3340 132 : m_oJSonLabel = std::move(oLabel);
3341 132 : }
3342 :
3343 : /************************************************************************/
3344 : /* BuildHistory() */
3345 : /************************************************************************/
3346 :
3347 132 : void ISIS3Dataset::BuildHistory()
3348 : {
3349 264 : CPLString osHistory;
3350 :
3351 132 : if (m_oSrcJSonLabel.IsValid() && m_bUseSrcHistory)
3352 : {
3353 25 : vsi_l_offset nHistoryOffset = 0;
3354 25 : int nHistorySize = 0;
3355 50 : CPLString osSrcFilename;
3356 :
3357 75 : CPLJSONObject oFilename = m_oSrcJSonLabel["_filename"];
3358 25 : if (oFilename.GetType() == CPLJSONObject::Type::String)
3359 : {
3360 20 : osSrcFilename = oFilename.ToString();
3361 : }
3362 50 : CPLString osHistoryFilename(osSrcFilename);
3363 75 : CPLJSONObject oHistory = m_oSrcJSonLabel["History_IsisCube"];
3364 25 : if (oHistory.GetType() == CPLJSONObject::Type::Object)
3365 : {
3366 33 : CPLJSONObject oHistoryFilename = oHistory["^History"];
3367 11 : if (oHistoryFilename.GetType() == CPLJSONObject::Type::String)
3368 : {
3369 14 : osHistoryFilename = CPLFormFilenameSafe(
3370 14 : CPLGetPathSafe(osSrcFilename).c_str(),
3371 21 : oHistoryFilename.ToString().c_str(), nullptr);
3372 7 : if (CPLHasPathTraversal(oHistoryFilename.ToString().c_str()))
3373 : {
3374 0 : CPLError(CE_Warning, CPLE_AppDefined,
3375 : "Path traversal detected for History: %s. Not "
3376 : "including it in the label",
3377 0 : oHistoryFilename.ToString().c_str());
3378 0 : osHistoryFilename.clear();
3379 : }
3380 : }
3381 :
3382 33 : CPLJSONObject oStartByte = oHistory["StartByte"];
3383 11 : if (oStartByte.GetType() == CPLJSONObject::Type::Integer)
3384 : {
3385 11 : if (oStartByte.ToInteger() > 0)
3386 : {
3387 11 : nHistoryOffset =
3388 11 : static_cast<vsi_l_offset>(oStartByte.ToInteger()) - 1U;
3389 : }
3390 : }
3391 :
3392 33 : CPLJSONObject oBytes = oHistory["Bytes"];
3393 11 : if (oBytes.GetType() == CPLJSONObject::Type::Integer)
3394 : {
3395 11 : nHistorySize = static_cast<int>(oBytes.ToInteger());
3396 : }
3397 : }
3398 :
3399 25 : if (osHistoryFilename.empty())
3400 : {
3401 5 : CPLDebug("ISIS3", "Cannot find filename for source history");
3402 : }
3403 20 : else if (nHistorySize <= 0 || nHistorySize > 1000000)
3404 : {
3405 9 : CPLDebug("ISIS3", "Invalid or missing value for History.Bytes "
3406 : "for source history");
3407 : }
3408 : else
3409 : {
3410 11 : VSILFILE *fpHistory = VSIFOpenL(osHistoryFilename, "rb");
3411 11 : if (fpHistory != nullptr)
3412 : {
3413 6 : VSIFSeekL(fpHistory, nHistoryOffset, SEEK_SET);
3414 6 : osHistory.resize(nHistorySize);
3415 6 : if (VSIFReadL(&osHistory[0], nHistorySize, 1, fpHistory) != 1)
3416 : {
3417 0 : CPLError(CE_Warning, CPLE_FileIO,
3418 : "Cannot read %d bytes at offset " CPL_FRMT_GUIB
3419 : "of %s: history will not be preserved",
3420 : nHistorySize, nHistoryOffset,
3421 : osHistoryFilename.c_str());
3422 0 : osHistory.clear();
3423 : }
3424 6 : VSIFCloseL(fpHistory);
3425 : }
3426 : else
3427 : {
3428 5 : CPLError(CE_Warning, CPLE_FileIO,
3429 : "Cannot open %s: history will not be preserved",
3430 : osHistoryFilename.c_str());
3431 : }
3432 : }
3433 : }
3434 :
3435 132 : if (m_bAddGDALHistory && !m_osGDALHistory.empty())
3436 : {
3437 1 : if (!osHistory.empty())
3438 0 : osHistory += "\n";
3439 1 : osHistory += m_osGDALHistory;
3440 : }
3441 131 : else if (m_bAddGDALHistory)
3442 : {
3443 129 : if (!osHistory.empty())
3444 6 : osHistory += "\n";
3445 :
3446 258 : CPLJSONObject oHistoryObj;
3447 129 : char szFullFilename[2048] = {0};
3448 129 : if (!CPLGetExecPath(szFullFilename, sizeof(szFullFilename) - 1))
3449 0 : strcpy(szFullFilename, "unknown_program");
3450 258 : const CPLString osProgram(CPLGetBasenameSafe(szFullFilename));
3451 258 : const CPLString osPath(CPLGetPathSafe(szFullFilename));
3452 :
3453 258 : CPLJSONObject oObj;
3454 129 : oHistoryObj.Add(osProgram, oObj);
3455 :
3456 129 : oObj.Add("_type", "object");
3457 129 : oObj.Add("GdalVersion", GDALVersionInfo("RELEASE_NAME"));
3458 129 : if (osPath != ".")
3459 129 : oObj.Add("ProgramPath", osPath);
3460 129 : time_t nCurTime = time(nullptr);
3461 129 : if (nCurTime != -1)
3462 : {
3463 : struct tm mytm;
3464 129 : CPLUnixTimeToYMDHMS(nCurTime, &mytm);
3465 129 : oObj.Add("ExecutionDateTime",
3466 : CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
3467 129 : mytm.tm_year + 1900, mytm.tm_mon + 1,
3468 : mytm.tm_mday, mytm.tm_hour, mytm.tm_min,
3469 : mytm.tm_sec));
3470 : }
3471 129 : char szHostname[256] = {0};
3472 129 : if (gethostname(szHostname, sizeof(szHostname) - 1) == 0)
3473 : {
3474 129 : oObj.Add("HostName", std::string(szHostname));
3475 : }
3476 129 : const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
3477 129 : if (pszUsername == nullptr)
3478 129 : pszUsername = CPLGetConfigOption("USER", nullptr);
3479 129 : if (pszUsername != nullptr)
3480 : {
3481 0 : oObj.Add("UserName", pszUsername);
3482 : }
3483 129 : oObj.Add("Description", "GDAL conversion");
3484 :
3485 129 : CPLJSONObject oUserParameters;
3486 129 : oObj.Add("UserParameters", oUserParameters);
3487 :
3488 129 : oUserParameters.Add("_type", "group");
3489 129 : if (!m_osFromFilename.empty())
3490 : {
3491 38 : const CPLString osFromFilename = CPLGetFilename(m_osFromFilename);
3492 38 : oUserParameters.Add("FROM", osFromFilename);
3493 : }
3494 129 : if (nullptr != GetDescription())
3495 : {
3496 129 : const CPLString osToFileName = CPLGetFilename(GetDescription());
3497 129 : oUserParameters.Add("TO", osToFileName);
3498 : }
3499 129 : if (m_bForce360)
3500 1 : oUserParameters.Add("Force_360", "true");
3501 :
3502 129 : osHistory += SerializeAsPDL(oHistoryObj);
3503 : }
3504 :
3505 132 : m_osHistory = std::move(osHistory);
3506 132 : }
3507 :
3508 : /************************************************************************/
3509 : /* WriteLabel() */
3510 : /************************************************************************/
3511 :
3512 131 : void ISIS3Dataset::WriteLabel()
3513 : {
3514 131 : m_bIsLabelWritten = true;
3515 :
3516 131 : if (!m_oJSonLabel.IsValid())
3517 131 : BuildLabel();
3518 :
3519 : // Serialize label
3520 262 : CPLString osLabel(SerializeAsPDL(m_oJSonLabel));
3521 131 : osLabel += "End\n";
3522 131 : if (m_osExternalFilename.empty() && osLabel.size() < 65536)
3523 : {
3524 : // In-line labels have conventionally a minimize size of 65536 bytes
3525 : // See #2741
3526 100 : osLabel.resize(65536);
3527 : }
3528 131 : char *pszLabel = &osLabel[0];
3529 131 : const int nLabelSize = static_cast<int>(osLabel.size());
3530 :
3531 : // Hack back StartByte value
3532 : {
3533 131 : char *pszStartByte = strstr(pszLabel, pszSTARTBYTE_PLACEHOLDER);
3534 131 : if (pszStartByte != nullptr)
3535 : {
3536 100 : const char *pszOffset = CPLSPrintf("%d", 1 + nLabelSize);
3537 100 : memcpy(pszStartByte, pszOffset, strlen(pszOffset));
3538 100 : memset(pszStartByte + strlen(pszOffset), ' ',
3539 100 : strlen(pszSTARTBYTE_PLACEHOLDER) - strlen(pszOffset));
3540 : }
3541 : }
3542 :
3543 : // Hack back Label.Bytes value
3544 : {
3545 131 : char *pszLabelBytes = strstr(pszLabel, pszLABEL_BYTES_PLACEHOLDER);
3546 131 : if (pszLabelBytes != nullptr)
3547 : {
3548 131 : const char *pszBytes = CPLSPrintf("%d", nLabelSize);
3549 131 : memcpy(pszLabelBytes, pszBytes, strlen(pszBytes));
3550 131 : memset(pszLabelBytes + strlen(pszBytes), ' ',
3551 131 : strlen(pszLABEL_BYTES_PLACEHOLDER) - strlen(pszBytes));
3552 : }
3553 : }
3554 :
3555 131 : const GDALDataType eType = GetRasterBand(1)->GetRasterDataType();
3556 131 : const int nDTSize = GDALGetDataTypeSizeBytes(eType);
3557 131 : vsi_l_offset nImagePixels = 0;
3558 131 : if (m_poExternalDS == nullptr)
3559 : {
3560 113 : if (m_bIsTiled)
3561 : {
3562 7 : int nBlockXSize = 1, nBlockYSize = 1;
3563 7 : GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
3564 7 : nImagePixels = static_cast<vsi_l_offset>(nBlockXSize) *
3565 14 : nBlockYSize * nBands *
3566 7 : DIV_ROUND_UP(nRasterXSize, nBlockXSize) *
3567 7 : DIV_ROUND_UP(nRasterYSize, nBlockYSize);
3568 : }
3569 : else
3570 : {
3571 106 : nImagePixels =
3572 106 : static_cast<vsi_l_offset>(nRasterXSize) * nRasterYSize * nBands;
3573 : }
3574 : }
3575 :
3576 : // Hack back History.StartBytes value
3577 : char *pszHistoryStartByte =
3578 131 : strstr(pszLabel, pszHISTORY_STARTBYTE_PLACEHOLDER);
3579 :
3580 131 : vsi_l_offset nHistoryOffset = 0;
3581 131 : vsi_l_offset nLastOffset = 0;
3582 131 : if (pszHistoryStartByte != nullptr)
3583 : {
3584 99 : CPLAssert(m_osExternalFilename.empty());
3585 99 : nHistoryOffset = nLabelSize + nImagePixels * nDTSize;
3586 99 : nLastOffset = nHistoryOffset + m_osHistory.size();
3587 : const char *pszStartByte =
3588 99 : CPLSPrintf(CPL_FRMT_GUIB, nHistoryOffset + 1);
3589 99 : CPLAssert(strlen(pszStartByte) <
3590 : strlen(pszHISTORY_STARTBYTE_PLACEHOLDER));
3591 99 : memcpy(pszHistoryStartByte, pszStartByte, strlen(pszStartByte));
3592 99 : memset(pszHistoryStartByte + strlen(pszStartByte), ' ',
3593 99 : strlen(pszHISTORY_STARTBYTE_PLACEHOLDER) - strlen(pszStartByte));
3594 : }
3595 :
3596 : // Replace placeholders in other sections
3597 138 : for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
3598 : {
3599 7 : if (!m_aoNonPixelSections[i].osPlaceHolder.empty())
3600 : {
3601 : char *pszPlaceHolder =
3602 5 : strstr(pszLabel, m_aoNonPixelSections[i].osPlaceHolder.c_str());
3603 5 : CPLAssert(pszPlaceHolder != nullptr);
3604 : const char *pszStartByte =
3605 5 : CPLSPrintf(CPL_FRMT_GUIB, nLastOffset + 1);
3606 5 : nLastOffset += m_aoNonPixelSections[i].nSize;
3607 5 : CPLAssert(strlen(pszStartByte) <
3608 : m_aoNonPixelSections[i].osPlaceHolder.size());
3609 :
3610 5 : memcpy(pszPlaceHolder, pszStartByte, strlen(pszStartByte));
3611 5 : memset(pszPlaceHolder + strlen(pszStartByte), ' ',
3612 5 : m_aoNonPixelSections[i].osPlaceHolder.size() -
3613 5 : strlen(pszStartByte));
3614 : }
3615 : }
3616 :
3617 : // Write to final file
3618 131 : VSIFSeekL(m_fpLabel, 0, SEEK_SET);
3619 131 : VSIFWriteL(pszLabel, 1, osLabel.size(), m_fpLabel);
3620 :
3621 131 : if (m_osExternalFilename.empty())
3622 : {
3623 : // Update image offset in bands
3624 100 : if (m_bIsTiled)
3625 : {
3626 15 : for (int i = 0; i < nBands; i++)
3627 : {
3628 : ISISTiledBand *poBand =
3629 9 : reinterpret_cast<ISISTiledBand *>(GetRasterBand(i + 1));
3630 9 : poBand->m_nFirstTileOffset += nLabelSize;
3631 : }
3632 : }
3633 : else
3634 : {
3635 218 : for (int i = 0; i < nBands; i++)
3636 : {
3637 : ISIS3RawRasterBand *poBand =
3638 : reinterpret_cast<ISIS3RawRasterBand *>(
3639 124 : GetRasterBand(i + 1));
3640 124 : poBand->nImgOffset += nLabelSize;
3641 : }
3642 : }
3643 : }
3644 :
3645 131 : if (m_bInitToNodata)
3646 : {
3647 : // Initialize the image to nodata
3648 55 : const double dfNoData = GetRasterBand(1)->GetNoDataValue();
3649 55 : if (dfNoData == 0.0)
3650 : {
3651 43 : VSIFTruncateL(m_fpImage,
3652 43 : VSIFTellL(m_fpImage) + nImagePixels * nDTSize);
3653 : }
3654 12 : else if (nDTSize != 0) // to make Coverity not warn about div by 0
3655 : {
3656 12 : const int nPageSize = 4096; // Must be multiple of 4 since
3657 : // Float32 is the largest type
3658 12 : CPLAssert((nPageSize % nDTSize) == 0);
3659 12 : const int nMaxPerPage = nPageSize / nDTSize;
3660 12 : GByte *pabyTemp = static_cast<GByte *>(CPLMalloc(nPageSize));
3661 12 : GDALCopyWords(&dfNoData, GDT_Float64, 0, pabyTemp, eType, nDTSize,
3662 : nMaxPerPage);
3663 : #ifdef CPL_MSB
3664 : GDALSwapWords(pabyTemp, nDTSize, nMaxPerPage, nDTSize);
3665 : #endif
3666 80 : for (vsi_l_offset i = 0; i < nImagePixels; i += nMaxPerPage)
3667 : {
3668 : int n;
3669 68 : if (i + nMaxPerPage <= nImagePixels)
3670 56 : n = nMaxPerPage;
3671 : else
3672 12 : n = static_cast<int>(nImagePixels - i);
3673 68 : if (VSIFWriteL(pabyTemp, static_cast<size_t>(n) * nDTSize, 1,
3674 68 : m_fpImage) != 1)
3675 : {
3676 0 : CPLError(CE_Failure, CPLE_FileIO,
3677 : "Cannot initialize imagery to null");
3678 0 : break;
3679 : }
3680 : }
3681 :
3682 12 : CPLFree(pabyTemp);
3683 : }
3684 : }
3685 :
3686 : // Write history
3687 131 : if (!m_osHistory.empty())
3688 : {
3689 129 : if (m_osExternalFilename.empty())
3690 : {
3691 99 : VSIFSeekL(m_fpLabel, nHistoryOffset, SEEK_SET);
3692 99 : VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), m_fpLabel);
3693 : }
3694 : else
3695 : {
3696 60 : CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
3697 30 : osFilename += ".History.IsisCube";
3698 60 : osFilename = CPLFormFilenameSafe(
3699 60 : CPLGetPathSafe(GetDescription()).c_str(), osFilename, nullptr);
3700 30 : VSILFILE *fp = VSIFOpenL(osFilename, "wb");
3701 30 : if (fp)
3702 : {
3703 30 : m_aosAdditionalFiles.AddString(osFilename);
3704 :
3705 30 : VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), fp);
3706 30 : VSIFCloseL(fp);
3707 : }
3708 : else
3709 : {
3710 0 : CPLError(CE_Warning, CPLE_FileIO, "Cannot write %s",
3711 : osFilename.c_str());
3712 : }
3713 : }
3714 : }
3715 :
3716 : // Write other non pixel sections
3717 138 : for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
3718 : {
3719 : VSILFILE *fpSrc =
3720 7 : VSIFOpenL(m_aoNonPixelSections[i].osSrcFilename, "rb");
3721 7 : if (fpSrc == nullptr)
3722 : {
3723 0 : CPLError(CE_Warning, CPLE_FileIO, "Cannot open %s",
3724 0 : m_aoNonPixelSections[i].osSrcFilename.c_str());
3725 0 : continue;
3726 : }
3727 :
3728 7 : VSILFILE *fpDest = m_fpLabel;
3729 7 : if (!m_aoNonPixelSections[i].osDstFilename.empty())
3730 : {
3731 2 : fpDest = VSIFOpenL(m_aoNonPixelSections[i].osDstFilename, "wb");
3732 2 : if (fpDest == nullptr)
3733 : {
3734 0 : CPLError(CE_Warning, CPLE_FileIO, "Cannot create %s",
3735 0 : m_aoNonPixelSections[i].osDstFilename.c_str());
3736 0 : VSIFCloseL(fpSrc);
3737 0 : continue;
3738 : }
3739 :
3740 : m_aosAdditionalFiles.AddString(
3741 2 : m_aoNonPixelSections[i].osDstFilename);
3742 : }
3743 :
3744 7 : VSIFSeekL(fpSrc, m_aoNonPixelSections[i].nSrcOffset, SEEK_SET);
3745 : GByte abyBuffer[4096];
3746 7 : vsi_l_offset nRemaining = m_aoNonPixelSections[i].nSize;
3747 14 : while (nRemaining)
3748 : {
3749 7 : size_t nToRead = 4096;
3750 7 : if (nRemaining < nToRead)
3751 7 : nToRead = static_cast<size_t>(nRemaining);
3752 7 : size_t nRead = VSIFReadL(abyBuffer, 1, nToRead, fpSrc);
3753 7 : if (nRead != nToRead)
3754 : {
3755 0 : CPLError(CE_Warning, CPLE_FileIO,
3756 : "Could not read " CPL_FRMT_GUIB " bytes from %s",
3757 0 : m_aoNonPixelSections[i].nSize,
3758 0 : m_aoNonPixelSections[i].osSrcFilename.c_str());
3759 0 : break;
3760 : }
3761 7 : VSIFWriteL(abyBuffer, 1, nRead, fpDest);
3762 7 : nRemaining -= nRead;
3763 : }
3764 :
3765 7 : VSIFCloseL(fpSrc);
3766 7 : if (fpDest != m_fpLabel)
3767 2 : VSIFCloseL(fpDest);
3768 : }
3769 131 : }
3770 :
3771 : /************************************************************************/
3772 : /* SerializeAsPDL() */
3773 : /************************************************************************/
3774 :
3775 260 : CPLString ISIS3Dataset::SerializeAsPDL(const CPLJSONObject &oObj)
3776 : {
3777 520 : const CPLString osTmpFile(VSIMemGenerateHiddenFilename("isis3_pdl"));
3778 260 : VSILFILE *fpTmp = VSIFOpenL(osTmpFile, "wb+");
3779 260 : SerializeAsPDL(fpTmp, oObj);
3780 260 : VSIFCloseL(fpTmp);
3781 : CPLString osContent(reinterpret_cast<char *>(
3782 260 : VSIGetMemFileBuffer(osTmpFile, nullptr, FALSE)));
3783 260 : VSIUnlink(osTmpFile);
3784 520 : return osContent;
3785 : }
3786 :
3787 : /************************************************************************/
3788 : /* SerializeAsPDL() */
3789 : /************************************************************************/
3790 :
3791 : constexpr size_t WIDTH = 79;
3792 :
3793 1398 : void ISIS3Dataset::SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
3794 : int nDepth)
3795 : {
3796 2796 : CPLString osIndentation;
3797 3402 : for (int i = 0; i < nDepth; i++)
3798 2004 : osIndentation += " ";
3799 :
3800 2796 : std::vector<CPLJSONObject> aoChildren = oObj.GetChildren();
3801 1398 : size_t nMaxKeyLength = 0;
3802 2796 : std::vector<std::pair<CPLString, CPLJSONObject>> aoChildren2;
3803 7697 : for (const CPLJSONObject &oChild : aoChildren)
3804 : {
3805 6299 : const CPLString osKey = oChild.GetName();
3806 11460 : if (EQUAL(osKey, "_type") || EQUAL(osKey, "_container_name") ||
3807 11460 : EQUAL(osKey, "_filename") || EQUAL(osKey, "_data"))
3808 : {
3809 1307 : continue;
3810 : }
3811 :
3812 4992 : const auto eType = oChild.GetType();
3813 4992 : if (eType == CPLJSONObject::Type::String ||
3814 2256 : eType == CPLJSONObject::Type::Integer ||
3815 1462 : eType == CPLJSONObject::Type::Double ||
3816 : eType == CPLJSONObject::Type::Array)
3817 : {
3818 3540 : aoChildren2.emplace_back(osKey, oChild);
3819 3540 : if (osKey.size() > nMaxKeyLength)
3820 : {
3821 1680 : nMaxKeyLength = osKey.size();
3822 : }
3823 : }
3824 1452 : else if (eType == CPLJSONObject::Type::Object)
3825 : {
3826 4356 : CPLJSONObject oValue = oChild.GetObj("value");
3827 4356 : CPLJSONObject oUnit = oChild.GetObj("unit");
3828 1695 : if (oValue.IsValid() &&
3829 243 : oUnit.GetType() == CPLJSONObject::Type::String)
3830 : {
3831 243 : aoChildren2.emplace_back(osKey, oChild);
3832 243 : if (osKey.size() > nMaxKeyLength)
3833 : {
3834 64 : nMaxKeyLength = osKey.size();
3835 : }
3836 : }
3837 1209 : else if (oChild.GetObj("values").GetType() ==
3838 : CPLJSONObject::Type::Array)
3839 : {
3840 4 : if (osKey.size() > nMaxKeyLength)
3841 : {
3842 1 : nMaxKeyLength = osKey.size();
3843 : }
3844 13 : for (const auto &oSubChild : oChild.GetObj("values").ToArray())
3845 : {
3846 9 : aoChildren2.emplace_back(osKey, oSubChild);
3847 : }
3848 : }
3849 : else
3850 : {
3851 1205 : aoChildren2.emplace_back(osKey, oChild);
3852 : }
3853 : }
3854 : else
3855 : {
3856 0 : aoChildren2.emplace_back(osKey, oChild);
3857 : }
3858 : }
3859 :
3860 6395 : for (const auto &[osKey, oChild] : aoChildren2)
3861 : {
3862 4997 : if (STARTS_WITH(osKey, "_comment"))
3863 : {
3864 1 : if (oChild.GetType() == CPLJSONObject::Type::String)
3865 : {
3866 1 : VSIFPrintfL(fp, "#%s\n", oChild.ToString().c_str());
3867 : }
3868 1 : continue;
3869 : }
3870 9992 : CPLString osPadding;
3871 4996 : size_t nLen = osKey.size();
3872 4996 : if (nLen < nMaxKeyLength)
3873 : {
3874 3047 : osPadding.append(nMaxKeyLength - nLen, ' ');
3875 : }
3876 :
3877 4996 : const auto eType = oChild.GetType();
3878 4996 : if (eType == CPLJSONObject::Type::Object)
3879 : {
3880 4350 : CPLJSONObject oType = oChild.GetObj("_type");
3881 4350 : CPLJSONObject oContainerName = oChild.GetObj("_container_name");
3882 2900 : CPLString osContainerName = osKey;
3883 1450 : if (oContainerName.GetType() == CPLJSONObject::Type::String)
3884 : {
3885 140 : osContainerName = oContainerName.ToString();
3886 : }
3887 1450 : if (oType.GetType() == CPLJSONObject::Type::String)
3888 : {
3889 3414 : const CPLString osType = oType.ToString();
3890 1138 : if (EQUAL(osType, "Object"))
3891 : {
3892 665 : if (nDepth == 0 && VSIFTellL(fp) != 0)
3893 274 : VSIFPrintfL(fp, "\n");
3894 665 : VSIFPrintfL(fp, "%sObject = %s\n", osIndentation.c_str(),
3895 : osContainerName.c_str());
3896 665 : SerializeAsPDL(fp, oChild, nDepth + 1);
3897 665 : VSIFPrintfL(fp, "%sEnd_Object\n", osIndentation.c_str());
3898 : }
3899 473 : else if (EQUAL(osType, "Group"))
3900 : {
3901 473 : VSIFPrintfL(fp, "\n");
3902 473 : VSIFPrintfL(fp, "%sGroup = %s\n", osIndentation.c_str(),
3903 : osContainerName.c_str());
3904 473 : SerializeAsPDL(fp, oChild, nDepth + 1);
3905 473 : VSIFPrintfL(fp, "%sEnd_Group\n", osIndentation.c_str());
3906 : }
3907 : }
3908 : else
3909 : {
3910 936 : CPLJSONObject oValue = oChild.GetObj("value");
3911 936 : CPLJSONObject oUnit = oChild.GetObj("unit");
3912 557 : if (oValue.IsValid() &&
3913 245 : oUnit.GetType() == CPLJSONObject::Type::String)
3914 : {
3915 735 : const CPLString osUnit = oUnit.ToString();
3916 245 : const auto eValueType = oValue.GetType();
3917 245 : if (eValueType == CPLJSONObject::Type::Integer)
3918 : {
3919 3 : VSIFPrintfL(fp, "%s%s%s = %d <%s>\n",
3920 : osIndentation.c_str(), osKey.c_str(),
3921 : osPadding.c_str(), oValue.ToInteger(),
3922 : osUnit.c_str());
3923 : }
3924 242 : else if (eValueType == CPLJSONObject::Type::Double)
3925 : {
3926 241 : const double dfVal = oValue.ToDouble();
3927 241 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
3928 241 : static_cast<int>(dfVal) == dfVal)
3929 : {
3930 90 : VSIFPrintfL(fp, "%s%s%s = %d.0 <%s>\n",
3931 : osIndentation.c_str(), osKey.c_str(),
3932 : osPadding.c_str(),
3933 : static_cast<int>(dfVal),
3934 : osUnit.c_str());
3935 : }
3936 : else
3937 : {
3938 151 : VSIFPrintfL(fp, "%s%s%s = %.17g <%s>\n",
3939 : osIndentation.c_str(), osKey.c_str(),
3940 : osPadding.c_str(), dfVal,
3941 : osUnit.c_str());
3942 : }
3943 : }
3944 : }
3945 : }
3946 : }
3947 3546 : else if (eType == CPLJSONObject::Type::String)
3948 : {
3949 6141 : CPLString osVal = oChild.ToString();
3950 2047 : const char *pszVal = osVal.c_str();
3951 2047 : if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
3952 1909 : strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
3953 : {
3954 138 : osVal.replaceAll("\\n", "\n");
3955 138 : osVal.replaceAll("\\r", "\r");
3956 138 : VSIFPrintfL(fp, "%s%s%s = \"%s\"\n", osIndentation.c_str(),
3957 : osKey.c_str(), osPadding.c_str(), osVal.c_str());
3958 : }
3959 : else
3960 : {
3961 1909 : if (osIndentation.size() + osKey.size() + osPadding.size() +
3962 1909 : strlen(" = ") + strlen(pszVal) >
3963 1910 : WIDTH &&
3964 1 : osIndentation.size() + osKey.size() + osPadding.size() +
3965 : strlen(" = ") <
3966 : WIDTH)
3967 : {
3968 1 : size_t nFirstPos = osIndentation.size() + osKey.size() +
3969 1 : osPadding.size() + strlen(" = ");
3970 1 : VSIFPrintfL(fp, "%s%s%s = ", osIndentation.c_str(),
3971 : osKey.c_str(), osPadding.c_str());
3972 1 : size_t nCurPos = nFirstPos;
3973 96 : for (int j = 0; pszVal[j] != '\0'; j++)
3974 : {
3975 95 : nCurPos++;
3976 95 : if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
3977 : {
3978 1 : VSIFPrintfL(fp, "-\n");
3979 15 : for (size_t k = 0; k < nFirstPos; k++)
3980 : {
3981 14 : const char chSpace = ' ';
3982 14 : VSIFWriteL(&chSpace, 1, 1, fp);
3983 : }
3984 1 : nCurPos = nFirstPos + 1;
3985 : }
3986 95 : VSIFWriteL(&pszVal[j], 1, 1, fp);
3987 : }
3988 1 : VSIFPrintfL(fp, "\n");
3989 : }
3990 : else
3991 : {
3992 1908 : VSIFPrintfL(fp, "%s%s%s = %s\n", osIndentation.c_str(),
3993 : osKey.c_str(), osPadding.c_str(), pszVal);
3994 : }
3995 : }
3996 : }
3997 1499 : else if (eType == CPLJSONObject::Type::Integer)
3998 : {
3999 693 : const int nVal = oChild.ToInteger();
4000 693 : VSIFPrintfL(fp, "%s%s%s = %d\n", osIndentation.c_str(),
4001 : osKey.c_str(), osPadding.c_str(), nVal);
4002 : }
4003 806 : else if (eType == CPLJSONObject::Type::Double)
4004 : {
4005 794 : const double dfVal = oChild.ToDouble();
4006 794 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
4007 794 : static_cast<int>(dfVal) == dfVal)
4008 : {
4009 550 : VSIFPrintfL(fp, "%s%s%s = %d.0\n", osIndentation.c_str(),
4010 : osKey.c_str(), osPadding.c_str(),
4011 : static_cast<int>(dfVal));
4012 : }
4013 : else
4014 : {
4015 244 : VSIFPrintfL(fp, "%s%s%s = %.17g\n", osIndentation.c_str(),
4016 : osKey.c_str(), osPadding.c_str(), dfVal);
4017 : }
4018 : }
4019 12 : else if (eType == CPLJSONObject::Type::Array)
4020 : {
4021 24 : CPLJSONArray oArrayItem(oChild);
4022 12 : const int nLength = oArrayItem.Size();
4023 12 : size_t nFirstPos = osIndentation.size() + osKey.size() +
4024 12 : osPadding.size() + strlen(" = (");
4025 12 : VSIFPrintfL(fp, "%s%s%s = (", osIndentation.c_str(), osKey.c_str(),
4026 : osPadding.c_str());
4027 12 : size_t nCurPos = nFirstPos;
4028 :
4029 57 : for (int idx = 0; idx < nLength; idx++)
4030 : {
4031 90 : CPLJSONObject oItem = oArrayItem[idx];
4032 45 : const auto eArrayItemType = oItem.GetType();
4033 :
4034 : const auto outputArrayVal =
4035 244 : [fp, idx, nFirstPos, &nCurPos](const std::string &osVal)
4036 : {
4037 31 : const size_t nValLen = osVal.size();
4038 31 : if (nFirstPos < WIDTH && idx > 0 &&
4039 23 : nCurPos + nValLen > WIDTH)
4040 : {
4041 2 : VSIFPrintfL(fp, "\n");
4042 32 : for (size_t j = 0; j < nFirstPos; j++)
4043 : {
4044 30 : constexpr char chSpace = ' ';
4045 30 : VSIFWriteL(&chSpace, 1, 1, fp);
4046 : }
4047 2 : nCurPos = nFirstPos;
4048 : }
4049 31 : VSIFPrintfL(fp, "%s", osVal.c_str());
4050 31 : nCurPos += nValLen;
4051 31 : };
4052 :
4053 45 : if (eArrayItemType == CPLJSONObject::Type::Object)
4054 : {
4055 9 : const auto oValue = oItem["value"];
4056 9 : const auto oUnit = oItem["unit"];
4057 6 : if (oValue.IsValid() && oUnit.IsValid() &&
4058 3 : (oValue.GetType() == CPLJSONObject::Type::Integer ||
4059 1 : oValue.GetType() == CPLJSONObject::Type::Double))
4060 : {
4061 3 : if (oValue.GetType() == CPLJSONObject::Type::Integer)
4062 : {
4063 2 : const int nVal = oValue.ToInteger();
4064 6 : outputArrayVal(CPLSPrintf(
4065 4 : "%d <%s>", nVal, oUnit.ToString().c_str()));
4066 : }
4067 : else
4068 : {
4069 1 : const double dfVal = oValue.ToDouble();
4070 1 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
4071 1 : static_cast<int>(dfVal) == dfVal)
4072 : {
4073 0 : outputArrayVal(CPLSPrintf(
4074 : "%d.0 <%s>", static_cast<int>(dfVal),
4075 0 : oUnit.ToString().c_str()));
4076 : }
4077 : else
4078 : {
4079 3 : outputArrayVal(
4080 : CPLSPrintf("%.17g <%s>", dfVal,
4081 2 : oUnit.ToString().c_str()));
4082 : }
4083 : }
4084 : }
4085 : else
4086 : {
4087 0 : CPLError(CE_Warning, CPLE_AppDefined,
4088 : "Invalid JSON object");
4089 : }
4090 : }
4091 42 : else if (eArrayItemType == CPLJSONObject::Type::String)
4092 : {
4093 42 : CPLString osVal = oItem.ToString();
4094 14 : const char *pszVal = osVal.c_str();
4095 14 : if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
4096 7 : strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
4097 : {
4098 7 : osVal.replaceAll("\\n", "\n");
4099 7 : osVal.replaceAll("\\r", "\r");
4100 7 : VSIFPrintfL(fp, "\"%s\"", osVal.c_str());
4101 : }
4102 7 : else if (nFirstPos < WIDTH &&
4103 7 : nCurPos + strlen(pszVal) > WIDTH)
4104 : {
4105 1 : if (idx > 0)
4106 : {
4107 1 : VSIFPrintfL(fp, "\n");
4108 16 : for (size_t j = 0; j < nFirstPos; j++)
4109 : {
4110 15 : const char chSpace = ' ';
4111 15 : VSIFWriteL(&chSpace, 1, 1, fp);
4112 : }
4113 1 : nCurPos = nFirstPos;
4114 : }
4115 :
4116 102 : for (int j = 0; pszVal[j] != '\0'; j++)
4117 : {
4118 101 : nCurPos++;
4119 101 : if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
4120 : {
4121 1 : VSIFPrintfL(fp, "-\n");
4122 16 : for (size_t k = 0; k < nFirstPos; k++)
4123 : {
4124 15 : const char chSpace = ' ';
4125 15 : VSIFWriteL(&chSpace, 1, 1, fp);
4126 : }
4127 1 : nCurPos = nFirstPos + 1;
4128 : }
4129 101 : VSIFWriteL(&pszVal[j], 1, 1, fp);
4130 1 : }
4131 : }
4132 : else
4133 : {
4134 6 : VSIFPrintfL(fp, "%s", pszVal);
4135 6 : nCurPos += strlen(pszVal);
4136 : }
4137 : }
4138 28 : else if (eArrayItemType == CPLJSONObject::Type::Integer)
4139 : {
4140 19 : const int nVal = oItem.ToInteger();
4141 19 : outputArrayVal(CPLSPrintf("%d", nVal));
4142 : }
4143 9 : else if (eArrayItemType == CPLJSONObject::Type::Double)
4144 : {
4145 9 : const double dfVal = oItem.ToDouble();
4146 18 : CPLString osVal;
4147 9 : if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
4148 9 : static_cast<int>(dfVal) == dfVal)
4149 : {
4150 8 : osVal = CPLSPrintf("%d.0", static_cast<int>(dfVal));
4151 : }
4152 : else
4153 : {
4154 1 : osVal = CPLSPrintf("%.17g", dfVal);
4155 : }
4156 9 : outputArrayVal(osVal);
4157 : }
4158 45 : if (idx < nLength - 1)
4159 : {
4160 33 : VSIFPrintfL(fp, ", ");
4161 33 : nCurPos += 2;
4162 : }
4163 : }
4164 12 : VSIFPrintfL(fp, ")\n");
4165 : }
4166 : }
4167 1398 : }
4168 :
4169 : /************************************************************************/
4170 : /* Create() */
4171 : /************************************************************************/
4172 :
4173 165 : GDALDataset *ISIS3Dataset::Create(const char *pszFilename, int nXSize,
4174 : int nYSize, int nBandsIn, GDALDataType eType,
4175 : CSLConstList papszOptions)
4176 : {
4177 165 : if (eType != GDT_UInt8 && eType != GDT_UInt16 && eType != GDT_Int16 &&
4178 : eType != GDT_Float32)
4179 : {
4180 27 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
4181 27 : return nullptr;
4182 : }
4183 138 : if (nBandsIn == 0 || nBandsIn > 32767)
4184 : {
4185 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
4186 1 : return nullptr;
4187 : }
4188 :
4189 : const char *pszDataLocation =
4190 137 : CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
4191 137 : const bool bIsTiled = CPLFetchBool(papszOptions, "TILED", false);
4192 : const int nBlockXSize = std::max(
4193 137 : 1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256")));
4194 : const int nBlockYSize = std::max(
4195 137 : 1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256")));
4196 171 : if (!EQUAL(pszDataLocation, "LABEL") &&
4197 171 : !EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "LBL"))
4198 : {
4199 1 : CPLError(CE_Failure, CPLE_NotSupported,
4200 : "For DATA_LOCATION=%s, "
4201 : "the main filename should have a .lbl extension",
4202 : pszDataLocation);
4203 1 : return nullptr;
4204 : }
4205 :
4206 : const char *pszPermission =
4207 136 : VSISupportsRandomWrite(pszFilename, true) ? "wb+" : "wb";
4208 :
4209 136 : VSILFILE *fp = VSIFOpenExL(pszFilename, pszPermission, true);
4210 136 : if (fp == nullptr)
4211 : {
4212 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
4213 : VSIGetLastErrorMsg());
4214 3 : return nullptr;
4215 : }
4216 133 : VSILFILE *fpImage = nullptr;
4217 266 : std::string osExternalFilename;
4218 133 : GDALDataset *poExternalDS = nullptr;
4219 133 : bool bGeoTIFFAsRegularExternal = false;
4220 133 : if (EQUAL(pszDataLocation, "EXTERNAL"))
4221 : {
4222 : osExternalFilename = CSLFetchNameValueDef(
4223 : papszOptions, "EXTERNAL_FILENAME",
4224 14 : CPLResetExtensionSafe(pszFilename, "cub").c_str());
4225 14 : fpImage = VSIFOpenExL(osExternalFilename.c_str(), pszPermission, true);
4226 14 : if (fpImage == nullptr)
4227 : {
4228 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
4229 : osExternalFilename.c_str(), VSIGetLastErrorMsg());
4230 1 : VSIFCloseL(fp);
4231 1 : return nullptr;
4232 : }
4233 : }
4234 119 : else if (EQUAL(pszDataLocation, "GEOTIFF"))
4235 : {
4236 : osExternalFilename = CSLFetchNameValueDef(
4237 : papszOptions, "EXTERNAL_FILENAME",
4238 19 : CPLResetExtensionSafe(pszFilename, "tif").c_str());
4239 : GDALDriver *poDrv =
4240 19 : static_cast<GDALDriver *>(GDALGetDriverByName("GTiff"));
4241 19 : if (poDrv == nullptr)
4242 : {
4243 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GTiff driver");
4244 0 : VSIFCloseL(fp);
4245 0 : return nullptr;
4246 : }
4247 19 : char **papszGTiffOptions = nullptr;
4248 : papszGTiffOptions =
4249 19 : CSLSetNameValue(papszGTiffOptions, "ENDIANNESS", "LITTLE");
4250 19 : if (bIsTiled)
4251 : {
4252 : papszGTiffOptions =
4253 3 : CSLSetNameValue(papszGTiffOptions, "TILED", "YES");
4254 3 : papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKXSIZE",
4255 : CPLSPrintf("%d", nBlockXSize));
4256 3 : papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE",
4257 : CPLSPrintf("%d", nBlockYSize));
4258 : }
4259 : const char *pszGTiffOptions =
4260 19 : CSLFetchNameValueDef(papszOptions, "GEOTIFF_OPTIONS", "");
4261 19 : char **papszTokens = CSLTokenizeString2(pszGTiffOptions, ",", 0);
4262 28 : for (int i = 0; papszTokens[i] != nullptr; i++)
4263 : {
4264 9 : papszGTiffOptions = CSLAddString(papszGTiffOptions, papszTokens[i]);
4265 : }
4266 19 : CSLDestroy(papszTokens);
4267 :
4268 : // If the user didn't specify any compression and
4269 : // GEOTIFF_AS_REGULAR_EXTERNAL is set (or unspecified), then the
4270 : // GeoTIFF file can be seen as a regular external raw file, provided
4271 : // we make some provision on its organization.
4272 29 : if (CSLFetchNameValue(papszGTiffOptions, "COMPRESS") == nullptr &&
4273 10 : CPLFetchBool(papszOptions, "GEOTIFF_AS_REGULAR_EXTERNAL", true))
4274 : {
4275 9 : bGeoTIFFAsRegularExternal = true;
4276 : papszGTiffOptions =
4277 9 : CSLSetNameValue(papszGTiffOptions, "INTERLEAVE", "BAND");
4278 : // Will make sure that our blocks at nodata are not optimized
4279 : // away but indeed well written
4280 9 : papszGTiffOptions = CSLSetNameValue(
4281 : papszGTiffOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "YES");
4282 9 : if (!bIsTiled && nBandsIn > 1)
4283 : {
4284 : papszGTiffOptions =
4285 1 : CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE", "1");
4286 : }
4287 : }
4288 :
4289 19 : poExternalDS = poDrv->Create(osExternalFilename.c_str(), nXSize, nYSize,
4290 : nBandsIn, eType, papszGTiffOptions);
4291 19 : CSLDestroy(papszGTiffOptions);
4292 19 : if (poExternalDS == nullptr)
4293 : {
4294 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
4295 : osExternalFilename.c_str());
4296 1 : VSIFCloseL(fp);
4297 1 : return nullptr;
4298 : }
4299 : }
4300 :
4301 131 : ISIS3Dataset *poDS = new ISIS3Dataset();
4302 131 : poDS->SetDescription(pszFilename);
4303 131 : poDS->eAccess = GA_Update;
4304 131 : poDS->nRasterXSize = nXSize;
4305 131 : poDS->nRasterYSize = nYSize;
4306 131 : poDS->m_osExternalFilename = std::move(osExternalFilename);
4307 131 : poDS->m_poExternalDS = poExternalDS;
4308 131 : poDS->m_bGeoTIFFAsRegularExternal = bGeoTIFFAsRegularExternal;
4309 131 : if (bGeoTIFFAsRegularExternal)
4310 8 : poDS->m_bGeoTIFFInitDone = false;
4311 131 : poDS->m_fpLabel = fp;
4312 131 : poDS->m_fpImage = fpImage ? fpImage : fp;
4313 131 : poDS->m_bIsLabelWritten = false;
4314 131 : poDS->m_bIsTiled = bIsTiled;
4315 131 : poDS->m_bInitToNodata = (poDS->m_poExternalDS == nullptr);
4316 131 : poDS->m_osComment = CSLFetchNameValueDef(papszOptions, "COMMENT", "");
4317 : poDS->m_osLatitudeType =
4318 131 : CSLFetchNameValueDef(papszOptions, "LATITUDE_TYPE", "");
4319 : poDS->m_osLongitudeDirection =
4320 131 : CSLFetchNameValueDef(papszOptions, "LONGITUDE_DIRECTION", "");
4321 : poDS->m_osTargetName =
4322 131 : CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
4323 131 : poDS->m_bForce360 = CPLFetchBool(papszOptions, "FORCE_360", false);
4324 131 : poDS->m_bWriteBoundingDegrees =
4325 131 : CPLFetchBool(papszOptions, "WRITE_BOUNDING_DEGREES", true);
4326 : poDS->m_osBoundingDegrees =
4327 131 : CSLFetchNameValueDef(papszOptions, "BOUNDING_DEGREES", "");
4328 131 : poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
4329 131 : poDS->m_bUseSrcMapping =
4330 131 : CPLFetchBool(papszOptions, "USE_SRC_MAPPING", false);
4331 131 : poDS->m_bUseSrcHistory =
4332 131 : CPLFetchBool(papszOptions, "USE_SRC_HISTORY", true);
4333 131 : poDS->m_bAddGDALHistory =
4334 131 : CPLFetchBool(papszOptions, "ADD_GDAL_HISTORY", true);
4335 131 : if (poDS->m_bAddGDALHistory)
4336 : {
4337 : poDS->m_osGDALHistory =
4338 129 : CSLFetchNameValueDef(papszOptions, "GDAL_HISTORY", "");
4339 : }
4340 131 : const double dfNoData = (eType == GDT_UInt8) ? ISIS3_NULL1
4341 : : (eType == GDT_UInt16) ? ISIS3_NULLU2
4342 : : (eType == GDT_Int16)
4343 : ? ISIS3_NULL2
4344 : :
4345 : /*(eType == GDT_Float32) ?*/ ISIS3_NULL4;
4346 :
4347 303 : for (int i = 0; i < nBandsIn; i++)
4348 : {
4349 172 : GDALRasterBand *poBand = nullptr;
4350 :
4351 172 : if (poDS->m_poExternalDS != nullptr)
4352 : {
4353 : ISIS3WrapperRasterBand *poISISBand = new ISIS3WrapperRasterBand(
4354 24 : poDS->m_poExternalDS->GetRasterBand(i + 1));
4355 24 : poBand = poISISBand;
4356 : }
4357 148 : else if (bIsTiled)
4358 : {
4359 : ISISTiledBand *poISISBand = new ISISTiledBand(
4360 : poDS, poDS->m_fpImage, i + 1, eType, nBlockXSize, nBlockYSize,
4361 : 0, // nSkipBytes, to be hacked
4362 : // afterwards for in-label imagery
4363 10 : 0, 0, CPL_IS_LSB);
4364 :
4365 10 : poBand = poISISBand;
4366 : }
4367 : else
4368 : {
4369 138 : const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
4370 138 : const int nLineOffset = nPixelOffset * nXSize;
4371 138 : const vsi_l_offset nBandOffset =
4372 138 : static_cast<vsi_l_offset>(nLineOffset) * nYSize;
4373 : ISIS3RawRasterBand *poISISBand = new ISIS3RawRasterBand(
4374 : poDS, i + 1, poDS->m_fpImage,
4375 138 : nBandOffset * i, // nImgOffset, to be
4376 : // hacked afterwards for in-label imagery
4377 138 : nPixelOffset, nLineOffset, eType, CPL_IS_LSB);
4378 :
4379 138 : poBand = poISISBand;
4380 : }
4381 172 : poDS->SetBand(i + 1, poBand);
4382 172 : poBand->SetNoDataValue(dfNoData);
4383 : }
4384 :
4385 131 : return poDS;
4386 : }
4387 :
4388 : /************************************************************************/
4389 : /* GetUnderlyingDataset() */
4390 : /************************************************************************/
4391 :
4392 79 : static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
4393 : {
4394 158 : if (poSrcDS->GetDriver() != nullptr &&
4395 79 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
4396 : {
4397 2 : VRTDataset *poVRTDS = cpl::down_cast<VRTDataset *>(poSrcDS);
4398 2 : poSrcDS = poVRTDS->GetSingleSimpleSource();
4399 : }
4400 :
4401 79 : return poSrcDS;
4402 : }
4403 :
4404 : /************************************************************************/
4405 : /* CreateCopy() */
4406 : /************************************************************************/
4407 :
4408 79 : GDALDataset *ISIS3Dataset::CreateCopy(const char *pszFilename,
4409 : GDALDataset *poSrcDS, int /*bStrict*/,
4410 : CSLConstList papszOptions,
4411 : GDALProgressFunc pfnProgress,
4412 : void *pProgressData)
4413 : {
4414 : const char *pszDataLocation =
4415 79 : CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
4416 79 : GDALDataset *poSrcUnderlyingDS = GetUnderlyingDataset(poSrcDS);
4417 79 : if (poSrcUnderlyingDS == nullptr)
4418 2 : poSrcUnderlyingDS = poSrcDS;
4419 89 : if (EQUAL(pszDataLocation, "GEOTIFF") &&
4420 10 : strcmp(poSrcUnderlyingDS->GetDescription(),
4421 : CSLFetchNameValueDef(
4422 : papszOptions, "EXTERNAL_FILENAME",
4423 89 : CPLResetExtensionSafe(pszFilename, "tif").c_str())) == 0)
4424 : {
4425 1 : CPLError(CE_Failure, CPLE_NotSupported,
4426 : "Output file has same name as input file");
4427 1 : return nullptr;
4428 : }
4429 78 : if (poSrcDS->GetRasterCount() == 0)
4430 : {
4431 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
4432 1 : return nullptr;
4433 : }
4434 :
4435 77 : const int nXSize = poSrcDS->GetRasterXSize();
4436 77 : const int nYSize = poSrcDS->GetRasterYSize();
4437 77 : const int nBands = poSrcDS->GetRasterCount();
4438 77 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
4439 : ISIS3Dataset *poDS = reinterpret_cast<ISIS3Dataset *>(
4440 77 : Create(pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
4441 77 : if (poDS == nullptr)
4442 10 : return nullptr;
4443 67 : poDS->m_osFromFilename = poSrcUnderlyingDS->GetDescription();
4444 :
4445 67 : GDALGeoTransform gt;
4446 67 : if (poSrcDS->GetGeoTransform(gt) == CE_None && gt != GDALGeoTransform())
4447 : {
4448 40 : poDS->SetGeoTransform(gt);
4449 : }
4450 :
4451 67 : auto poSrcSRS = poSrcDS->GetSpatialRef();
4452 67 : if (poSrcSRS)
4453 : {
4454 40 : poDS->SetSpatialRef(poSrcSRS);
4455 : }
4456 :
4457 159 : for (int i = 1; i <= nBands; i++)
4458 : {
4459 92 : const double dfOffset = poSrcDS->GetRasterBand(i)->GetOffset();
4460 92 : if (dfOffset != 0.0)
4461 1 : poDS->GetRasterBand(i)->SetOffset(dfOffset);
4462 :
4463 92 : const double dfScale = poSrcDS->GetRasterBand(i)->GetScale();
4464 92 : if (dfScale != 1.0)
4465 1 : poDS->GetRasterBand(i)->SetScale(dfScale);
4466 : }
4467 :
4468 : // Do we need to remap nodata ?
4469 67 : int bHasNoData = FALSE;
4470 67 : poDS->m_dfSrcNoData =
4471 67 : poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHasNoData);
4472 67 : poDS->m_bHasSrcNoData = CPL_TO_BOOL(bHasNoData);
4473 :
4474 67 : if (poDS->m_bUseSrcLabel)
4475 : {
4476 59 : CSLConstList papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
4477 59 : if (papszMD_ISIS3 != nullptr)
4478 : {
4479 21 : poDS->SetMetadata(papszMD_ISIS3, "json:ISIS3");
4480 : }
4481 : }
4482 :
4483 : // We don't need to initialize the imagery as we are going to copy it
4484 : // completely
4485 67 : poDS->m_bInitToNodata = false;
4486 67 : CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS, nullptr,
4487 : pfnProgress, pProgressData);
4488 67 : poDS->FlushCache(false);
4489 67 : poDS->m_bHasSrcNoData = false;
4490 67 : if (eErr != CE_None)
4491 : {
4492 11 : delete poDS;
4493 11 : return nullptr;
4494 : }
4495 :
4496 56 : return poDS;
4497 : }
4498 :
4499 : /************************************************************************/
4500 : /* GDALRegister_ISIS3() */
4501 : /************************************************************************/
4502 :
4503 2063 : void GDALRegister_ISIS3()
4504 :
4505 : {
4506 2063 : if (GDALGetDriverByName(ISIS3_DRIVER_NAME) != nullptr)
4507 283 : return;
4508 :
4509 1780 : GDALDriver *poDriver = new GDALDriver();
4510 1780 : ISIS3DriverSetCommonMetadata(poDriver);
4511 :
4512 1780 : poDriver->pfnOpen = ISIS3Dataset::Open;
4513 1780 : poDriver->pfnCreate = ISIS3Dataset::Create;
4514 1780 : poDriver->pfnCreateCopy = ISIS3Dataset::CreateCopy;
4515 :
4516 1780 : GetGDALDriverManager()->RegisterDriver(poDriver);
4517 : }
|