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