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