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