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