Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: VICAR Driver; JPL/MIPL VICAR Format
4 : * Purpose: Implementation of VICARDataset
5 : * Author: Sebastian Walter <sebastian dot walter at fu-berlin dot de>
6 : *
7 : * NOTE: This driver code is loosely based on the ISIS and PDS drivers.
8 : * It is not intended to diminish the contribution of the original authors
9 : ******************************************************************************
10 : * Copyright (c) 2014, Sebastian Walter <sebastian dot walter at fu-berlin dot
11 : *de>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : constexpr int VICAR_NULL1 = 0;
17 : constexpr int VICAR_NULL2 = -32768;
18 : constexpr double VICAR_NULL3 = -32768.0;
19 :
20 : #include "cpl_port.h"
21 :
22 : #include "cpl_safemaths.hpp"
23 : #include "cpl_vax.h"
24 : #include "cpl_vsi_error.h"
25 : #include "vicardataset.h"
26 : #include "nasakeywordhandler.h"
27 : #include "vicarkeywordhandler.h"
28 : #include "pdsdrivercore.h"
29 : #include "json_utils.h"
30 :
31 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
32 : #include "gtiff.h"
33 : #include "geotiff.h"
34 : #include "tifvsi.h"
35 : #include "xtiffio.h"
36 : #include "gt_wkt_srs_priv.h"
37 : #endif
38 :
39 : #include <exception>
40 : #include <limits>
41 : #include <string>
42 :
43 : #ifdef EMBED_RESOURCE_FILES
44 : #include "embedded_resources.h"
45 : #endif
46 :
47 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
48 : /* GeoTIFF 1.0 geokeys */
49 :
50 : static const geokey_t GTiffAsciiKeys[] = {GTCitationGeoKey, GeogCitationGeoKey,
51 : PCSCitationGeoKey,
52 : VerticalCitationGeoKey};
53 :
54 : static const geokey_t GTiffDoubleKeys[] = {
55 : GeogInvFlatteningGeoKey, GeogSemiMajorAxisGeoKey,
56 : GeogSemiMinorAxisGeoKey, ProjAzimuthAngleGeoKey,
57 : ProjCenterLatGeoKey, ProjCenterLongGeoKey,
58 : ProjFalseEastingGeoKey, ProjFalseNorthingGeoKey,
59 : ProjFalseOriginEastingGeoKey, ProjFalseOriginLatGeoKey,
60 : ProjFalseOriginLongGeoKey, ProjFalseOriginNorthingGeoKey,
61 : ProjLinearUnitSizeGeoKey, ProjNatOriginLatGeoKey,
62 : ProjNatOriginLongGeoKey, ProjOriginLatGeoKey,
63 : ProjOriginLongGeoKey, ProjRectifiedGridAngleGeoKey,
64 : ProjScaleAtNatOriginGeoKey, ProjScaleAtOriginGeoKey,
65 : ProjStdParallel1GeoKey, ProjStdParallel2GeoKey,
66 : ProjStdParallelGeoKey, ProjStraightVertPoleLongGeoKey,
67 : GeogLinearUnitSizeGeoKey, GeogAngularUnitSizeGeoKey,
68 : GeogPrimeMeridianLongGeoKey, ProjCenterEastingGeoKey,
69 : ProjCenterNorthingGeoKey, ProjScaleAtCenterGeoKey};
70 :
71 : static const geokey_t GTiffShortKeys[] = {
72 : GTModelTypeGeoKey, GTRasterTypeGeoKey, GeogAngularUnitsGeoKey,
73 : GeogEllipsoidGeoKey, GeogGeodeticDatumGeoKey, GeographicTypeGeoKey,
74 : ProjCoordTransGeoKey, ProjLinearUnitsGeoKey, ProjectedCSTypeGeoKey,
75 : ProjectionGeoKey, GeogPrimeMeridianGeoKey, GeogLinearUnitsGeoKey,
76 : GeogAzimuthUnitsGeoKey, VerticalCSTypeGeoKey, VerticalDatumGeoKey,
77 : VerticalUnitsGeoKey};
78 : #endif
79 :
80 : /************************************************************************/
81 : /* OGRVICARBinaryPrefixesLayer */
82 : /************************************************************************/
83 :
84 : class OGRVICARBinaryPrefixesLayer final : public OGRLayer
85 : {
86 : VSILFILE *m_fp = nullptr;
87 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
88 : int m_iRecord = 0;
89 : int m_nRecords = 0;
90 : vsi_l_offset m_nFileOffset = 0;
91 : vsi_l_offset m_nStride = 0;
92 : bool m_bError = false;
93 : bool m_bByteSwapIntegers = false;
94 : RawRasterBand::ByteOrder m_eBREALByteOrder;
95 :
96 : enum Type
97 : {
98 : FIELD_UNKNOWN,
99 : FIELD_UNSIGNED_CHAR,
100 : FIELD_UNSIGNED_SHORT,
101 : FIELD_UNSIGNED_INT,
102 : FIELD_SHORT,
103 : FIELD_INT,
104 : FIELD_FLOAT,
105 : FIELD_DOUBLE,
106 : };
107 :
108 : static Type GetTypeFromString(const char *pszStr);
109 :
110 : struct Field
111 : {
112 : int nOffset;
113 : Type eType;
114 : };
115 :
116 : std::vector<Field> m_aoFields;
117 : std::vector<GByte> m_abyRecord;
118 :
119 : OGRFeature *GetNextRawFeature();
120 :
121 : public:
122 : OGRVICARBinaryPrefixesLayer(VSILFILE *fp, int nRecords,
123 : const CPLJSONObject &oDef,
124 : vsi_l_offset nFileOffset, vsi_l_offset nStride,
125 : RawRasterBand::ByteOrder eBINTByteOrder,
126 : RawRasterBand::ByteOrder eBREALByteOrder);
127 : ~OGRVICARBinaryPrefixesLayer();
128 :
129 2 : bool HasError() const
130 : {
131 2 : return m_bError;
132 : }
133 :
134 1 : void ResetReading() override
135 : {
136 1 : m_iRecord = 0;
137 1 : }
138 :
139 0 : OGRFeatureDefn *GetLayerDefn() override
140 : {
141 0 : return m_poFeatureDefn;
142 : }
143 :
144 : OGRFeature *GetNextFeature() override;
145 :
146 1 : int TestCapability(const char *) override
147 : {
148 1 : return false;
149 : }
150 : };
151 :
152 : /************************************************************************/
153 : /* GetTypeFromString() */
154 : /************************************************************************/
155 :
156 : OGRVICARBinaryPrefixesLayer::Type
157 16 : OGRVICARBinaryPrefixesLayer::GetTypeFromString(const char *pszStr)
158 : {
159 16 : if (EQUAL(pszStr, "unsigned char") || EQUAL(pszStr, "unsigned byte"))
160 2 : return FIELD_UNSIGNED_CHAR;
161 14 : if (EQUAL(pszStr, "unsigned short"))
162 2 : return FIELD_UNSIGNED_SHORT;
163 12 : if (EQUAL(pszStr, "unsigned int"))
164 4 : return FIELD_UNSIGNED_INT;
165 8 : if (EQUAL(pszStr, "short"))
166 2 : return FIELD_SHORT;
167 6 : if (EQUAL(pszStr, "int"))
168 2 : return FIELD_INT;
169 4 : if (EQUAL(pszStr, "float"))
170 2 : return FIELD_FLOAT;
171 2 : if (EQUAL(pszStr, "double"))
172 2 : return FIELD_DOUBLE;
173 0 : return FIELD_UNKNOWN;
174 : }
175 :
176 : /************************************************************************/
177 : /* OGRVICARBinaryPrefixesLayer() */
178 : /************************************************************************/
179 :
180 2 : OGRVICARBinaryPrefixesLayer::OGRVICARBinaryPrefixesLayer(
181 : VSILFILE *fp, int nRecords, const CPLJSONObject &oDef,
182 : vsi_l_offset nFileOffset, vsi_l_offset nStride,
183 : RawRasterBand::ByteOrder eBINTByteOrder,
184 2 : RawRasterBand::ByteOrder eBREALByteOrder)
185 : : m_fp(fp), m_nRecords(nRecords), m_nFileOffset(nFileOffset),
186 : m_nStride(nStride),
187 : #ifdef CPL_LSB
188 2 : m_bByteSwapIntegers(eBINTByteOrder !=
189 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN),
190 : #else
191 : m_bByteSwapIntegers(eBINTByteOrder !=
192 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN),
193 : #endif
194 2 : m_eBREALByteOrder(eBREALByteOrder)
195 : {
196 2 : m_poFeatureDefn = new OGRFeatureDefn("binary_prefixes");
197 2 : SetDescription(m_poFeatureDefn->GetName());
198 2 : m_poFeatureDefn->Reference();
199 2 : m_poFeatureDefn->SetGeomType(wkbNone);
200 2 : int nRecordSize = oDef.GetInteger("size");
201 4 : const auto oFields = oDef.GetObj("fields");
202 2 : if (oFields.IsValid() && oFields.GetType() == CPLJSONObject::Type::Array)
203 : {
204 2 : auto oFieldsArray = oFields.ToArray();
205 2 : int nOffset = 0;
206 18 : for (int i = 0; i < oFieldsArray.Size(); i++)
207 : {
208 16 : auto oField = oFieldsArray[i];
209 16 : if (oField.GetType() == CPLJSONObject::Type::Object)
210 : {
211 32 : auto osName = oField.GetString("name");
212 32 : auto osType = oField.GetString("type");
213 16 : auto bHidden = oField.GetBool("hidden");
214 16 : auto eType = GetTypeFromString(osType.c_str());
215 16 : if (eType == FIELD_UNKNOWN)
216 : {
217 0 : CPLError(CE_Failure, CPLE_NotSupported,
218 : "Field %s of type %s not supported",
219 : osName.c_str(), osType.c_str());
220 0 : m_bError = true;
221 0 : return;
222 : }
223 16 : else if (!osName.empty())
224 : {
225 16 : OGRFieldType eFieldType(OFTMaxType);
226 : Field f;
227 16 : f.nOffset = nOffset;
228 16 : f.eType = eType;
229 16 : switch (eType)
230 : {
231 2 : case FIELD_UNSIGNED_CHAR:
232 2 : nOffset += 1;
233 2 : eFieldType = OFTInteger;
234 2 : break;
235 2 : case FIELD_UNSIGNED_SHORT:
236 2 : nOffset += 2;
237 2 : eFieldType = OFTInteger;
238 2 : break;
239 4 : case FIELD_UNSIGNED_INT:
240 4 : nOffset += 4;
241 4 : eFieldType = OFTInteger64;
242 4 : break;
243 2 : case FIELD_SHORT:
244 2 : nOffset += 2;
245 2 : eFieldType = OFTInteger;
246 2 : break;
247 2 : case FIELD_INT:
248 2 : nOffset += 4;
249 2 : eFieldType = OFTInteger;
250 2 : break;
251 2 : case FIELD_FLOAT:
252 2 : nOffset += 4;
253 2 : eFieldType = OFTReal;
254 2 : break;
255 2 : case FIELD_DOUBLE:
256 2 : nOffset += 8;
257 2 : eFieldType = OFTReal;
258 2 : break;
259 0 : default:
260 0 : CPLAssert(false);
261 : break;
262 : }
263 16 : if (nOffset > nRecordSize)
264 : {
265 0 : CPLError(CE_Failure, CPLE_AppDefined,
266 : "Field definitions not consistent with "
267 : "declared record size");
268 0 : m_bError = true;
269 0 : return;
270 : }
271 16 : if (!bHidden)
272 : {
273 14 : m_aoFields.push_back(f);
274 28 : OGRFieldDefn oFieldDefn(osName.c_str(), eFieldType);
275 14 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
276 : }
277 : }
278 : else
279 : {
280 0 : m_bError = true;
281 : }
282 : }
283 : else
284 : {
285 0 : m_bError = true;
286 : }
287 16 : if (m_bError)
288 : {
289 0 : CPLError(CE_Failure, CPLE_AppDefined,
290 : "Error while reading binary prefix definition");
291 0 : return;
292 : }
293 : }
294 : }
295 2 : m_abyRecord.resize(nRecordSize);
296 : }
297 :
298 : /************************************************************************/
299 : /* ~OGRVICARBinaryPrefixesLayer() */
300 : /************************************************************************/
301 :
302 4 : OGRVICARBinaryPrefixesLayer::~OGRVICARBinaryPrefixesLayer()
303 : {
304 2 : m_poFeatureDefn->Release();
305 4 : }
306 :
307 : /************************************************************************/
308 : /* GetNextRawFeature() */
309 : /************************************************************************/
310 :
311 3 : OGRFeature *OGRVICARBinaryPrefixesLayer::GetNextRawFeature()
312 : {
313 3 : if (m_iRecord >= m_nRecords)
314 1 : return nullptr;
315 :
316 4 : if (VSIFSeekL(m_fp, m_nFileOffset + m_iRecord * m_nStride, SEEK_SET) != 0 ||
317 2 : VSIFReadL(&m_abyRecord[0], m_abyRecord.size(), 1, m_fp) != 1)
318 : {
319 0 : return nullptr;
320 : }
321 :
322 2 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
323 16 : for (int i = 0; i < poFeature->GetFieldCount(); i++)
324 : {
325 14 : int nOffset = m_aoFields[i].nOffset;
326 14 : switch (m_aoFields[i].eType)
327 : {
328 2 : case FIELD_UNSIGNED_CHAR:
329 2 : poFeature->SetField(i, m_abyRecord[nOffset]);
330 2 : break;
331 2 : case FIELD_UNSIGNED_SHORT:
332 : {
333 : unsigned short v;
334 2 : memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
335 2 : if (m_bByteSwapIntegers)
336 : {
337 0 : CPL_SWAP16PTR(&v);
338 : }
339 2 : poFeature->SetField(i, v);
340 2 : break;
341 : }
342 2 : case FIELD_UNSIGNED_INT:
343 : {
344 : unsigned int v;
345 2 : memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
346 2 : if (m_bByteSwapIntegers)
347 : {
348 0 : CPL_SWAP32PTR(&v);
349 : }
350 2 : poFeature->SetField(i, static_cast<GIntBig>(v));
351 2 : break;
352 : }
353 2 : case FIELD_SHORT:
354 : {
355 : short v;
356 2 : memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
357 2 : if (m_bByteSwapIntegers)
358 : {
359 0 : CPL_SWAP16PTR(&v);
360 : }
361 2 : poFeature->SetField(i, v);
362 2 : break;
363 : }
364 2 : case FIELD_INT:
365 : {
366 : int v;
367 2 : memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
368 2 : if (m_bByteSwapIntegers)
369 : {
370 0 : CPL_SWAP32PTR(&v);
371 : }
372 2 : poFeature->SetField(i, v);
373 2 : break;
374 : }
375 2 : case FIELD_FLOAT:
376 : {
377 : float v;
378 2 : memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
379 2 : if (m_eBREALByteOrder == RawRasterBand::ByteOrder::ORDER_VAX)
380 : {
381 0 : CPLVaxToIEEEFloat(&v);
382 : }
383 2 : else if (m_eBREALByteOrder !=
384 : #ifdef CPL_LSB
385 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
386 : #else
387 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
388 : #endif
389 : )
390 : {
391 0 : CPL_SWAP32PTR(&v);
392 : }
393 2 : poFeature->SetField(i, v);
394 2 : break;
395 : }
396 2 : case FIELD_DOUBLE:
397 : {
398 : double v;
399 2 : memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
400 2 : if (m_eBREALByteOrder == RawRasterBand::ByteOrder::ORDER_VAX)
401 : {
402 0 : CPLVaxToIEEEDouble(&v);
403 : }
404 2 : else if (m_eBREALByteOrder !=
405 : #ifdef CPL_LSB
406 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
407 : #else
408 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
409 : #endif
410 : )
411 : {
412 0 : CPL_SWAP64PTR(&v);
413 : }
414 2 : poFeature->SetField(i, v);
415 2 : break;
416 : }
417 0 : default:
418 0 : CPLAssert(false);
419 : }
420 : }
421 2 : poFeature->SetFID(m_iRecord);
422 2 : m_iRecord++;
423 2 : return poFeature;
424 : }
425 :
426 : /************************************************************************/
427 : /* GetNextFeature() */
428 : /************************************************************************/
429 :
430 3 : OGRFeature *OGRVICARBinaryPrefixesLayer::GetNextFeature()
431 : {
432 : while (true)
433 : {
434 3 : auto poFeature = GetNextRawFeature();
435 3 : if (poFeature == nullptr)
436 1 : return nullptr;
437 :
438 4 : if ((m_poFilterGeom == nullptr ||
439 4 : FilterGeometry(poFeature->GetGeometryRef())) &&
440 2 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
441 : {
442 2 : return poFeature;
443 : }
444 : else
445 0 : delete poFeature;
446 0 : }
447 : }
448 :
449 : /************************************************************************/
450 : /* VICARRawRasterBand */
451 : /************************************************************************/
452 :
453 : class VICARRawRasterBand final : public RawRasterBand
454 : {
455 : protected:
456 : friend class VICARDataset;
457 :
458 : public:
459 : VICARRawRasterBand(VICARDataset *poDSIn, int nBandIn, VSILFILE *fpRawIn,
460 : vsi_l_offset nImgOffsetIn, int nPixelOffsetIn,
461 : int nLineOffsetIn, GDALDataType eDataTypeIn,
462 : ByteOrder eByteOrderIn);
463 :
464 : virtual CPLErr IReadBlock(int, int, void *) override;
465 : virtual CPLErr IWriteBlock(int, int, void *) override;
466 :
467 : virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
468 : GDALDataType, GSpacing nPixelSpace,
469 : GSpacing nLineSpace,
470 : GDALRasterIOExtraArg *psExtraArg) override;
471 : };
472 :
473 : /************************************************************************/
474 : /* VICARRawRasterBand() */
475 : /************************************************************************/
476 :
477 216 : VICARRawRasterBand::VICARRawRasterBand(VICARDataset *poDSIn, int nBandIn,
478 : VSILFILE *fpRawIn,
479 : vsi_l_offset nImgOffsetIn,
480 : int nPixelOffsetIn, int nLineOffsetIn,
481 : GDALDataType eDataTypeIn,
482 216 : ByteOrder eByteOrderIn)
483 : : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
484 : nLineOffsetIn, eDataTypeIn, eByteOrderIn,
485 216 : RawRasterBand::OwnFP::NO)
486 : {
487 216 : }
488 :
489 : /************************************************************************/
490 : /* IReadBlock() */
491 : /************************************************************************/
492 :
493 2001 : CPLErr VICARRawRasterBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
494 :
495 : {
496 2001 : VICARDataset *poGDS = reinterpret_cast<VICARDataset *>(poDS);
497 2001 : if (!poGDS->m_bIsLabelWritten)
498 0 : poGDS->WriteLabel();
499 2001 : return RawRasterBand::IReadBlock(nXBlock, nYBlock, pImage);
500 : }
501 :
502 : /************************************************************************/
503 : /* IWriteBlock() */
504 : /************************************************************************/
505 :
506 2000 : CPLErr VICARRawRasterBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
507 :
508 : {
509 2000 : VICARDataset *poGDS = reinterpret_cast<VICARDataset *>(poDS);
510 2000 : if (!poGDS->m_bIsLabelWritten)
511 0 : poGDS->WriteLabel();
512 2000 : return RawRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
513 : }
514 :
515 : /************************************************************************/
516 : /* IRasterIO() */
517 : /************************************************************************/
518 :
519 496 : CPLErr VICARRawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
520 : int nXSize, int nYSize, void *pData,
521 : int nBufXSize, int nBufYSize,
522 : GDALDataType eBufType,
523 : GSpacing nPixelSpace, GSpacing nLineSpace,
524 : GDALRasterIOExtraArg *psExtraArg)
525 :
526 : {
527 496 : VICARDataset *poGDS = reinterpret_cast<VICARDataset *>(poDS);
528 496 : if (!poGDS->m_bIsLabelWritten)
529 29 : poGDS->WriteLabel();
530 496 : return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
531 : pData, nBufXSize, nBufYSize, eBufType,
532 496 : nPixelSpace, nLineSpace, psExtraArg);
533 : }
534 :
535 : /************************************************************************/
536 : /* VICARBASICRasterBand */
537 : /************************************************************************/
538 :
539 : class VICARBASICRasterBand final : public GDALPamRasterBand
540 : {
541 : public:
542 : VICARBASICRasterBand(VICARDataset *poDSIn, int nBandIn, GDALDataType eType);
543 :
544 : virtual CPLErr IReadBlock(int, int, void *) override;
545 : virtual CPLErr IWriteBlock(int, int, void *) override;
546 : };
547 :
548 : /************************************************************************/
549 : /* VICARBASICRasterBand() */
550 : /************************************************************************/
551 :
552 21 : VICARBASICRasterBand::VICARBASICRasterBand(VICARDataset *poDSIn, int nBandIn,
553 21 : GDALDataType eType)
554 : {
555 21 : poDS = poDSIn;
556 21 : nBand = nBandIn;
557 21 : nBlockXSize = poDSIn->GetRasterXSize();
558 21 : nBlockYSize = 1;
559 21 : eDataType = eType;
560 21 : }
561 :
562 : namespace
563 : {
564 : class DecodeEncodeException : public std::exception
565 : {
566 : public:
567 0 : DecodeEncodeException() = default;
568 : };
569 : } // namespace
570 :
571 : //////////////////////////////////////////////////////////////////////////
572 : /// Below functions are adapted from Public Domain VICAR project
573 : /// from
574 : /// https://github.com/nasa/VICAR/blob/master/vos/rtl/source/basic_compression.c
575 : //////////////////////////////////////////////////////////////////////////
576 :
577 : /* masking array used in the algorithm to take out bits in memory */
578 : const unsigned int cod1mask[25] = {
579 : 0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f,
580 : 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff,
581 : 0x3fff, 0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
582 : 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff};
583 :
584 : /*****************************************************/
585 : /* This function is a helper function for the BASIC */
586 : /* compression algorithm to get a specified number */
587 : /* of bits from buffer, convert it to a number and */
588 : /* return it. */
589 : /*****************************************************/
590 11439 : static unsigned char grab1(int nbit, const unsigned char *buffer,
591 : size_t buffer_size, size_t &buffer_pos,
592 : int &bit1ptr) /* bit position in the current byte of
593 : encrypted or decrypted buffer*/
594 : {
595 : unsigned char val;
596 11439 : int shift = 8 - nbit - (bit1ptr);
597 :
598 11439 : if (buffer_pos >= buffer_size)
599 : {
600 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of decoding buffer");
601 0 : throw DecodeEncodeException();
602 : }
603 :
604 11439 : if (shift > 0)
605 : {
606 5625 : val = (buffer[buffer_pos] >> shift) & cod1mask[nbit];
607 5625 : bit1ptr += nbit;
608 :
609 5625 : return val;
610 : }
611 5814 : if (shift < 0)
612 : {
613 3642 : unsigned v1 = buffer[buffer_pos] & cod1mask[nbit + shift];
614 3642 : buffer_pos++;
615 :
616 3642 : if (buffer_pos >= buffer_size)
617 : {
618 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of decoding buffer");
619 0 : throw DecodeEncodeException();
620 : }
621 :
622 3642 : unsigned v2 = (buffer[buffer_pos] >> (8 + shift)) & cod1mask[-shift];
623 :
624 3642 : val = static_cast<unsigned char>((v1 << (-shift)) + v2);
625 :
626 3642 : bit1ptr = -shift;
627 :
628 3642 : return val;
629 : }
630 2172 : val = buffer[buffer_pos] & cod1mask[nbit];
631 2172 : buffer_pos++;
632 2172 : bit1ptr = 0;
633 :
634 2172 : return val;
635 : }
636 :
637 : /*****************************************************/
638 : /* This function is the decoding algorithm for BASIC */
639 : /* compression. The encoded buffer is passed into */
640 : /* code and the decoded buffer is passed out in buf. */
641 : /*****************************************************/
642 330 : static void basic_decode(const unsigned char *code, size_t code_size,
643 : unsigned char *buf, int ns, int wid)
644 : {
645 330 : int runInt = -3;
646 : unsigned char runChar;
647 330 : unsigned int nval = 999999;
648 : static const int cmprtrns1[7] = {-3, -2, -1, 0, 1, 2, 3};
649 330 : size_t buffer_pos = 0;
650 330 : int bit1ptr = 0;
651 330 : unsigned int old = 0;
652 330 : const int ptop = ns * wid;
653 :
654 720 : for (int iw = 0; iw < wid; iw++)
655 : {
656 305190 : for (int ip = iw; ip < ptop; ip += wid)
657 : {
658 304800 : if (runInt > (-3))
659 : {
660 301053 : buf[ip] = static_cast<unsigned char>(nval);
661 301053 : runInt--;
662 301053 : continue;
663 : }
664 3747 : unsigned char val = grab1(3, code, code_size, buffer_pos, bit1ptr);
665 :
666 3747 : if (val < 7)
667 : {
668 459 : nval = CPLUnsanitizedAdd<unsigned>(old, cmprtrns1[val]);
669 459 : buf[ip] = static_cast<unsigned char>(nval);
670 459 : old = nval;
671 459 : continue;
672 : }
673 3288 : val = grab1(1, code, code_size, buffer_pos, bit1ptr);
674 :
675 3288 : if (val)
676 : {
677 228 : runChar = grab1(4, code, code_size, buffer_pos, bit1ptr);
678 228 : if (runChar == 15)
679 : {
680 210 : runChar = grab1(8, code, code_size, buffer_pos, bit1ptr);
681 :
682 210 : if (runChar == 255)
683 : {
684 : unsigned char part0 =
685 150 : grab1(8, code, code_size, buffer_pos, bit1ptr);
686 : unsigned char part1 =
687 150 : grab1(8, code, code_size, buffer_pos, bit1ptr);
688 : unsigned char part2 =
689 150 : grab1(8, code, code_size, buffer_pos, bit1ptr);
690 150 : runInt = part0 | (part1 << 8) | (part2 << 16);
691 : }
692 : else
693 60 : runInt = runChar + 15;
694 : }
695 : else
696 18 : runInt = runChar;
697 :
698 228 : val = grab1(3, code, code_size, buffer_pos, bit1ptr);
699 228 : if (val < 7)
700 0 : nval = CPLUnsanitizedAdd<unsigned>(old, cmprtrns1[val]);
701 : else
702 228 : nval = grab1(8, code, code_size, buffer_pos, bit1ptr);
703 228 : buf[ip] = static_cast<unsigned char>(nval);
704 228 : old = nval;
705 : }
706 : else
707 : {
708 3060 : val = grab1(8, code, code_size, buffer_pos, bit1ptr);
709 3060 : buf[ip] = val;
710 3060 : old = val;
711 : }
712 : }
713 : }
714 330 : }
715 :
716 : /*****************************************************/
717 : /* This function is a helper function for the BASIC */
718 : /* encoding operation. It puts the value in val into*/
719 : /* memory location pointed by pcode1+reg1 into nbit */
720 : /* number of bits. */
721 : /*****************************************************/
722 2717 : static void emit1(unsigned char val, int nbit, unsigned char *reg1,
723 : int &bit1ptr, unsigned char *coded_buffer,
724 : size_t &coded_buffer_pos, size_t coded_buffer_size)
725 : {
726 : int shift;
727 :
728 2717 : shift = 8 - nbit - bit1ptr;
729 2717 : if (shift > 0)
730 : {
731 779 : *reg1 = static_cast<unsigned char>(*reg1 | (val << shift));
732 779 : bit1ptr += nbit;
733 779 : return;
734 : }
735 1938 : if (shift < 0)
736 : {
737 1265 : if (coded_buffer_pos >= coded_buffer_size)
738 : {
739 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of encoding buffer");
740 0 : throw DecodeEncodeException();
741 : }
742 1265 : coded_buffer[coded_buffer_pos] =
743 1265 : static_cast<unsigned char>(*reg1 | (val >> (-shift)));
744 1265 : coded_buffer_pos++;
745 1265 : *reg1 = static_cast<unsigned char>(val << (8 + shift));
746 1265 : bit1ptr = -shift;
747 1265 : return;
748 : }
749 673 : if (coded_buffer_pos >= coded_buffer_size)
750 : {
751 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of encoding buffer");
752 0 : throw DecodeEncodeException();
753 : }
754 673 : coded_buffer[coded_buffer_pos] = static_cast<unsigned char>(*reg1 | val);
755 673 : coded_buffer_pos++;
756 :
757 673 : *reg1 = 0;
758 673 : bit1ptr = 0;
759 : }
760 :
761 : /*****************************************************/
762 : /* This function is meat of the BASIC encoding */
763 : /* algorithm. This function is called repeatedly by */
764 : /* the basic_encode function to compress the data */
765 : /* according to its run length (run), last 2 */
766 : /* different values (vold and old), and the current */
767 : /* value (val), into the memory location pointed by */
768 : /* pcode1+reg1. */
769 : /*****************************************************/
770 1096 : static void basic_encrypt(int *run, int *old, int *vold, int val,
771 : unsigned char *reg1, int &bit1ptr,
772 : unsigned char *coded_buffer, size_t &coded_buffer_pos,
773 : size_t coded_buffer_size)
774 : {
775 1096 : if (*run < 4)
776 : {
777 1020 : if (abs(*old - *vold) < 4)
778 0 : emit1((unsigned char)(*old - *vold + 3), 3, reg1, bit1ptr,
779 : coded_buffer, coded_buffer_pos, coded_buffer_size);
780 : else
781 : {
782 1020 : emit1((unsigned char)14, 4, reg1, bit1ptr, coded_buffer,
783 : coded_buffer_pos, coded_buffer_size);
784 1020 : emit1((unsigned char)(*old), 8, reg1, bit1ptr, coded_buffer,
785 : coded_buffer_pos, coded_buffer_size);
786 : }
787 :
788 1173 : while (*run > 1)
789 : {
790 153 : emit1((unsigned char)3, 3, reg1, bit1ptr, coded_buffer,
791 : coded_buffer_pos, coded_buffer_size);
792 153 : (*run)--;
793 : }
794 :
795 1020 : *vold = *old;
796 1020 : *old = val;
797 : }
798 : else
799 : {
800 76 : emit1((unsigned char)15, 4, reg1, bit1ptr, coded_buffer,
801 : coded_buffer_pos, coded_buffer_size);
802 76 : if (*run < 19)
803 : {
804 6 : emit1((unsigned char)(*run - 4), 4, reg1, bit1ptr, coded_buffer,
805 : coded_buffer_pos, coded_buffer_size);
806 : }
807 : else
808 : {
809 70 : emit1((unsigned char)15, 4, reg1, bit1ptr, coded_buffer,
810 : coded_buffer_pos, coded_buffer_size);
811 70 : if (*run < 274)
812 : {
813 20 : emit1((char)(*run - 19), 8, reg1, bit1ptr, coded_buffer,
814 : coded_buffer_pos, coded_buffer_size);
815 : }
816 : else
817 : {
818 50 : emit1((unsigned char)255, 8, reg1, bit1ptr, coded_buffer,
819 : coded_buffer_pos, coded_buffer_size);
820 :
821 50 : unsigned char part0 =
822 50 : static_cast<unsigned char>((*run - 4) & 0xff);
823 50 : unsigned char part1 =
824 50 : static_cast<unsigned char>(((*run - 4) >> 8) & 0xff);
825 50 : unsigned char part2 =
826 50 : static_cast<unsigned char>(((*run - 4) >> 16) & 0xff);
827 50 : emit1(part0, 8, reg1, bit1ptr, coded_buffer, coded_buffer_pos,
828 : coded_buffer_size);
829 50 : emit1(part1, 8, reg1, bit1ptr, coded_buffer, coded_buffer_pos,
830 : coded_buffer_size);
831 50 : emit1(part2, 8, reg1, bit1ptr, coded_buffer, coded_buffer_pos,
832 : coded_buffer_size);
833 : }
834 : }
835 76 : if (abs(*old - *vold) < 4)
836 : {
837 0 : emit1((unsigned char)(*old - *vold + 3), 3, reg1, bit1ptr,
838 : coded_buffer, coded_buffer_pos, coded_buffer_size);
839 : }
840 : else
841 : {
842 76 : emit1((unsigned char)7, 3, reg1, bit1ptr, coded_buffer,
843 : coded_buffer_pos, coded_buffer_size);
844 76 : emit1((unsigned char)(*old), 8, reg1, bit1ptr, coded_buffer,
845 : coded_buffer_pos, coded_buffer_size);
846 : }
847 76 : *vold = *old;
848 76 : *old = val;
849 76 : *run = 1;
850 : }
851 1096 : }
852 :
853 : /*****************************************************/
854 : /* This function loops through the data given by */
855 : /* unencodedBuf, keeping track of run length. When */
856 : /* the value of the data changes, it passes the run */
857 : /* length, last 2 differing values, the current */
858 : /* value, and the pointer in pcode1 buffer to encode */
859 : /* the data into, to basic_encrypt function. */
860 : /*****************************************************/
861 110 : static void basic_encode(const unsigned char *unencodedBuf,
862 : unsigned char *coded_buffer, size_t coded_buffer_size,
863 : int ns, int wid, size_t *totBytes)
864 : {
865 110 : int val = 0;
866 110 : int bit1ptr = 0;
867 110 : const int ptop = ns * wid;
868 110 : unsigned char reg1 = 0;
869 110 : int run = 0;
870 110 : int old = unencodedBuf[0];
871 110 : int vold = 999999;
872 :
873 110 : size_t coded_buffer_pos = 0;
874 :
875 240 : for (int iw = 0; iw < wid; iw++)
876 : {
877 101730 : for (int ip = iw; ip < ptop; ip += wid)
878 : {
879 101600 : val = unencodedBuf[ip];
880 :
881 101600 : if (val == old)
882 100614 : run++;
883 : else
884 986 : basic_encrypt(&run, &old, &vold, val, ®1, bit1ptr,
885 : coded_buffer, coded_buffer_pos,
886 : coded_buffer_size);
887 : }
888 : }
889 :
890 : /* purge of last code */
891 110 : basic_encrypt(&run, &old, &vold, val, ®1, bit1ptr, coded_buffer,
892 : coded_buffer_pos, coded_buffer_size);
893 :
894 110 : if (coded_buffer_pos >= coded_buffer_size)
895 : {
896 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of encoding buffer");
897 0 : throw DecodeEncodeException();
898 : }
899 110 : coded_buffer[coded_buffer_pos] = reg1;
900 :
901 110 : *totBytes = coded_buffer_pos;
902 110 : if (bit1ptr > 0)
903 102 : (*totBytes)++;
904 110 : }
905 :
906 : //////////////////////////////////////////////////////////////////////////
907 : /// End of VICAR code
908 : //////////////////////////////////////////////////////////////////////////
909 :
910 : /************************************************************************/
911 : /* IReadBlock() */
912 : /************************************************************************/
913 :
914 330 : CPLErr VICARBASICRasterBand::IReadBlock(int /*nXBlock*/, int nYBlock,
915 : void *pImage)
916 :
917 : {
918 330 : VICARDataset *poGDS = reinterpret_cast<VICARDataset *>(poDS);
919 :
920 330 : const int nRecord = (nBand - 1) * nRasterYSize + nYBlock;
921 330 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
922 :
923 330 : if (poGDS->eAccess == GA_Update &&
924 0 : poGDS->m_anRecordOffsets[nRecord + 1] == 0)
925 : {
926 0 : memset(pImage, 0, static_cast<size_t>(nDTSize) * nRasterXSize);
927 0 : return CE_None;
928 : }
929 :
930 : // Find at which offset the compressed record is.
931 : // For BASIC compression, each compressed run is preceded by a uint32 value
932 : // givin its size, including the size of this uint32 value
933 : // For BASIC2 compression, the uint32 sizes of all records are put
934 : // immediately after the label.
935 660 : for (; poGDS->m_nLastRecordOffset <= nRecord; poGDS->m_nLastRecordOffset++)
936 : {
937 330 : CPLAssert(poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset + 1] ==
938 : 0);
939 :
940 : int nRet;
941 330 : if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
942 : {
943 : nRet =
944 80 : VSIFSeekL(poGDS->fpImage,
945 80 : poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset] -
946 : sizeof(GUInt32),
947 : SEEK_SET);
948 : }
949 : else
950 : {
951 250 : nRet = VSIFSeekL(poGDS->fpImage,
952 250 : poGDS->m_nImageOffsetWithoutNBB +
953 250 : static_cast<vsi_l_offset>(sizeof(GUInt32)) *
954 250 : poGDS->m_nLastRecordOffset,
955 : SEEK_SET);
956 : }
957 : GUInt32 nSize;
958 660 : if (nRet != 0 ||
959 330 : VSIFReadL(&nSize, sizeof(nSize), 1, poGDS->fpImage) != 1)
960 : {
961 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read record %d size",
962 : poGDS->m_nLastRecordOffset);
963 0 : return CE_Failure;
964 : }
965 330 : CPL_LSBPTR32(&nSize);
966 740 : if ((poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC &&
967 80 : nSize <= sizeof(GUInt32)) ||
968 330 : (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC2 &&
969 910 : nSize == 0) ||
970 330 : poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset] >
971 330 : std::numeric_limits<uint64_t>::max() - nSize)
972 : {
973 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong size at record %d",
974 : poGDS->m_nLastRecordOffset);
975 0 : return CE_Failure;
976 : }
977 :
978 330 : poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset + 1] =
979 330 : poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset] + nSize;
980 : }
981 :
982 : unsigned int nSize;
983 330 : if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
984 : {
985 160 : nSize = static_cast<unsigned>(poGDS->m_anRecordOffsets[nRecord + 1] -
986 80 : poGDS->m_anRecordOffsets[nRecord] -
987 : sizeof(GUInt32));
988 : }
989 : else
990 : {
991 500 : nSize = static_cast<unsigned>(poGDS->m_anRecordOffsets[nRecord + 1] -
992 250 : poGDS->m_anRecordOffsets[nRecord]);
993 : }
994 330 : if (nSize > 100 * 1000 * 1000 ||
995 0 : (nSize > 1000 &&
996 0 : (nSize - 11) / 4 > static_cast<unsigned>(nRasterXSize) * nDTSize))
997 : {
998 0 : return CE_Failure;
999 : }
1000 330 : if (poGDS->m_abyCodedBuffer.size() < nSize)
1001 : {
1002 : try
1003 : {
1004 39 : poGDS->m_abyCodedBuffer.resize(nSize);
1005 : }
1006 0 : catch (const std::exception &e)
1007 : {
1008 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1009 0 : return CE_Failure;
1010 : }
1011 : }
1012 330 : if (VSIFSeekL(poGDS->fpImage, poGDS->m_anRecordOffsets[nRecord],
1013 660 : SEEK_SET) != 0 ||
1014 330 : VSIFReadL(&poGDS->m_abyCodedBuffer[0], nSize, 1, poGDS->fpImage) != 1)
1015 : {
1016 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read record %d", nRecord);
1017 0 : return CE_Failure;
1018 : }
1019 :
1020 : try
1021 : {
1022 330 : basic_decode(poGDS->m_abyCodedBuffer.data(), nSize,
1023 : static_cast<unsigned char *>(pImage), nRasterXSize,
1024 : nDTSize);
1025 : }
1026 0 : catch (const DecodeEncodeException &)
1027 : {
1028 0 : return CE_Failure;
1029 : }
1030 : #ifdef CPL_MSB
1031 : if (nDTSize > 1)
1032 : {
1033 : GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
1034 : }
1035 : #endif
1036 330 : return CE_None;
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* IWriteBlock() */
1041 : /************************************************************************/
1042 :
1043 111 : CPLErr VICARBASICRasterBand::IWriteBlock(int /*nXBlock*/, int nYBlock,
1044 : void *pImage)
1045 :
1046 : {
1047 111 : VICARDataset *poGDS = reinterpret_cast<VICARDataset *>(poDS);
1048 111 : if (poGDS->eAccess == GA_ReadOnly)
1049 0 : return CE_Failure;
1050 111 : if (!poGDS->m_bIsLabelWritten)
1051 : {
1052 5 : poGDS->WriteLabel();
1053 5 : poGDS->m_nLabelSize = VSIFTellL(poGDS->fpImage);
1054 5 : poGDS->m_anRecordOffsets[0] = poGDS->m_nLabelSize;
1055 5 : if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
1056 : {
1057 2 : poGDS->m_anRecordOffsets[0] += sizeof(GUInt32);
1058 : }
1059 : else
1060 : {
1061 3 : poGDS->m_anRecordOffsets[0] +=
1062 3 : static_cast<vsi_l_offset>(sizeof(GUInt32)) * nRasterYSize;
1063 : }
1064 : }
1065 111 : if (nYBlock != poGDS->m_nLastRecordOffset)
1066 : {
1067 1 : CPLError(CE_Failure, CPLE_NotSupported,
1068 : "Lines must be written in sequential order");
1069 1 : return CE_Failure;
1070 : }
1071 :
1072 110 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1073 110 : const size_t nMaxEncodedSize =
1074 110 : static_cast<size_t>(nRasterXSize) * nDTSize +
1075 110 : static_cast<size_t>(nRasterXSize) * nDTSize / 2 + 11;
1076 110 : if (poGDS->m_abyCodedBuffer.size() < nMaxEncodedSize)
1077 : {
1078 : try
1079 : {
1080 4 : poGDS->m_abyCodedBuffer.resize(nMaxEncodedSize);
1081 : }
1082 0 : catch (const std::exception &e)
1083 : {
1084 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1085 0 : return CE_Failure;
1086 : }
1087 : }
1088 :
1089 : #ifdef CPL_MSB
1090 : if (nDTSize > 1)
1091 : {
1092 : GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
1093 : }
1094 : #endif
1095 :
1096 110 : size_t nCodedSize = 0;
1097 : try
1098 : {
1099 220 : basic_encode(
1100 110 : static_cast<unsigned char *>(pImage), &poGDS->m_abyCodedBuffer[0],
1101 : poGDS->m_abyCodedBuffer.size(), nRasterXSize, nDTSize, &nCodedSize);
1102 : }
1103 0 : catch (const DecodeEncodeException &)
1104 : {
1105 0 : return CE_Failure;
1106 : }
1107 :
1108 : #ifdef CPL_MSB
1109 : if (nDTSize > 1)
1110 : {
1111 : GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
1112 : }
1113 : #endif
1114 :
1115 110 : if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
1116 : {
1117 20 : VSIFSeekL(poGDS->fpImage,
1118 20 : poGDS->m_anRecordOffsets[nYBlock] - sizeof(GUInt32),
1119 : SEEK_SET);
1120 20 : GUInt32 nSizeToWrite =
1121 20 : static_cast<GUInt32>(nCodedSize + sizeof(GUInt32));
1122 20 : CPL_LSBPTR32(&nSizeToWrite);
1123 20 : VSIFWriteL(&nSizeToWrite, sizeof(GUInt32), 1, poGDS->fpImage);
1124 20 : VSIFWriteL(poGDS->m_abyCodedBuffer.data(), nCodedSize, 1,
1125 : poGDS->fpImage);
1126 20 : poGDS->m_anRecordOffsets[nYBlock + 1] =
1127 20 : poGDS->m_anRecordOffsets[nYBlock] + nCodedSize + sizeof(GUInt32);
1128 : }
1129 : else
1130 : {
1131 90 : VSIFSeekL(poGDS->fpImage,
1132 90 : poGDS->m_nLabelSize +
1133 90 : static_cast<vsi_l_offset>(nYBlock) * sizeof(GUInt32),
1134 : SEEK_SET);
1135 90 : GUInt32 nSizeToWrite = static_cast<GUInt32>(nCodedSize);
1136 90 : CPL_LSBPTR32(&nSizeToWrite);
1137 90 : VSIFWriteL(&nSizeToWrite, sizeof(GUInt32), 1, poGDS->fpImage);
1138 90 : VSIFSeekL(poGDS->fpImage, poGDS->m_anRecordOffsets[nYBlock], SEEK_SET);
1139 90 : VSIFWriteL(poGDS->m_abyCodedBuffer.data(), nCodedSize, 1,
1140 : poGDS->fpImage);
1141 90 : poGDS->m_anRecordOffsets[nYBlock + 1] =
1142 90 : poGDS->m_anRecordOffsets[nYBlock] + nCodedSize;
1143 : }
1144 :
1145 110 : poGDS->m_nLastRecordOffset++;
1146 :
1147 110 : return CE_None;
1148 : }
1149 :
1150 : /************************************************************************/
1151 : /* VICARDataset() */
1152 : /************************************************************************/
1153 :
1154 166 : VICARDataset::VICARDataset()
1155 :
1156 : {
1157 166 : m_oJSonLabel.Deinit();
1158 166 : m_oSrcJSonLabel.Deinit();
1159 166 : }
1160 :
1161 : /************************************************************************/
1162 : /* ~VICARDataset() */
1163 : /************************************************************************/
1164 :
1165 332 : VICARDataset::~VICARDataset()
1166 :
1167 : {
1168 166 : VICARDataset::Close();
1169 332 : }
1170 :
1171 : /************************************************************************/
1172 : /* Close() */
1173 : /************************************************************************/
1174 :
1175 322 : CPLErr VICARDataset::Close()
1176 : {
1177 322 : CPLErr eErr = CE_None;
1178 322 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
1179 : {
1180 166 : if (!m_bIsLabelWritten)
1181 19 : WriteLabel();
1182 :
1183 166 : if (VICARDataset::FlushCache(true) != CE_None)
1184 0 : eErr = CE_Failure;
1185 :
1186 166 : PatchLabel();
1187 166 : if (fpImage)
1188 166 : VSIFCloseL(fpImage);
1189 :
1190 166 : if (GDALPamDataset::Close() != CE_None)
1191 0 : eErr = CE_Failure;
1192 : }
1193 322 : return eErr;
1194 : }
1195 :
1196 : /************************************************************************/
1197 : /* GetSpatialRef() */
1198 : /************************************************************************/
1199 :
1200 26 : const OGRSpatialReference *VICARDataset::GetSpatialRef() const
1201 :
1202 : {
1203 26 : if (!m_oSRS.IsEmpty())
1204 16 : return &m_oSRS;
1205 :
1206 10 : return GDALPamDataset::GetSpatialRef();
1207 : }
1208 :
1209 : /************************************************************************/
1210 : /* SetSpatialRef() */
1211 : /************************************************************************/
1212 :
1213 39 : CPLErr VICARDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1214 : {
1215 39 : if (eAccess == GA_ReadOnly)
1216 0 : return GDALPamDataset::SetSpatialRef(poSRS);
1217 39 : if (poSRS)
1218 39 : m_oSRS = *poSRS;
1219 : else
1220 0 : m_oSRS.Clear();
1221 39 : InvalidateLabel();
1222 39 : return CE_None;
1223 : }
1224 :
1225 : /************************************************************************/
1226 : /* GetGeoTransform() */
1227 : /************************************************************************/
1228 :
1229 39 : CPLErr VICARDataset::GetGeoTransform(double *padfTransform)
1230 :
1231 : {
1232 39 : if (m_bGotTransform)
1233 : {
1234 33 : memcpy(padfTransform, &m_adfGeoTransform[0], sizeof(double) * 6);
1235 33 : return CE_None;
1236 : }
1237 :
1238 6 : return GDALPamDataset::GetGeoTransform(padfTransform);
1239 : }
1240 :
1241 : /************************************************************************/
1242 : /* SetGeoTransform() */
1243 : /************************************************************************/
1244 :
1245 43 : CPLErr VICARDataset::SetGeoTransform(double *padfTransform)
1246 :
1247 : {
1248 43 : if (eAccess == GA_ReadOnly)
1249 0 : return GDALPamDataset::SetGeoTransform(padfTransform);
1250 43 : if (padfTransform[1] <= 0.0 || padfTransform[1] != -padfTransform[5] ||
1251 42 : padfTransform[2] != 0.0 || padfTransform[4] != 0.0)
1252 : {
1253 1 : CPLError(CE_Failure, CPLE_NotSupported,
1254 : "Only north-up geotransform with square pixels supported");
1255 1 : return CE_Failure;
1256 : }
1257 42 : m_bGotTransform = true;
1258 42 : memcpy(&m_adfGeoTransform[0], padfTransform, sizeof(double) * 6);
1259 42 : InvalidateLabel();
1260 42 : return CE_None;
1261 : }
1262 :
1263 : /************************************************************************/
1264 : /* GetRawBinaryLayout() */
1265 : /************************************************************************/
1266 :
1267 2 : bool VICARDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1268 : {
1269 2 : if (!RawDataset::GetRawBinaryLayout(sLayout))
1270 0 : return false;
1271 2 : sLayout.osRawFilename = GetDescription();
1272 2 : return true;
1273 : }
1274 :
1275 : /************************************************************************/
1276 : /* GetMetadataDomainList() */
1277 : /************************************************************************/
1278 :
1279 1 : char **VICARDataset::GetMetadataDomainList()
1280 : {
1281 1 : return BuildMetadataDomainList(nullptr, FALSE, "", "json:VICAR", nullptr);
1282 : }
1283 :
1284 : /************************************************************************/
1285 : /* GetMetadata() */
1286 : /************************************************************************/
1287 :
1288 35 : char **VICARDataset::GetMetadata(const char *pszDomain)
1289 : {
1290 35 : if (pszDomain != nullptr && EQUAL(pszDomain, "json:VICAR"))
1291 : {
1292 22 : if (m_aosVICARMD.empty())
1293 : {
1294 19 : if (eAccess == GA_Update && !m_oJSonLabel.IsValid())
1295 : {
1296 0 : BuildLabel();
1297 : }
1298 19 : CPLAssert(m_oJSonLabel.IsValid());
1299 : const CPLString osJson =
1300 38 : m_oJSonLabel.Format(CPLJSONObject::PrettyFormat::Pretty);
1301 19 : m_aosVICARMD.InsertString(0, osJson.c_str());
1302 : }
1303 22 : return m_aosVICARMD.List();
1304 : }
1305 13 : return GDALPamDataset::GetMetadata(pszDomain);
1306 : }
1307 :
1308 : /************************************************************************/
1309 : /* InvalidateLabel() */
1310 : /************************************************************************/
1311 :
1312 94 : void VICARDataset::InvalidateLabel()
1313 : {
1314 94 : m_oJSonLabel.Deinit();
1315 94 : m_aosVICARMD.Clear();
1316 94 : }
1317 :
1318 : /************************************************************************/
1319 : /* SetMetadata() */
1320 : /************************************************************************/
1321 :
1322 13 : CPLErr VICARDataset::SetMetadata(char **papszMD, const char *pszDomain)
1323 : {
1324 13 : if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr &&
1325 13 : EQUAL(pszDomain, "json:VICAR"))
1326 : {
1327 13 : m_oSrcJSonLabel.Deinit();
1328 13 : InvalidateLabel();
1329 13 : if (papszMD != nullptr && papszMD[0] != nullptr)
1330 : {
1331 13 : CPLJSONDocument oJSONDocument;
1332 13 : const GByte *pabyData = reinterpret_cast<const GByte *>(papszMD[0]);
1333 13 : if (!oJSONDocument.LoadMemory(pabyData))
1334 : {
1335 0 : return CE_Failure;
1336 : }
1337 :
1338 13 : m_oSrcJSonLabel = oJSONDocument.GetRoot();
1339 13 : if (!m_oSrcJSonLabel.IsValid())
1340 : {
1341 0 : return CE_Failure;
1342 : }
1343 : }
1344 13 : return CE_None;
1345 : }
1346 0 : return GDALPamDataset::SetMetadata(papszMD, pszDomain);
1347 : }
1348 :
1349 : /************************************************************************/
1350 : /* SerializeString() */
1351 : /************************************************************************/
1352 :
1353 851 : static std::string SerializeString(const std::string &s)
1354 : {
1355 1702 : return '\'' + CPLString(s).replaceAll('\'', "''").replaceAll('\n', "\\n") +
1356 1702 : '\'';
1357 : }
1358 :
1359 : /************************************************************************/
1360 : /* WriteLabelItemValue() */
1361 : /************************************************************************/
1362 :
1363 1931 : static void WriteLabelItemValue(std::string &osLabel, const CPLJSONObject &obj)
1364 : {
1365 1931 : const auto eType(obj.GetType());
1366 1931 : if (eType == CPLJSONObject::Type::Boolean)
1367 : {
1368 2 : osLabel += CPLSPrintf("%d", obj.ToBool() ? 1 : 0);
1369 : }
1370 1929 : else if (eType == CPLJSONObject::Type::Integer)
1371 : {
1372 797 : osLabel += CPLSPrintf("%d", obj.ToInteger());
1373 : }
1374 1132 : else if (eType == CPLJSONObject::Type::Long)
1375 : {
1376 : std::string osVal(
1377 2 : CPLSPrintf("%.17g", static_cast<double>(obj.ToLong())));
1378 1 : if (osVal.find('.') == std::string::npos)
1379 1 : osVal += ".0";
1380 1 : osLabel += osVal;
1381 : }
1382 1131 : else if (eType == CPLJSONObject::Type::Double)
1383 : {
1384 324 : double dfVal = obj.ToDouble();
1385 324 : if (dfVal >= static_cast<double>(std::numeric_limits<GIntBig>::min()) &&
1386 648 : dfVal <= static_cast<double>(std::numeric_limits<GIntBig>::max()) &&
1387 324 : static_cast<double>(static_cast<GIntBig>(dfVal)) == dfVal)
1388 : {
1389 212 : std::string osVal(CPLSPrintf("%.17g", dfVal));
1390 106 : if (osVal.find('.') == std::string::npos)
1391 106 : osVal += ".0";
1392 106 : osLabel += osVal;
1393 : }
1394 : else
1395 : {
1396 218 : osLabel += CPLSPrintf("%.15g", dfVal);
1397 : }
1398 : }
1399 807 : else if (eType == CPLJSONObject::Type::String)
1400 : {
1401 796 : osLabel += SerializeString(obj.ToString());
1402 : }
1403 11 : else if (eType == CPLJSONObject::Type::Array)
1404 : {
1405 18 : const auto oArray = obj.ToArray();
1406 9 : osLabel += '(';
1407 27 : for (int i = 0; i < oArray.Size(); i++)
1408 : {
1409 18 : if (i > 0)
1410 9 : osLabel += ',';
1411 18 : WriteLabelItemValue(osLabel, oArray[i]);
1412 : }
1413 9 : osLabel += ')';
1414 : }
1415 2 : else if (eType == CPLJSONObject::Type::Null)
1416 : {
1417 1 : osLabel += "'NULL'";
1418 : }
1419 : else
1420 : {
1421 : osLabel +=
1422 1 : SerializeString(obj.Format(CPLJSONObject::PrettyFormat::Plain));
1423 : }
1424 1931 : }
1425 :
1426 : /************************************************************************/
1427 : /* SanitizeItemName() */
1428 : /************************************************************************/
1429 :
1430 1913 : static std::string SanitizeItemName(const std::string &osItemName)
1431 : {
1432 3826 : std::string osRet(osItemName);
1433 1913 : if (osRet.size() > 32)
1434 0 : osRet.resize(32);
1435 1913 : if (osRet.empty())
1436 0 : return "UNNAMED";
1437 1913 : if (osRet[0] < 'A' || osRet[0] > 'Z')
1438 0 : osRet[0] = 'X'; // item name must start with a letter
1439 14570 : for (size_t i = 1; i < osRet.size(); i++)
1440 : {
1441 12657 : char ch = osRet[i];
1442 12657 : if (ch >= 'a' && ch <= 'z')
1443 0 : osRet[i] = ch - 'a' + 'A';
1444 12657 : else if (!((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ||
1445 : ch == '_'))
1446 : {
1447 0 : osRet[i] = '_';
1448 : }
1449 : }
1450 1913 : if (osRet != osItemName)
1451 : {
1452 0 : CPLError(CE_Warning, CPLE_AppDefined,
1453 : "Label item name %s has been sanitized to %s",
1454 : osItemName.c_str(), osRet.c_str());
1455 : }
1456 1913 : return osRet;
1457 : }
1458 :
1459 : /************************************************************************/
1460 : /* WriteLabelItem() */
1461 : /************************************************************************/
1462 :
1463 1913 : static void WriteLabelItem(std::string &osLabel, const CPLJSONObject &obj,
1464 : const std::string &osItemName = std::string())
1465 : {
1466 1913 : osLabel += ' ';
1467 : osLabel +=
1468 1913 : SanitizeItemName(osItemName.empty() ? obj.GetName() : osItemName);
1469 1913 : osLabel += '=';
1470 1913 : WriteLabelItemValue(osLabel, obj);
1471 1913 : }
1472 :
1473 : /************************************************************************/
1474 : /* WriteLabel() */
1475 : /************************************************************************/
1476 :
1477 53 : void VICARDataset::WriteLabel()
1478 : {
1479 53 : m_bIsLabelWritten = true;
1480 :
1481 53 : if (!m_oJSonLabel.IsValid())
1482 53 : BuildLabel();
1483 :
1484 106 : std::string osLabel;
1485 106 : auto children = m_oJSonLabel.GetChildren();
1486 1504 : for (const auto &child : children)
1487 : {
1488 1451 : const auto osName(child.GetName());
1489 1451 : if (osName == "LBLSIZE" || osName == "PROPERTY" || osName == "TASK")
1490 110 : continue;
1491 2682 : std::string osNameSubst;
1492 1341 : if (osName == "DAT_TIM" || osName == "USER")
1493 : {
1494 2 : osNameSubst = osName + '_';
1495 : }
1496 1341 : WriteLabelItem(osLabel, child, osNameSubst);
1497 : }
1498 :
1499 159 : auto property = m_oJSonLabel.GetObj("PROPERTY");
1500 53 : if (property.IsValid() && property.GetType() == CPLJSONObject::Type::Object)
1501 : {
1502 46 : children = property.GetChildren();
1503 89 : for (const auto &child : children)
1504 : {
1505 43 : if (child.GetType() == CPLJSONObject::Type::Object)
1506 : {
1507 43 : osLabel += " PROPERTY=" + SerializeString(child.GetName());
1508 86 : auto childrenProperty = child.GetChildren();
1509 551 : for (const auto &childProperty : childrenProperty)
1510 : {
1511 1016 : const auto osName(child.GetName());
1512 1016 : std::string osNameSubst;
1513 1524 : if (osName == "LBLSIZE" || osName == "PROPERTY" ||
1514 2032 : osName == "TASK" || osName == "DAT_TIM" ||
1515 508 : osName == "USER")
1516 : {
1517 0 : osNameSubst = osName + '_';
1518 : }
1519 508 : WriteLabelItem(osLabel, childProperty, osNameSubst);
1520 : }
1521 : }
1522 : }
1523 : }
1524 :
1525 159 : auto task = m_oJSonLabel.GetObj("TASK");
1526 53 : if (task.IsValid() && task.GetType() == CPLJSONObject::Type::Object)
1527 : {
1528 11 : children = task.GetChildren();
1529 22 : for (const auto &child : children)
1530 : {
1531 11 : if (child.GetType() == CPLJSONObject::Type::Object)
1532 : {
1533 11 : osLabel += " TASK=" + SerializeString(child.GetName());
1534 33 : auto oUser = child.GetObj("USER");
1535 11 : if (oUser.IsValid())
1536 10 : WriteLabelItem(osLabel, oUser);
1537 33 : auto oDatTim = child.GetObj("DAT_TIM");
1538 11 : if (oDatTim.IsValid())
1539 11 : WriteLabelItem(osLabel, oDatTim);
1540 22 : auto childrenProperty = child.GetChildren();
1541 54 : for (const auto &childProperty : childrenProperty)
1542 : {
1543 43 : const auto osName(child.GetName());
1544 43 : if (osName == "USER" || osName == "DAT_TIM")
1545 0 : continue;
1546 86 : std::string osNameSubst;
1547 86 : if (osName == "LBLSIZE" || osName == "PROPERTY" ||
1548 43 : osName == "TASK")
1549 : {
1550 10 : osNameSubst = osName + '_';
1551 : }
1552 43 : WriteLabelItem(osLabel, childProperty, osNameSubst);
1553 : }
1554 : }
1555 : }
1556 : }
1557 :
1558 : // Figure out label size, round it to the next multiple of RECSIZE
1559 53 : constexpr size_t MAX_LOG10_LBLSIZE = 10;
1560 53 : size_t nLabelSize = strlen("LBLSIZE=") + MAX_LOG10_LBLSIZE + osLabel.size();
1561 53 : nLabelSize =
1562 53 : (nLabelSize + m_nRecordSize - 1) / m_nRecordSize * m_nRecordSize;
1563 : std::string osLabelSize(
1564 159 : CPLSPrintf("LBLSIZE=%d", static_cast<int>(nLabelSize)));
1565 421 : while (osLabelSize.size() < strlen("LBLSIZE=") + MAX_LOG10_LBLSIZE)
1566 368 : osLabelSize += ' ';
1567 53 : osLabel = osLabelSize + osLabel;
1568 53 : CPLAssert(osLabel.size() <= nLabelSize);
1569 :
1570 : // Write label
1571 53 : VSIFSeekL(fpImage, 0, SEEK_SET);
1572 53 : VSIFWriteL(osLabel.data(), 1, osLabel.size(), fpImage);
1573 53 : const size_t nZeroPadding = nLabelSize - osLabel.size();
1574 53 : if (nZeroPadding)
1575 : {
1576 50 : VSIFWriteL(std::string(nZeroPadding, '\0').data(), 1, nZeroPadding,
1577 : fpImage);
1578 : }
1579 :
1580 53 : if (m_bInitToNodata && m_eCompress == COMPRESS_NONE)
1581 : {
1582 : const int nDTSize =
1583 20 : GDALGetDataTypeSizeBytes(GetRasterBand(1)->GetRasterDataType());
1584 20 : VSIFTruncateL(fpImage, VSIFTellL(fpImage) +
1585 20 : static_cast<vsi_l_offset>(nRasterXSize) *
1586 20 : nRasterYSize * nBands * nDTSize);
1587 : }
1588 :
1589 : // Patch band offsets to take into account label
1590 140 : for (int i = 0; i < nBands; i++)
1591 : {
1592 87 : auto poBand = dynamic_cast<VICARRawRasterBand *>(GetRasterBand(i + 1));
1593 87 : if (poBand)
1594 82 : poBand->nImgOffset += nLabelSize;
1595 : }
1596 53 : }
1597 :
1598 : /************************************************************************/
1599 : /* PatchLabel() */
1600 : /************************************************************************/
1601 :
1602 166 : void VICARDataset::PatchLabel()
1603 : {
1604 166 : if (eAccess == GA_ReadOnly || m_eCompress == COMPRESS_NONE)
1605 161 : return;
1606 :
1607 5 : VSIFSeekL(fpImage, 0, SEEK_END);
1608 5 : const vsi_l_offset nFileSize = VSIFTellL(fpImage);
1609 5 : VSIFSeekL(fpImage, 0, SEEK_SET);
1610 10 : std::string osBuffer;
1611 5 : osBuffer.resize(1024);
1612 5 : size_t nRead = VSIFReadL(&osBuffer[0], 1, 1024, fpImage);
1613 :
1614 : {
1615 5 : CPLString osEOCI1;
1616 5 : osEOCI1.Printf("%u", static_cast<unsigned>(nFileSize));
1617 36 : while (osEOCI1.size() < 10)
1618 31 : osEOCI1 += ' ';
1619 5 : size_t nPos = osBuffer.find("EOCI1=");
1620 5 : CPLAssert(nPos <= nRead - (strlen("EOCI1=") + 10));
1621 5 : memcpy(&osBuffer[nPos + strlen("EOCI1=")], osEOCI1.data(), 10);
1622 : }
1623 :
1624 : {
1625 5 : CPLString osEOCI2;
1626 5 : osEOCI2.Printf("%u", static_cast<unsigned>(nFileSize >> 32));
1627 50 : while (osEOCI2.size() < 10)
1628 45 : osEOCI2 += ' ';
1629 5 : size_t nPos = osBuffer.find("EOCI2=");
1630 5 : CPLAssert(nPos <= nRead - (strlen("EOCI2=") + 10));
1631 5 : memcpy(&osBuffer[nPos + strlen("EOCI2=")], osEOCI2.data(), 10);
1632 : }
1633 5 : VSIFSeekL(fpImage, 0, SEEK_SET);
1634 5 : VSIFWriteL(&osBuffer[0], 1, nRead, fpImage);
1635 : }
1636 :
1637 : /************************************************************************/
1638 : /* BuildLabel() */
1639 : /************************************************************************/
1640 :
1641 53 : void VICARDataset::BuildLabel()
1642 : {
1643 106 : CPLJSONObject oLabel = m_oSrcJSonLabel;
1644 53 : if (!oLabel.IsValid())
1645 : {
1646 38 : oLabel = CPLJSONObject();
1647 : }
1648 :
1649 53 : oLabel.Set("LBLSIZE", 0); // to be overridden later
1650 :
1651 53 : if (!oLabel.GetObj("TYPE").IsValid())
1652 41 : oLabel.Set("TYPE", "IMAGE");
1653 :
1654 53 : const auto eType = GetRasterBand(1)->GetRasterDataType();
1655 53 : const char *pszFormat = "";
1656 53 : CPL_IGNORE_RET_VAL(pszFormat); // Make CSA happy
1657 53 : switch (eType)
1658 : {
1659 33 : case GDT_Byte:
1660 33 : pszFormat = "BYTE";
1661 33 : break;
1662 5 : case GDT_Int16:
1663 5 : pszFormat = "HALF";
1664 5 : break;
1665 3 : case GDT_Int32:
1666 3 : pszFormat = "FULL";
1667 3 : break;
1668 4 : case GDT_Float32:
1669 4 : pszFormat = "REAL";
1670 4 : break;
1671 4 : case GDT_Float64:
1672 4 : pszFormat = "DOUB";
1673 4 : break;
1674 4 : case GDT_CFloat32:
1675 4 : pszFormat = "COMP";
1676 4 : break;
1677 0 : default:
1678 0 : CPLAssert(false);
1679 : break;
1680 : }
1681 53 : oLabel.Set("FORMAT", pszFormat);
1682 :
1683 53 : oLabel.Set("BUFSIZ", m_nRecordSize); // arbitrary value
1684 53 : oLabel.Set("DIM", 3);
1685 53 : oLabel.Set("EOL", 0);
1686 53 : oLabel.Set("RECSIZE", m_nRecordSize);
1687 53 : oLabel.Set("ORG", "BSQ");
1688 53 : oLabel.Set("NL", nRasterYSize);
1689 53 : oLabel.Set("NS", nRasterXSize);
1690 53 : oLabel.Set("NB", nBands);
1691 53 : oLabel.Set("N1", nRasterXSize);
1692 53 : oLabel.Set("N2", nRasterYSize);
1693 53 : oLabel.Set("N3", nBands);
1694 53 : oLabel.Set("N4", 0);
1695 53 : oLabel.Set("NBB", 0);
1696 53 : oLabel.Set("NLB", 0);
1697 53 : oLabel.Set("HOST", "X86-64-LINX");
1698 53 : oLabel.Set("INTFMT", "LOW");
1699 53 : oLabel.Set("REALFMT", "RIEEE");
1700 53 : oLabel.Set("BHOST", "X86-64-LINX");
1701 53 : oLabel.Set("BINTFMT", "LOW");
1702 53 : if (!oLabel.GetObj("BLTYPE").IsValid())
1703 38 : oLabel.Set("BLTYPE", "");
1704 104 : oLabel.Set("COMPRESS", m_eCompress == COMPRESS_BASIC ? "BASIC"
1705 51 : : m_eCompress == COMPRESS_BASIC2 ? "BASIC2"
1706 : : "NONE");
1707 53 : if (m_eCompress == COMPRESS_NONE)
1708 : {
1709 48 : oLabel.Set("EOCI1", 0);
1710 48 : oLabel.Set("EOCI2", 0);
1711 : }
1712 : else
1713 : {
1714 : // To be later patched. Those fake values must take 10 bytes
1715 : // (8 + 2 single quotes) so that they can be later replaced by a
1716 : // integer of maximum value 4294967295 (10 digits)
1717 5 : oLabel.Set("EOCI1", "XXXXXXXX");
1718 5 : oLabel.Set("EOCI2", "XXXXXXXX");
1719 : }
1720 :
1721 53 : if (m_bUseSrcMap)
1722 : {
1723 0 : auto oMap = oLabel.GetObj("PROPERTY/MAP");
1724 0 : if (oMap.IsValid() && oMap.GetType() == CPLJSONObject::Type::Object)
1725 : {
1726 0 : if (!m_osTargetName.empty())
1727 0 : oMap.Set("TARGET_NAME", m_osTargetName);
1728 0 : if (!m_osLatitudeType.empty())
1729 0 : oMap.Set("COORDINATE_SYSTEM_NAME", m_osLatitudeType);
1730 0 : if (!m_osLongitudeDirection.empty())
1731 0 : oMap.Set("POSITIVE_LONGITUDE_DIRECTION",
1732 : m_osLongitudeDirection);
1733 : }
1734 : }
1735 53 : else if (m_bGeoRefFormatIsMIPL)
1736 : {
1737 153 : auto oProperty = oLabel.GetObj("PROPERTY");
1738 51 : if (oProperty.IsValid())
1739 : {
1740 8 : oProperty.Delete("MAP");
1741 8 : oProperty.Delete("GEOTIFF");
1742 : }
1743 51 : if (!m_oSRS.IsEmpty())
1744 : {
1745 37 : BuildLabelPropertyMap(oLabel);
1746 : }
1747 : }
1748 : else
1749 : {
1750 6 : auto oProperty = oLabel.GetObj("PROPERTY");
1751 2 : if (oProperty.IsValid())
1752 : {
1753 1 : oProperty.Delete("MAP");
1754 1 : oProperty.Delete("GEOTIFF");
1755 : }
1756 2 : if (!m_oSRS.IsEmpty())
1757 : {
1758 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
1759 2 : BuildLabelPropertyGeoTIFF(oLabel);
1760 : #endif
1761 : }
1762 : }
1763 :
1764 53 : m_oJSonLabel = std::move(oLabel);
1765 53 : }
1766 :
1767 : /************************************************************************/
1768 : /* BuildLabelPropertyMap() */
1769 : /************************************************************************/
1770 :
1771 37 : void VICARDataset::BuildLabelPropertyMap(CPLJSONObject &oLabel)
1772 : {
1773 37 : if (m_oSRS.IsProjected() || m_oSRS.IsGeographic())
1774 : {
1775 111 : auto oProperty = GetOrCreateJSONObject(oLabel, "PROPERTY");
1776 111 : auto oMap = GetOrCreateJSONObject(oProperty, "MAP");
1777 :
1778 37 : const char *pszDatum = m_oSRS.GetAttrValue("DATUM");
1779 74 : CPLString osTargetName(m_osTargetName);
1780 37 : if (osTargetName.empty())
1781 : {
1782 37 : if (pszDatum && STARTS_WITH(pszDatum, "D_"))
1783 : {
1784 2 : osTargetName = pszDatum + 2;
1785 : }
1786 35 : else if (pszDatum)
1787 : {
1788 35 : osTargetName = pszDatum;
1789 : }
1790 : }
1791 37 : if (!osTargetName.empty())
1792 37 : oMap.Add("TARGET_NAME", osTargetName);
1793 :
1794 37 : oMap.Add("A_AXIS_RADIUS", m_oSRS.GetSemiMajor() / 1000.0);
1795 37 : oMap.Add("B_AXIS_RADIUS", m_oSRS.GetSemiMajor() / 1000.0);
1796 37 : oMap.Add("C_AXIS_RADIUS", m_oSRS.GetSemiMinor() / 1000.0);
1797 :
1798 37 : if (!m_osLatitudeType.empty())
1799 0 : oMap.Add("COORDINATE_SYSTEM_NAME", m_osLatitudeType);
1800 : else
1801 37 : oMap.Add("COORDINATE_SYSTEM_NAME", "PLANETOCENTRIC");
1802 :
1803 37 : if (!m_osLongitudeDirection.empty())
1804 0 : oMap.Add("POSITIVE_LONGITUDE_DIRECTION", m_osLongitudeDirection);
1805 : else
1806 37 : oMap.Add("POSITIVE_LONGITUDE_DIRECTION", "EAST");
1807 :
1808 37 : const char *pszProjection = m_oSRS.GetAttrValue("PROJECTION");
1809 37 : if (pszProjection == nullptr)
1810 : {
1811 35 : oMap.Add("MAP_PROJECTION_TYPE", "SIMPLE_CYLINDRICAL");
1812 35 : oMap.Add("CENTER_LONGITUDE", 0.0);
1813 35 : oMap.Add("CENTER_LATITUDE", 0.0);
1814 : }
1815 2 : else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
1816 : {
1817 0 : oMap.Add("MAP_PROJECTION_TYPE", "EQUIRECTANGULAR");
1818 0 : if (m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
1819 : {
1820 0 : CPLError(CE_Warning, CPLE_NotSupported,
1821 : "Ignoring %s. Only 0 value supported",
1822 : SRS_PP_LATITUDE_OF_ORIGIN);
1823 : }
1824 0 : oMap.Add("CENTER_LONGITUDE",
1825 : m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
1826 : const double dfCenterLat =
1827 0 : m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1828 0 : oMap.Add("CENTER_LATITUDE", dfCenterLat);
1829 : }
1830 2 : else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
1831 : {
1832 2 : oMap.Add("MAP_PROJECTION_TYPE", "SINUSOIDAL");
1833 2 : oMap.Add("CENTER_LONGITUDE",
1834 : m_oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0));
1835 2 : oMap.Add("CENTER_LATITUDE", 0.0);
1836 : }
1837 : else
1838 : {
1839 0 : CPLError(CE_Warning, CPLE_NotSupported,
1840 : "Projection %s not supported", pszProjection);
1841 : }
1842 :
1843 37 : if (oMap["MAP_PROJECTION_TYPE"].IsValid())
1844 : {
1845 37 : if (m_oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
1846 : {
1847 0 : CPLError(CE_Warning, CPLE_NotSupported,
1848 : "Ignoring %s. Only 0 value supported",
1849 : SRS_PP_FALSE_EASTING);
1850 : }
1851 37 : if (m_oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
1852 : {
1853 0 : CPLError(CE_Warning, CPLE_AppDefined,
1854 : "Ignoring %s. Only 0 value supported",
1855 : SRS_PP_FALSE_NORTHING);
1856 : }
1857 :
1858 37 : if (m_bGotTransform)
1859 : {
1860 : const double dfDegToMeter =
1861 37 : m_oSRS.GetSemiMajor() * M_PI / 180.0;
1862 37 : if (m_oSRS.IsProjected())
1863 : {
1864 2 : const double dfLinearUnits = m_oSRS.GetLinearUnits();
1865 2 : const double dfScale = m_adfGeoTransform[1] * dfLinearUnits;
1866 2 : oMap.Add("SAMPLE_PROJECTION_OFFSET",
1867 2 : -m_adfGeoTransform[0] * dfLinearUnits / dfScale -
1868 : 0.5);
1869 2 : oMap.Add("LINE_PROJECTION_OFFSET",
1870 2 : m_adfGeoTransform[3] * dfLinearUnits / dfScale -
1871 : 0.5);
1872 2 : oMap.Add("MAP_SCALE", dfScale / 1000.0);
1873 : }
1874 35 : else if (m_oSRS.IsGeographic())
1875 : {
1876 35 : const double dfScale = m_adfGeoTransform[1] * dfDegToMeter;
1877 35 : oMap.Add("SAMPLE_PROJECTION_OFFSET",
1878 35 : -m_adfGeoTransform[0] * dfDegToMeter / dfScale -
1879 : 0.5);
1880 35 : oMap.Add("LINE_PROJECTION_OFFSET",
1881 35 : m_adfGeoTransform[3] * dfDegToMeter / dfScale -
1882 : 0.5);
1883 35 : oMap.Add("MAP_SCALE", dfScale / 1000.0);
1884 : }
1885 : }
1886 : }
1887 : }
1888 : else
1889 : {
1890 0 : CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
1891 : }
1892 37 : }
1893 :
1894 : /************************************************************************/
1895 : /* BuildLabelPropertyGeoTIFF() */
1896 : /************************************************************************/
1897 :
1898 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
1899 2 : void VICARDataset::BuildLabelPropertyGeoTIFF(CPLJSONObject &oLabel)
1900 : {
1901 4 : auto oProperty = GetOrCreateJSONObject(oLabel, "PROPERTY");
1902 4 : auto oGeoTIFF = GetOrCreateJSONObject(oProperty, "GEOTIFF");
1903 :
1904 : // Ported from Vicar Open Source: Afids expects to be able to read
1905 : // NITF_NROWS and NITF_NCOLS
1906 :
1907 2 : oGeoTIFF.Add("NITF_NROWS", nRasterYSize);
1908 2 : oGeoTIFF.Add("NITF_NCOLS", nRasterXSize);
1909 :
1910 : // Create a in-memory GeoTIFF file
1911 :
1912 : const std::string osTmpFilename(
1913 2 : VSIMemGenerateHiddenFilename("vicar_tmp.tif"));
1914 : GDALDriver *poGTiffDriver =
1915 2 : GDALDriver::FromHandle(GDALGetDriverByName("GTiff"));
1916 2 : if (poGTiffDriver == nullptr)
1917 : {
1918 0 : CPLError(CE_Failure, CPLE_AppDefined, "GTiff driver not available");
1919 0 : return;
1920 : }
1921 2 : const char *const apszOptions[] = {"GEOTIFF_VERSION=1.0", nullptr};
1922 : auto poDS = std::unique_ptr<GDALDataset>(poGTiffDriver->Create(
1923 2 : osTmpFilename.c_str(), 1, 1, 1, GDT_Byte, apszOptions));
1924 2 : if (!poDS)
1925 0 : return;
1926 2 : poDS->SetSpatialRef(&m_oSRS);
1927 2 : if (m_bGotTransform)
1928 2 : poDS->SetGeoTransform(&m_adfGeoTransform[0]);
1929 4 : poDS->SetMetadataItem(GDALMD_AREA_OR_POINT,
1930 2 : GetMetadataItem(GDALMD_AREA_OR_POINT));
1931 2 : poDS.reset();
1932 :
1933 : // Open it with libtiff/libgeotiff
1934 2 : VSILFILE *fpL = VSIFOpenL(osTmpFilename.c_str(), "r");
1935 2 : if (fpL == nullptr)
1936 : {
1937 0 : VSIUnlink(osTmpFilename.c_str());
1938 0 : return;
1939 : }
1940 :
1941 2 : TIFF *hTIFF = VSI_TIFFOpen(osTmpFilename.c_str(), "r", fpL);
1942 2 : CPLAssert(hTIFF);
1943 :
1944 2 : GTIF *hGTIF = GTIFNew(hTIFF);
1945 2 : CPLAssert(hGTIF);
1946 :
1947 : // Get geotiff keys and write them as VICAR metadata
1948 34 : for (const auto &gkey : GTiffShortKeys)
1949 : {
1950 32 : unsigned short val = 0;
1951 32 : if (GDALGTIFKeyGetSHORT(hGTIF, gkey, &val, 0, 1))
1952 : {
1953 20 : oGeoTIFF.Add(
1954 20 : CPLString(GTIFKeyName(gkey)).toupper(),
1955 : CPLSPrintf("%d(%s)", val, GTIFValueNameEx(hGTIF, gkey, val)));
1956 : }
1957 : }
1958 :
1959 62 : for (const auto &gkey : GTiffDoubleKeys)
1960 : {
1961 60 : double val = 0;
1962 60 : if (GDALGTIFKeyGetDOUBLE(hGTIF, gkey, &val, 0, 1))
1963 : {
1964 12 : oGeoTIFF.Add(CPLString(GTIFKeyName(gkey)).toupper(),
1965 : CPLSPrintf("%.17g", val));
1966 : }
1967 : }
1968 :
1969 10 : for (const auto &gkey : GTiffAsciiKeys)
1970 : {
1971 : char szAscii[1024];
1972 8 : if (GDALGTIFKeyGetASCII(hGTIF, gkey, szAscii,
1973 8 : static_cast<int>(sizeof(szAscii))))
1974 : {
1975 4 : oGeoTIFF.Add(CPLString(GTIFKeyName(gkey)).toupper(), szAscii);
1976 : }
1977 : }
1978 :
1979 2 : GTIFFree(hGTIF);
1980 :
1981 : // Get geotiff tags and write them as VICAR metadata
1982 : const std::map<int, const char *> oMapTagCodeToName = {
1983 : {TIFFTAG_GEOPIXELSCALE, "MODELPIXELSCALETAG"},
1984 : {TIFFTAG_GEOTIEPOINTS, "MODELTIEPOINTTAG"},
1985 4 : {TIFFTAG_GEOTRANSMATRIX, "MODELTRANSFORMATIONTAG"}};
1986 :
1987 8 : for (const auto &kv : oMapTagCodeToName)
1988 : {
1989 6 : uint16_t nCount = 0;
1990 6 : double *padfValues = nullptr;
1991 6 : if (TIFFGetField(hTIFF, kv.first, &nCount, &padfValues))
1992 : {
1993 4 : std::string osVal("(");
1994 22 : for (uint16_t i = 0; i < nCount; ++i)
1995 : {
1996 18 : if (i > 0)
1997 14 : osVal += ',';
1998 18 : osVal += CPLSPrintf("%.17g", padfValues[i]);
1999 : }
2000 4 : osVal += ')';
2001 4 : oGeoTIFF.Add(kv.second, osVal);
2002 : }
2003 : }
2004 :
2005 2 : XTIFFClose(hTIFF);
2006 2 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
2007 2 : VSIUnlink(osTmpFilename.c_str());
2008 : }
2009 : #endif
2010 :
2011 : /************************************************************************/
2012 : /* ReadProjectionFromMapGroup() */
2013 : /************************************************************************/
2014 :
2015 27 : void VICARDataset::ReadProjectionFromMapGroup()
2016 : {
2017 27 : double dfXDim = 1.0;
2018 27 : double dfYDim = 1.0;
2019 :
2020 27 : const char *value = GetKeyword("MAP.MAP_SCALE");
2021 27 : if (strlen(value) > 0)
2022 : {
2023 27 : dfXDim = CPLAtof(value) * 1000.0;
2024 27 : dfYDim = CPLAtof(value) * -1 * 1000.0;
2025 : }
2026 :
2027 : const double dfSampleOffset_Shift =
2028 27 : CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Shift", "0.5"));
2029 :
2030 : const double dfLineOffset_Shift =
2031 27 : CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Shift", "0.5"));
2032 :
2033 : const double dfSampleOffset_Mult =
2034 27 : CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Mult", "-1.0"));
2035 :
2036 : const double dfLineOffset_Mult =
2037 27 : CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Mult", "1.0"));
2038 :
2039 : /*********** Grab LINE_PROJECTION_OFFSET ************/
2040 27 : double dfULYMap = 0.5;
2041 :
2042 27 : value = GetKeyword("MAP.LINE_PROJECTION_OFFSET");
2043 27 : if (strlen(value) > 0)
2044 : {
2045 27 : const double yulcenter = CPLAtof(value);
2046 27 : dfULYMap =
2047 27 : ((yulcenter + dfLineOffset_Shift) * -dfYDim * dfLineOffset_Mult);
2048 : }
2049 : /*********** Grab SAMPLE_PROJECTION_OFFSET ************/
2050 27 : double dfULXMap = 0.5;
2051 :
2052 27 : value = GetKeyword("MAP.SAMPLE_PROJECTION_OFFSET");
2053 27 : if (strlen(value) > 0)
2054 : {
2055 27 : const double xulcenter = CPLAtof(value);
2056 27 : dfULXMap =
2057 27 : ((xulcenter + dfSampleOffset_Shift) * dfXDim * dfSampleOffset_Mult);
2058 : }
2059 :
2060 : /* ==================================================================== */
2061 : /* Get the coordinate system. */
2062 : /* ==================================================================== */
2063 27 : bool bProjectionSet = true;
2064 :
2065 : /*********** Grab TARGET_NAME ************/
2066 : /**** This is the planets name i.e. MARS ***/
2067 54 : const CPLString target_name = GetKeyword("MAP.TARGET_NAME");
2068 :
2069 : /********** Grab MAP_PROJECTION_TYPE *****/
2070 54 : const CPLString map_proj_name = GetKeyword("MAP.MAP_PROJECTION_TYPE");
2071 :
2072 : /****** Grab semi_major & convert to KM ******/
2073 27 : const double semi_major = CPLAtof(GetKeyword("MAP.A_AXIS_RADIUS")) * 1000.0;
2074 :
2075 : /****** Grab semi-minor & convert to KM ******/
2076 27 : const double semi_minor = CPLAtof(GetKeyword("MAP.C_AXIS_RADIUS")) * 1000.0;
2077 :
2078 : /*********** Grab CENTER_LAT ************/
2079 27 : const double center_lat = CPLAtof(GetKeyword("MAP.CENTER_LATITUDE"));
2080 :
2081 : /*********** Grab CENTER_LON ************/
2082 27 : const double center_lon = CPLAtof(GetKeyword("MAP.CENTER_LONGITUDE"));
2083 :
2084 : /********** Grab 1st std parallel *******/
2085 : const double first_std_parallel =
2086 27 : CPLAtof(GetKeyword("MAP.FIRST_STANDARD_PARALLEL"));
2087 :
2088 : /********** Grab 2nd std parallel *******/
2089 : const double second_std_parallel =
2090 27 : CPLAtof(GetKeyword("MAP.SECOND_STANDARD_PARALLEL"));
2091 :
2092 : /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
2093 : // Need to further study how ocentric/ographic will effect the gdal library.
2094 : // So far we will use this fact to define a sphere or ellipse for some
2095 : // projections Frank - may need to talk this over
2096 27 : bool bIsGeographic = true;
2097 27 : value = GetKeyword("MAP.COORDINATE_SYSTEM_NAME");
2098 27 : if (EQUAL(value, "PLANETOCENTRIC"))
2099 27 : bIsGeographic = false;
2100 :
2101 : /** Set oSRS projection and parameters --- all PDS supported types added
2102 : if apparently supported in oSRS "AITOFF", ** Not supported in GDAL??
2103 : "ALBERS",
2104 : "BONNE",
2105 : "BRIESEMEISTER", ** Not supported in GDAL??
2106 : "CYLINDRICAL EQUAL AREA",
2107 : "EQUIDISTANT",
2108 : "EQUIRECTANGULAR",
2109 : "GNOMONIC",
2110 : "HAMMER", ** Not supported in GDAL??
2111 : "HENDU", ** Not supported in GDAL??
2112 : "LAMBERT AZIMUTHAL EQUAL AREA",
2113 : "LAMBERT CONFORMAL",
2114 : "MERCATOR",
2115 : "MOLLWEIDE",
2116 : "OBLIQUE CYLINDRICAL",
2117 : "ORTHOGRAPHIC",
2118 : "SIMPLE CYLINDRICAL",
2119 : "SINUSOIDAL",
2120 : "STEREOGRAPHIC",
2121 : "TRANSVERSE MERCATOR",
2122 : "VAN DER GRINTEN", ** Not supported in GDAL??
2123 : "WERNER" ** Not supported in GDAL??
2124 : **/
2125 27 : CPLDebug("PDS", "using projection %s\n\n", map_proj_name.c_str());
2126 :
2127 54 : OGRSpatialReference oSRS;
2128 :
2129 27 : if ((EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
2130 39 : (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
2131 12 : (EQUAL(map_proj_name, "EQUIDISTANT")))
2132 : {
2133 15 : oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
2134 : }
2135 12 : else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
2136 : {
2137 0 : oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
2138 : }
2139 12 : else if (EQUAL(map_proj_name, "SINUSOIDAL"))
2140 : {
2141 12 : oSRS.SetSinusoidal(center_lon, 0, 0);
2142 : }
2143 0 : else if (EQUAL(map_proj_name, "MERCATOR"))
2144 : {
2145 0 : oSRS.SetMercator(center_lat, center_lon, 1, 0, 0);
2146 : }
2147 0 : else if (EQUAL(map_proj_name, "STEREOGRAPHIC"))
2148 : {
2149 0 : if ((fabs(center_lat) - 90) < 0.0000001)
2150 : {
2151 0 : oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
2152 : }
2153 : else
2154 : {
2155 0 : oSRS.SetStereographic(center_lat, center_lon, 1, 0, 0);
2156 : }
2157 : }
2158 0 : else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
2159 : {
2160 0 : oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
2161 : }
2162 0 : else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
2163 : {
2164 0 : oSRS.SetTM(center_lat, center_lon, 1, 0, 0);
2165 : }
2166 0 : else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
2167 : {
2168 0 : oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
2169 : center_lon, 0, 0);
2170 : }
2171 0 : else if (EQUAL(map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA"))
2172 : {
2173 0 : oSRS.SetLAEA(center_lat, center_lon, 0, 0);
2174 : }
2175 0 : else if (EQUAL(map_proj_name, "CYLINDRICAL_EQUAL_AREA"))
2176 : {
2177 0 : oSRS.SetCEA(first_std_parallel, center_lon, 0, 0);
2178 : }
2179 0 : else if (EQUAL(map_proj_name, "MOLLWEIDE"))
2180 : {
2181 0 : oSRS.SetMollweide(center_lon, 0, 0);
2182 : }
2183 0 : else if (EQUAL(map_proj_name, "ALBERS"))
2184 : {
2185 0 : oSRS.SetACEA(first_std_parallel, second_std_parallel, center_lat,
2186 : center_lon, 0, 0);
2187 : }
2188 0 : else if (EQUAL(map_proj_name, "BONNE"))
2189 : {
2190 0 : oSRS.SetBonne(first_std_parallel, center_lon, 0, 0);
2191 : }
2192 0 : else if (EQUAL(map_proj_name, "GNOMONIC"))
2193 : {
2194 0 : oSRS.SetGnomonic(center_lat, center_lon, 0, 0);
2195 : #ifdef FIXME
2196 : }
2197 : else if (EQUAL(map_proj_name, "OBLIQUE_CYLINDRICAL"))
2198 : {
2199 : // hope Swiss Oblique Cylindrical is the same
2200 : oSRS.SetSOC(center_lat, center_lon, 0, 0);
2201 : #endif
2202 : }
2203 : else
2204 : {
2205 0 : CPLDebug("VICAR",
2206 : "Dataset projection %s is not supported. Continuing...",
2207 : map_proj_name.c_str());
2208 0 : bProjectionSet = false;
2209 : }
2210 :
2211 27 : if (bProjectionSet)
2212 : {
2213 : // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
2214 81 : const CPLString proj_target_name = map_proj_name + " " + target_name;
2215 27 : oSRS.SetProjCS(proj_target_name); // set ProjCS keyword
2216 :
2217 : // The geographic/geocentric name will be the same basic name as the
2218 : // body name 'GCS' = Geographic/Geocentric Coordinate System
2219 54 : const CPLString geog_name = "GCS_" + target_name;
2220 :
2221 : // The datum and sphere names will be the same basic name aas the planet
2222 54 : const CPLString datum_name = "D_" + target_name;
2223 54 : CPLString sphere_name = target_name; // + "_IAU_IAG"); //Might not be
2224 : // IAU defined so don't add
2225 :
2226 : // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
2227 27 : double iflattening = 0.0;
2228 27 : if ((semi_major - semi_minor) < 0.0000001)
2229 12 : iflattening = 0;
2230 : else
2231 15 : iflattening = semi_major / (semi_major - semi_minor);
2232 :
2233 : // Set the body size but take into consideration which proj is being
2234 : // used to help w/ compatibility Notice that most PDS projections are
2235 : // spherical based on the fact that ISIS/PICS are spherical Set the body
2236 : // size but take into consideration which proj is being used to help w/
2237 : // proj4 compatibility The use of a Sphere, polar radius or ellipse here
2238 : // is based on how ISIS does it internally
2239 27 : if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
2240 54 : (fabs(center_lat) == 90))) ||
2241 27 : (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
2242 : {
2243 0 : if (bIsGeographic)
2244 : {
2245 : // Geograpraphic, so set an ellipse
2246 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
2247 : iflattening, "Reference_Meridian", 0.0);
2248 : }
2249 : else
2250 : {
2251 : // Geocentric, so force a sphere using the semi-minor axis. I
2252 : // hope...
2253 0 : sphere_name += "_polarRadius";
2254 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
2255 : 0.0, "Reference_Meridian", 0.0);
2256 : }
2257 : }
2258 27 : else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
2259 12 : (EQUAL(map_proj_name, "EQUIDISTANT")) ||
2260 12 : (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
2261 51 : (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
2262 12 : (EQUAL(map_proj_name, "SINUSOIDAL")))
2263 : {
2264 : // isis uses the spherical equation for these projections so force a
2265 : // sphere
2266 27 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
2267 : "Reference_Meridian", 0.0);
2268 : }
2269 0 : else if (EQUAL(map_proj_name, "EQUIRECTANGULAR"))
2270 : {
2271 : // isis uses local radius as a sphere, which is pre-calculated in
2272 : // the PDS label as the semi-major
2273 0 : sphere_name += "_localRadius";
2274 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
2275 : "Reference_Meridian", 0.0);
2276 : }
2277 : else
2278 : {
2279 : // All other projections: Mercator, Transverse Mercator, Lambert
2280 : // Conformal, etc. Geographic, so set an ellipse
2281 0 : if (bIsGeographic)
2282 : {
2283 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
2284 : iflattening, "Reference_Meridian", 0.0);
2285 : }
2286 : else
2287 : {
2288 : // Geocentric, so force a sphere. I hope...
2289 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
2290 : 0.0, "Reference_Meridian", 0.0);
2291 : }
2292 : }
2293 :
2294 27 : m_oSRS = std::move(oSRS);
2295 27 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2296 : }
2297 27 : if (bProjectionSet)
2298 : {
2299 27 : m_bGotTransform = true;
2300 27 : m_adfGeoTransform[0] = dfULXMap;
2301 27 : m_adfGeoTransform[1] = dfXDim;
2302 27 : m_adfGeoTransform[2] = 0.0;
2303 27 : m_adfGeoTransform[3] = dfULYMap;
2304 27 : m_adfGeoTransform[4] = 0.0;
2305 27 : m_adfGeoTransform[5] = dfYDim;
2306 : }
2307 27 : }
2308 :
2309 : /************************************************************************/
2310 : /* ReadProjectionFromGeoTIFFGroup() */
2311 : /************************************************************************/
2312 :
2313 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
2314 12 : void VICARDataset::ReadProjectionFromGeoTIFFGroup()
2315 : {
2316 12 : m_bGeoRefFormatIsMIPL = true;
2317 :
2318 : // We will build a in-memory temporary GeoTIFF file from the VICAR GEOTIFF
2319 : // metadata items.
2320 :
2321 : const std::string osTmpFilename(
2322 12 : VSIMemGenerateHiddenFilename("vicar_tmp.tif"));
2323 :
2324 : /* -------------------------------------------------------------------- */
2325 : /* Initialization of libtiff and libgeotiff. */
2326 : /* -------------------------------------------------------------------- */
2327 12 : GTiffOneTimeInit();
2328 12 : LibgeotiffOneTimeInit();
2329 :
2330 : /* -------------------------------------------------------------------- */
2331 : /* Initialize access to the memory geotiff structure. */
2332 : /* -------------------------------------------------------------------- */
2333 12 : VSILFILE *fpL = VSIFOpenL(osTmpFilename.c_str(), "w");
2334 12 : if (fpL == nullptr)
2335 0 : return;
2336 :
2337 12 : TIFF *hTIFF = VSI_TIFFOpen(osTmpFilename.c_str(), "w", fpL);
2338 :
2339 12 : if (hTIFF == nullptr)
2340 : {
2341 0 : CPLError(CE_Failure, CPLE_AppDefined,
2342 : "TIFF/GeoTIFF structure is corrupt.");
2343 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
2344 0 : return;
2345 : }
2346 :
2347 : /* -------------------------------------------------------------------- */
2348 : /* Write some minimal set of image parameters. */
2349 : /* -------------------------------------------------------------------- */
2350 12 : TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, 1);
2351 12 : TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, 1);
2352 12 : TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, 8);
2353 12 : TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
2354 12 : TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, 1);
2355 12 : TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
2356 12 : TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
2357 :
2358 : /* -------------------------------------------------------------------- */
2359 : /* Write geotiff keys from VICAR metadata */
2360 : /* -------------------------------------------------------------------- */
2361 12 : GTIF *hGTIF = GTIFNew(hTIFF);
2362 12 : CPLAssert(hGTIF);
2363 :
2364 60 : for (const auto &gkey : GTiffAsciiKeys)
2365 : {
2366 48 : const char *pszValue = GetKeyword(
2367 96 : ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
2368 : nullptr);
2369 48 : if (pszValue)
2370 : {
2371 8 : GTIFKeySet(hGTIF, gkey, TYPE_ASCII,
2372 8 : static_cast<int>(strlen(pszValue)), pszValue);
2373 : }
2374 : }
2375 :
2376 372 : for (const auto &gkey : GTiffDoubleKeys)
2377 : {
2378 360 : const char *pszValue = GetKeyword(
2379 720 : ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
2380 : nullptr);
2381 360 : if (pszValue)
2382 : {
2383 24 : GTIFKeySet(hGTIF, gkey, TYPE_DOUBLE, 1, CPLAtof(pszValue));
2384 : }
2385 : }
2386 :
2387 204 : for (const auto &gkey : GTiffShortKeys)
2388 : {
2389 192 : const char *pszValue = GetKeyword(
2390 384 : ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
2391 : nullptr);
2392 192 : if (pszValue)
2393 : {
2394 40 : GTIFKeySet(hGTIF, gkey, TYPE_SHORT, 1, atoi(pszValue));
2395 : }
2396 : }
2397 :
2398 12 : GTIFWriteKeys(hGTIF);
2399 12 : GTIFFree(hGTIF);
2400 :
2401 : /* -------------------------------------------------------------------- */
2402 : /* Write geotiff tags from VICAR metadata */
2403 : /* -------------------------------------------------------------------- */
2404 :
2405 : const std::map<const char *, int> oMapTagNameToCode = {
2406 : {"MODELPIXELSCALETAG", TIFFTAG_GEOPIXELSCALE},
2407 : {"MODELTIEPOINTTAG", TIFFTAG_GEOTIEPOINTS},
2408 : {"MODELTRANSFORMATIONTAG", TIFFTAG_GEOTRANSMATRIX},
2409 24 : };
2410 :
2411 48 : for (const auto &kv : oMapTagNameToCode)
2412 : {
2413 : const char *pszValue =
2414 36 : GetKeyword((std::string("GEOTIFF.") + kv.first).c_str(), nullptr);
2415 36 : if (pszValue)
2416 : {
2417 : // Remove leading ( and trailing ), and replace comma by space
2418 : // to separate on it.
2419 : const CPLStringList aosTokens(
2420 24 : CSLTokenizeString2(CPLString(pszValue)
2421 48 : .replaceAll('(', "")
2422 48 : .replaceAll(')', "")
2423 24 : .replaceAll(',', ' ')
2424 : .c_str(),
2425 48 : " ", 0));
2426 24 : if (!aosTokens.empty())
2427 : {
2428 48 : std::vector<double> adfValues;
2429 132 : for (int i = 0; i < aosTokens.size(); ++i)
2430 108 : adfValues.push_back(CPLAtof(aosTokens[i]));
2431 24 : TIFFSetField(hTIFF, kv.second, aosTokens.size(), &adfValues[0]);
2432 : }
2433 : }
2434 : }
2435 :
2436 : /* -------------------------------------------------------------------- */
2437 : /* Finalize the geotiff file. */
2438 : /* -------------------------------------------------------------------- */
2439 :
2440 12 : char bySmallImage = 0;
2441 :
2442 12 : TIFFWriteEncodedStrip(hTIFF, 0, &bySmallImage, 1);
2443 12 : TIFFWriteDirectory(hTIFF);
2444 :
2445 12 : XTIFFClose(hTIFF);
2446 12 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
2447 :
2448 : /* -------------------------------------------------------------------- */
2449 : /* Get georeferencing from file. */
2450 : /* -------------------------------------------------------------------- */
2451 : auto poGTiffDS =
2452 24 : std::unique_ptr<GDALDataset>(GDALDataset::Open(osTmpFilename.c_str()));
2453 12 : if (poGTiffDS)
2454 : {
2455 12 : auto poSRS = poGTiffDS->GetSpatialRef();
2456 12 : if (poSRS)
2457 4 : m_oSRS = *poSRS;
2458 :
2459 12 : if (poGTiffDS->GetGeoTransform(&m_adfGeoTransform[0]) == CE_None)
2460 : {
2461 12 : m_bGotTransform = true;
2462 : }
2463 :
2464 : const char *pszAreaOrPoint =
2465 12 : poGTiffDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
2466 12 : if (pszAreaOrPoint)
2467 4 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, pszAreaOrPoint);
2468 : }
2469 :
2470 12 : VSIUnlink(osTmpFilename.c_str());
2471 : }
2472 : #endif
2473 :
2474 : /************************************************************************/
2475 : /* Open() */
2476 : /************************************************************************/
2477 :
2478 115 : GDALDataset *VICARDataset::Open(GDALOpenInfo *poOpenInfo)
2479 : {
2480 : /* -------------------------------------------------------------------- */
2481 : /* Does this look like a VICAR dataset? */
2482 : /* -------------------------------------------------------------------- */
2483 115 : const vsi_l_offset nLabelOffset = VICARGetLabelOffset(poOpenInfo);
2484 115 : if (nLabelOffset == static_cast<vsi_l_offset>(-1))
2485 0 : return nullptr;
2486 115 : if (nLabelOffset > 0)
2487 : {
2488 4 : CPLString osSubFilename;
2489 : osSubFilename.Printf("/vsisubfile/" CPL_FRMT_GUIB ",%s",
2490 : static_cast<GUIntBig>(nLabelOffset),
2491 2 : poOpenInfo->pszFilename);
2492 4 : GDALOpenInfo oOpenInfo(osSubFilename.c_str(), poOpenInfo->eAccess);
2493 2 : return Open(&oOpenInfo);
2494 : }
2495 :
2496 226 : auto poDS = std::make_unique<VICARDataset>();
2497 113 : poDS->fpImage = poOpenInfo->fpL;
2498 113 : poOpenInfo->fpL = nullptr;
2499 113 : if (!poDS->oKeywords.Ingest(poDS->fpImage, poOpenInfo->pabyHeader))
2500 : {
2501 0 : return nullptr;
2502 : }
2503 :
2504 : /************ CHECK INSTRUMENT/DATA *****************/
2505 :
2506 113 : bool bIsDTM = false;
2507 113 : const char *value = poDS->GetKeyword("DTM.DTM_OFFSET");
2508 113 : if (!EQUAL(value, ""))
2509 : {
2510 0 : bIsDTM = true;
2511 : }
2512 :
2513 113 : bool bInstKnown = false;
2514 : // Check for HRSC
2515 113 : if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
2516 8 : bInstKnown = true;
2517 : // Check for Framing Camera on Dawn
2518 105 : else if (EQUAL(poDS->GetKeyword("INSTRUMENT_ID"), "FC2"))
2519 0 : bInstKnown = true;
2520 :
2521 : /************ Grab dimensions *****************/
2522 :
2523 113 : const int nCols = atoi(poDS->GetKeyword("NS"));
2524 113 : const int nRows = atoi(poDS->GetKeyword("NL"));
2525 113 : const int nBands = atoi(poDS->GetKeyword("NB"));
2526 :
2527 226 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
2528 113 : !GDALCheckBandCount(nBands, false))
2529 : {
2530 0 : CPLError(CE_Failure, CPLE_AppDefined,
2531 : "File %s appears to be a VICAR file, but failed to find some "
2532 : "required keywords.",
2533 : poOpenInfo->pszFilename);
2534 0 : return nullptr;
2535 : }
2536 :
2537 : const GDALDataType eDataType =
2538 113 : GetDataTypeFromFormat(poDS->GetKeyword("FORMAT"));
2539 113 : if (eDataType == GDT_Unknown)
2540 : {
2541 0 : CPLError(CE_Failure, CPLE_AppDefined,
2542 : "Could not find known VICAR label entries!\n");
2543 0 : return nullptr;
2544 : }
2545 113 : double dfNoData = 0.0;
2546 113 : if (eDataType == GDT_Byte)
2547 : {
2548 51 : dfNoData = VICAR_NULL1;
2549 : }
2550 62 : else if (eDataType == GDT_Int16)
2551 : {
2552 15 : dfNoData = VICAR_NULL2;
2553 : }
2554 47 : else if (eDataType == GDT_Float32)
2555 : {
2556 20 : dfNoData = VICAR_NULL3;
2557 : }
2558 :
2559 : /***** CHECK ENDIANNESS **************/
2560 :
2561 113 : RawRasterBand::ByteOrder eByteOrder =
2562 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2563 113 : if (GDALDataTypeIsInteger(eDataType))
2564 : {
2565 71 : value = poDS->GetKeyword("INTFMT", "LOW");
2566 71 : if (EQUAL(value, "LOW"))
2567 : {
2568 68 : eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2569 : }
2570 3 : else if (EQUAL(value, "HIGH"))
2571 : {
2572 3 : eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2573 : }
2574 : else
2575 : {
2576 0 : CPLError(CE_Failure, CPLE_NotSupported,
2577 : "INTFMT=%s layout not supported.", value);
2578 0 : return nullptr;
2579 : }
2580 : }
2581 : else
2582 : {
2583 42 : value = poDS->GetKeyword("REALFMT", "VAX");
2584 42 : if (EQUAL(value, "RIEEE"))
2585 : {
2586 29 : eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2587 : }
2588 13 : else if (EQUAL(value, "IEEE"))
2589 : {
2590 4 : eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2591 : }
2592 9 : else if (EQUAL(value, "VAX"))
2593 : {
2594 9 : eByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
2595 : }
2596 : else
2597 : {
2598 0 : CPLError(CE_Failure, CPLE_NotSupported,
2599 : "REALFMT=%s layout not supported.", value);
2600 0 : return nullptr;
2601 : }
2602 : }
2603 :
2604 : /* -------------------------------------------------------------------- */
2605 : /* Capture some information from the file that is of interest. */
2606 : /* -------------------------------------------------------------------- */
2607 113 : poDS->nRasterXSize = nCols;
2608 113 : poDS->nRasterYSize = nRows;
2609 :
2610 113 : if (poDS->GetKeyword("MAP.MAP_PROJECTION_TYPE")[0] != '\0')
2611 : {
2612 27 : poDS->ReadProjectionFromMapGroup();
2613 : }
2614 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
2615 168 : else if (poDS->GetKeyword("GEOTIFF.GTMODELTYPEGEOKEY")[0] != '\0' ||
2616 82 : poDS->GetKeyword("GEOTIFF.MODELTIEPOINTTAG")[0] != '\0')
2617 : {
2618 12 : poDS->ReadProjectionFromGeoTIFFGroup();
2619 : }
2620 : #endif
2621 :
2622 113 : if (!poDS->m_bGotTransform)
2623 74 : poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
2624 74 : poOpenInfo->pszFilename, "wld", &poDS->m_adfGeoTransform[0]));
2625 :
2626 113 : poDS->eAccess = poOpenInfo->eAccess;
2627 113 : poDS->m_oJSonLabel = poDS->oKeywords.GetJsonObject();
2628 :
2629 : /* -------------------------------------------------------------------- */
2630 : /* Compute the line offsets. */
2631 : /* -------------------------------------------------------------------- */
2632 :
2633 : GUInt64 nPixelOffset;
2634 : GUInt64 nLineOffset;
2635 : GUInt64 nBandOffset;
2636 : GUInt64 nImageOffsetWithoutNBB;
2637 : GUInt64 nNBB;
2638 : GUInt64 nImageSize;
2639 113 : if (!GetSpacings(poDS->oKeywords, nPixelOffset, nLineOffset, nBandOffset,
2640 226 : nImageOffsetWithoutNBB, nNBB, nImageSize) ||
2641 113 : nImageOffsetWithoutNBB > std::numeric_limits<GUInt64>::max() -
2642 113 : (nNBB + nBandOffset * (nBands - 1)))
2643 : {
2644 0 : CPLDebug("VICAR", "Invalid spacings found");
2645 0 : return nullptr;
2646 : }
2647 :
2648 113 : poDS->m_nRecordSize = atoi(poDS->GetKeyword("RECSIZE", ""));
2649 :
2650 113 : if (nNBB != 0)
2651 : {
2652 2 : const char *pszBLType = poDS->GetKeyword("BLTYPE", nullptr);
2653 : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2654 : const char *pszVicarConf = nullptr;
2655 : #else
2656 2 : const char *pszVicarConf = CPLFindFile("gdal", "vicar.json");
2657 : #endif
2658 4 : CPLJSONDocument oDoc;
2659 2 : if (!pszVicarConf || EQUAL(pszVicarConf, "vicar.json"))
2660 : {
2661 : #ifdef EMBED_RESOURCE_FILES
2662 : oDoc.LoadMemory(VICARGetEmbeddedConf());
2663 : pszVicarConf = "__embedded__";
2664 : #endif
2665 : }
2666 :
2667 2 : if (pszBLType && pszVicarConf && poDS->m_nRecordSize > 0)
2668 : {
2669 :
2670 2 : RawRasterBand::ByteOrder eBINTByteOrder =
2671 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2672 2 : value = poDS->GetKeyword("BINTFMT", "LOW");
2673 2 : if (EQUAL(value, "LOW"))
2674 : {
2675 2 : eBINTByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2676 : }
2677 0 : else if (EQUAL(value, "HIGH"))
2678 : {
2679 0 : eBINTByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2680 : }
2681 : else
2682 : {
2683 0 : CPLError(CE_Failure, CPLE_NotSupported,
2684 : "BINTFMT=%s layout not supported.", value);
2685 : }
2686 :
2687 2 : RawRasterBand::ByteOrder eBREALByteOrder =
2688 : RawRasterBand::ByteOrder::ORDER_VAX;
2689 2 : value = poDS->GetKeyword("BREALFMT", "VAX");
2690 2 : if (EQUAL(value, "RIEEE"))
2691 : {
2692 2 : eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2693 : }
2694 0 : else if (EQUAL(value, "IEEE"))
2695 : {
2696 0 : eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2697 : }
2698 0 : else if (EQUAL(value, "VAX"))
2699 : {
2700 0 : eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
2701 : }
2702 : else
2703 : {
2704 0 : CPLError(CE_Failure, CPLE_NotSupported,
2705 : "BREALFMT=%s layout not supported.", value);
2706 : }
2707 :
2708 2 : if (EQUAL(pszVicarConf, "__embedded__") || oDoc.Load(pszVicarConf))
2709 : {
2710 4 : const auto oRoot = oDoc.GetRoot();
2711 2 : if (oRoot.GetType() == CPLJSONObject::Type::Object)
2712 : {
2713 6 : auto oDef = oRoot.GetObj(pszBLType);
2714 8 : if (oDef.IsValid() &&
2715 4 : oDef.GetType() == CPLJSONObject::Type::Object &&
2716 4 : static_cast<GUInt64>(oDef.GetInteger("size")) == nNBB)
2717 : {
2718 : auto poLayer =
2719 : std::unique_ptr<OGRVICARBinaryPrefixesLayer>(
2720 : new OGRVICARBinaryPrefixesLayer(
2721 2 : poDS->fpImage,
2722 4 : static_cast<int>(nImageSize /
2723 2 : poDS->m_nRecordSize),
2724 : oDef, nImageOffsetWithoutNBB,
2725 2 : poDS->m_nRecordSize, eBINTByteOrder,
2726 4 : eBREALByteOrder));
2727 2 : if (!poLayer->HasError())
2728 : {
2729 2 : poDS->m_poLayer = std::move(poLayer);
2730 : }
2731 : }
2732 : }
2733 : }
2734 : }
2735 : }
2736 :
2737 113 : poDS->m_nImageOffsetWithoutNBB =
2738 : static_cast<vsi_l_offset>(nImageOffsetWithoutNBB);
2739 :
2740 226 : CPLString osCompress = poDS->GetKeyword("COMPRESS", "NONE");
2741 113 : if (EQUAL(osCompress, "BASIC") || EQUAL(osCompress, "BASIC2"))
2742 : {
2743 16 : if (poOpenInfo->eAccess == GA_Update)
2744 : {
2745 0 : CPLError(CE_Failure, CPLE_NotSupported,
2746 : "Update of compressed VICAR file not supported");
2747 0 : return nullptr;
2748 : }
2749 16 : poDS->SetMetadataItem("COMPRESS", osCompress, "IMAGE_STRUCTURE");
2750 16 : poDS->m_eCompress =
2751 16 : EQUAL(osCompress, "BASIC") ? COMPRESS_BASIC : COMPRESS_BASIC2;
2752 16 : if (poDS->nRasterYSize > 100 * 1000 * 1000 / nBands)
2753 : {
2754 0 : CPLError(CE_Failure, CPLE_NotSupported,
2755 : "Too many records for compressed dataset");
2756 0 : return nullptr;
2757 : }
2758 16 : if (!GDALDataTypeIsInteger(eDataType))
2759 : {
2760 0 : CPLError(CE_Failure, CPLE_NotSupported,
2761 : "Data type incompatible of compression");
2762 0 : return nullptr;
2763 : }
2764 : // To avoid potential issues in basic_decode()
2765 16 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
2766 16 : if (nDTSize == 0 || poDS->nRasterXSize > INT_MAX / nDTSize)
2767 : {
2768 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large scanline");
2769 0 : return nullptr;
2770 : }
2771 16 : const int nRecords = poDS->nRasterYSize * nBands;
2772 : try
2773 : {
2774 : // + 1 to store implicitly the size of the last record
2775 16 : poDS->m_anRecordOffsets.resize(nRecords + 1);
2776 : }
2777 0 : catch (const std::exception &e)
2778 : {
2779 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2780 0 : return nullptr;
2781 : }
2782 16 : if (poDS->m_eCompress == COMPRESS_BASIC)
2783 : {
2784 5 : poDS->m_anRecordOffsets[0] =
2785 5 : poDS->m_nImageOffsetWithoutNBB + sizeof(GUInt32);
2786 : }
2787 : else
2788 : {
2789 11 : poDS->m_anRecordOffsets[0] =
2790 11 : poDS->m_nImageOffsetWithoutNBB + sizeof(GUInt32) * nRecords;
2791 : }
2792 : }
2793 97 : else if (!EQUAL(osCompress, "NONE"))
2794 : {
2795 0 : CPLError(CE_Failure, CPLE_NotSupported, "COMPRESS=%s not supported",
2796 : osCompress.c_str());
2797 0 : return nullptr;
2798 : }
2799 :
2800 : /* -------------------------------------------------------------------- */
2801 : /* Create band information objects. */
2802 : /* -------------------------------------------------------------------- */
2803 263 : for (int i = 0; i < nBands; i++)
2804 : {
2805 0 : std::unique_ptr<GDALRasterBand> poBand;
2806 :
2807 295 : if (poDS->m_eCompress == COMPRESS_BASIC ||
2808 145 : poDS->m_eCompress == COMPRESS_BASIC2)
2809 : {
2810 32 : poBand = std::make_unique<VICARBASICRasterBand>(poDS.get(), i + 1,
2811 16 : eDataType);
2812 : }
2813 : else
2814 : {
2815 : auto poRawBand = std::make_unique<VICARRawRasterBand>(
2816 134 : poDS.get(), i + 1, poDS->fpImage,
2817 0 : static_cast<vsi_l_offset>(nImageOffsetWithoutNBB + nNBB +
2818 134 : nBandOffset * i),
2819 134 : static_cast<int>(nPixelOffset), static_cast<int>(nLineOffset),
2820 134 : eDataType, eByteOrder);
2821 134 : if (!poRawBand->IsValid())
2822 : {
2823 0 : return nullptr;
2824 : }
2825 134 : poBand = std::move(poRawBand);
2826 : }
2827 :
2828 : // only set NoData if instrument is supported
2829 150 : if (bInstKnown)
2830 8 : poBand->SetNoDataValue(dfNoData);
2831 150 : if (bIsDTM)
2832 : {
2833 0 : poBand->SetScale(static_cast<double>(
2834 0 : CPLAtof(poDS->GetKeyword("DTM.DTM_SCALING_FACTOR"))));
2835 0 : poBand->SetOffset(static_cast<double>(
2836 0 : CPLAtof(poDS->GetKeyword("DTM.DTM_OFFSET"))));
2837 : const char *pszMin =
2838 0 : poDS->GetKeyword("DTM.DTM_MINIMUM_DN", nullptr);
2839 : const char *pszMax =
2840 0 : poDS->GetKeyword("DTM.DTM_MAXIMUM_DN", nullptr);
2841 0 : if (pszMin != nullptr && pszMax != nullptr)
2842 0 : poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax), 0, 0);
2843 : const char *pszNoData =
2844 0 : poDS->GetKeyword("DTM.DTM_MISSING_DN", nullptr);
2845 0 : if (pszNoData != nullptr)
2846 0 : poBand->SetNoDataValue(CPLAtofM(pszNoData));
2847 : }
2848 150 : else if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
2849 : {
2850 8 : double scale = CPLAtof(
2851 : poDS->GetKeyword("DLRTO8.REFLECTANCE_SCALING_FACTOR", "-1."));
2852 8 : if (scale < 0.)
2853 : {
2854 0 : scale = CPLAtof(
2855 : poDS->GetKeyword("HRCAL.REFLECTANCE_SCALING_FACTOR", "1."));
2856 : }
2857 8 : poBand->SetScale(scale);
2858 : double offset =
2859 8 : CPLAtof(poDS->GetKeyword("DLRTO8.REFLECTANCE_OFFSET", "-1."));
2860 8 : if (offset < 0.)
2861 : {
2862 : offset =
2863 0 : CPLAtof(poDS->GetKeyword("HRCAL.REFLECTANCE_OFFSET", "0."));
2864 : }
2865 8 : poBand->SetOffset(offset);
2866 : }
2867 150 : const char *pszMin = poDS->GetKeyword("STATISTICS.MINIMUM", nullptr);
2868 150 : const char *pszMax = poDS->GetKeyword("STATISTICS.MAXIMUM", nullptr);
2869 150 : const char *pszMean = poDS->GetKeyword("STATISTICS.MEAN", nullptr);
2870 : const char *pszStdDev =
2871 150 : poDS->GetKeyword("STATISTICS.STANDARD_DEVIATION", nullptr);
2872 150 : if (pszMin != nullptr && pszMax != nullptr && pszMean != nullptr &&
2873 : pszStdDev != nullptr)
2874 0 : poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax),
2875 0 : CPLAtofM(pszMean), CPLAtofM(pszStdDev));
2876 :
2877 150 : poDS->SetBand(i + 1, std::move(poBand));
2878 : }
2879 :
2880 : /* -------------------------------------------------------------------- */
2881 : /* Instrument-specific keywords as metadata. */
2882 : /* -------------------------------------------------------------------- */
2883 :
2884 : /****************** HRSC ******************************/
2885 :
2886 113 : if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
2887 : {
2888 16 : poDS->SetMetadataItem(
2889 : "SPACECRAFT_NAME",
2890 8 : poDS->GetKeyword("M94_INSTRUMENT.INSTRUMENT_HOST_NAME"));
2891 8 : poDS->SetMetadataItem("PRODUCT_TYPE", poDS->GetKeyword("TYPE"));
2892 :
2893 8 : if (EQUAL(poDS->GetKeyword("M94_INSTRUMENT.DETECTOR_ID"),
2894 : "MEX_HRSC_SRC"))
2895 : {
2896 : static const char *const apszKeywords[] = {
2897 : "M94_ORBIT.IMAGE_TIME",
2898 : "FILE.EVENT_TYPE",
2899 : "FILE.PROCESSING_LEVEL_ID",
2900 : "M94_INSTRUMENT.DETECTOR_ID",
2901 : "M94_CAMERAS.EXPOSURE_DURATION",
2902 : "HRCONVER.INSTRUMENT_TEMPERATURE",
2903 : nullptr};
2904 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2905 : {
2906 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
2907 0 : if (pszKeywordValue != nullptr)
2908 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2909 : }
2910 : }
2911 : else
2912 : {
2913 : static const char *const apszKeywords[] = {
2914 : "M94_ORBIT.START_TIME",
2915 : "M94_ORBIT.STOP_TIME",
2916 : "M94_INSTRUMENT.DETECTOR_ID",
2917 : "M94_CAMERAS.MACROPIXEL_SIZE",
2918 : "FILE.EVENT_TYPE",
2919 : "M94_INSTRUMENT.MISSION_PHASE_NAME",
2920 : "HRORTHO.SPICE_FILE_NAME",
2921 : "HRCONVER.MISSING_FRAMES",
2922 : "HRCONVER.OVERFLOW_FRAMES",
2923 : "HRCONVER.ERROR_FRAMES",
2924 : "HRFOOT.BEST_GROUND_SAMPLING_DISTANCE",
2925 : "DLRTO8.RADIANCE_SCALING_FACTOR",
2926 : "DLRTO8.RADIANCE_OFFSET",
2927 : "DLRTO8.REFLECTANCE_SCALING_FACTOR",
2928 : "DLRTO8.REFLECTANCE_OFFSET",
2929 : "HRCAL.RADIANCE_SCALING_FACTOR",
2930 : "HRCAL.RADIANCE_OFFSET",
2931 : "HRCAL.REFLECTANCE_SCALING_FACTOR",
2932 : "HRCAL.REFLECTANCE_OFFSET",
2933 : "HRORTHO.DTM_NAME",
2934 : "HRORTHO.EXTORI_FILE_NAME",
2935 : "HRORTHO.GEOMETRIC_CALIB_FILE_NAME",
2936 : nullptr};
2937 184 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2938 : {
2939 : const char *pszKeywordValue =
2940 176 : poDS->GetKeyword(apszKeywords[i], nullptr);
2941 176 : if (pszKeywordValue != nullptr)
2942 144 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2943 : }
2944 : }
2945 : }
2946 113 : if (bIsDTM && EQUAL(poDS->GetKeyword("MAP.TARGET_NAME"), "MARS"))
2947 : {
2948 0 : poDS->SetMetadataItem("SPACECRAFT_NAME", "MARS_EXPRESS");
2949 0 : poDS->SetMetadataItem("PRODUCT_TYPE", "DTM");
2950 : static const char *const apszKeywords[] = {
2951 : "DTM.DTM_MISSING_DN", "DTM.DTM_OFFSET",
2952 : "DTM.DTM_SCALING_FACTOR", "DTM.DTM_A_AXIS_RADIUS",
2953 : "DTM.DTM_B_AXIS_RADIUS", "DTM.DTM_C_AXIS_RADIUS",
2954 : "DTM.DTM_DESC", "DTM.DTM_MINIMUM_DN",
2955 : "DTM.DTM_MAXIMUM_DN", nullptr};
2956 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2957 : {
2958 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
2959 0 : if (pszKeywordValue != nullptr)
2960 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2961 : }
2962 : }
2963 :
2964 : /****************** DAWN ******************************/
2965 113 : else if (EQUAL(poDS->GetKeyword("INSTRUMENT_ID"), "FC2"))
2966 : {
2967 0 : poDS->SetMetadataItem("SPACECRAFT_NAME", "DAWN");
2968 : static const char *const apszKeywords[] = {
2969 : "ORBIT_NUMBER",
2970 : "FILTER_NUMBER",
2971 : "FRONT_DOOR_STATUS",
2972 : "FIRST_LINE",
2973 : "FIRST_LINE_SAMPLE",
2974 : "PRODUCER_INSTITUTION_NAME",
2975 : "SOURCE_FILE_NAME",
2976 : "PROCESSING_LEVEL_ID",
2977 : "TARGET_NAME",
2978 : "LIMB_IN_IMAGE",
2979 : "POLE_IN_IMAGE",
2980 : "REFLECTANCE_SCALING_FACTOR",
2981 : "SPICE_FILE_NAME",
2982 : "SPACECRAFT_CENTRIC_LATITUDE",
2983 : "SPACECRAFT_EASTERN_LONGITUDE",
2984 : "FOOTPRINT_POSITIVE_LONGITUDE",
2985 : nullptr};
2986 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2987 : {
2988 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
2989 0 : if (pszKeywordValue != nullptr)
2990 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2991 : }
2992 : }
2993 113 : else if (bIsDTM && (EQUAL(poDS->GetKeyword("TARGET_NAME"), "VESTA") ||
2994 0 : EQUAL(poDS->GetKeyword("TARGET_NAME"), "CERES")))
2995 : {
2996 0 : poDS->SetMetadataItem("SPACECRAFT_NAME", "DAWN");
2997 0 : poDS->SetMetadataItem("PRODUCT_TYPE", "DTM");
2998 : static const char *const apszKeywords[] = {
2999 : "DTM_MISSING_DN",
3000 : "DTM_OFFSET",
3001 : "DTM_SCALING_FACTOR",
3002 : "DTM_A_AXIS_RADIUS",
3003 : "DTM_B_AXIS_RADIUS",
3004 : "DTM_C_AXIS_RADIUS",
3005 : "DTM_MINIMUM_DN",
3006 : "DTM_MAXIMUM_DN",
3007 : "MAP_PROJECTION_TYPE",
3008 : "COORDINATE_SYSTEM_NAME",
3009 : "POSITIVE_LONGITUDE_DIRECTION",
3010 : "MAP_SCALE",
3011 : "CENTER_LONGITUDE",
3012 : "LINE_PROJECTION_OFFSET",
3013 : "SAMPLE_PROJECTION_OFFSET",
3014 : nullptr};
3015 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
3016 : {
3017 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
3018 0 : if (pszKeywordValue != nullptr)
3019 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
3020 : }
3021 : }
3022 :
3023 : /* -------------------------------------------------------------------- */
3024 : /* Initialize any PAM information. */
3025 : /* -------------------------------------------------------------------- */
3026 113 : poDS->TryLoadXML();
3027 :
3028 : /* -------------------------------------------------------------------- */
3029 : /* Check for overviews. */
3030 : /* -------------------------------------------------------------------- */
3031 113 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
3032 :
3033 113 : return poDS.release();
3034 : }
3035 :
3036 : /************************************************************************/
3037 : /* GetKeyword() */
3038 : /************************************************************************/
3039 :
3040 3561 : const char *VICARDataset::GetKeyword(const char *pszPath,
3041 : const char *pszDefault)
3042 :
3043 : {
3044 3561 : return oKeywords.GetKeyword(pszPath, pszDefault);
3045 : }
3046 :
3047 : /************************************************************************/
3048 : /* GetDataTypeFromFormat() */
3049 : /************************************************************************/
3050 :
3051 276 : GDALDataType VICARDataset::GetDataTypeFromFormat(const char *pszFormat)
3052 : {
3053 276 : if (EQUAL(pszFormat, "BYTE"))
3054 110 : return GDT_Byte;
3055 :
3056 166 : if (EQUAL(pszFormat, "HALF") || EQUAL(pszFormat, "WORD"))
3057 39 : return GDT_Int16;
3058 :
3059 127 : if (EQUAL(pszFormat, "FULL") || EQUAL(pszFormat, "LONG"))
3060 13 : return GDT_Int32;
3061 :
3062 114 : if (EQUAL(pszFormat, "REAL"))
3063 56 : return GDT_Float32;
3064 :
3065 58 : if (EQUAL(pszFormat, "DOUB"))
3066 29 : return GDT_Float64;
3067 :
3068 29 : if (EQUAL(pszFormat, "COMP") || EQUAL(pszFormat, "COMPLEX"))
3069 29 : return GDT_CFloat32;
3070 :
3071 0 : return GDT_Unknown;
3072 : }
3073 :
3074 : /************************************************************************/
3075 : /* GetSpacings() */
3076 : /************************************************************************/
3077 :
3078 163 : bool VICARDataset::GetSpacings(const VICARKeywordHandler &keywords,
3079 : GUInt64 &nPixelOffset, GUInt64 &nLineOffset,
3080 : GUInt64 &nBandOffset,
3081 : GUInt64 &nImageOffsetWithoutNBB, GUInt64 &nNBB,
3082 : GUInt64 &nImageSize)
3083 : {
3084 : const GDALDataType eDataType =
3085 163 : GetDataTypeFromFormat(keywords.GetKeyword("FORMAT", ""));
3086 163 : if (eDataType == GDT_Unknown)
3087 0 : return false;
3088 163 : const GUInt64 nItemSize = GDALGetDataTypeSizeBytes(eDataType);
3089 163 : const char *value = keywords.GetKeyword("ORG", "BSQ");
3090 : // number of bytes of binary prefix before each record
3091 163 : nNBB = atoi(keywords.GetKeyword("NBB", ""));
3092 163 : const GUInt64 nCols64 = atoi(keywords.GetKeyword("NS", ""));
3093 163 : const GUInt64 nRows64 = atoi(keywords.GetKeyword("NL", ""));
3094 163 : const GUInt64 nBands64 = atoi(keywords.GetKeyword("NB", ""));
3095 : try
3096 : {
3097 163 : if (EQUAL(value, "BIP"))
3098 : {
3099 6 : nPixelOffset = (CPLSM(nItemSize) * CPLSM(nBands64)).v();
3100 6 : nBandOffset = nItemSize;
3101 6 : nLineOffset =
3102 6 : (CPLSM(nNBB) + CPLSM(nPixelOffset) * CPLSM(nCols64)).v();
3103 6 : nImageSize = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
3104 : }
3105 157 : else if (EQUAL(value, "BIL"))
3106 : {
3107 6 : nPixelOffset = nItemSize;
3108 6 : nBandOffset = (CPLSM(nItemSize) * CPLSM(nCols64)).v();
3109 6 : nLineOffset =
3110 6 : (CPLSM(nNBB) + CPLSM(nBandOffset) * CPLSM(nBands64)).v();
3111 6 : nImageSize = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
3112 : }
3113 151 : else if (EQUAL(value, "BSQ"))
3114 : {
3115 151 : nPixelOffset = nItemSize;
3116 151 : nLineOffset =
3117 151 : (CPLSM(nNBB) + CPLSM(nPixelOffset) * CPLSM(nCols64)).v();
3118 151 : nBandOffset = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
3119 151 : nImageSize = (CPLSM(nBandOffset) * CPLSM(nBands64)).v();
3120 : }
3121 : else
3122 : {
3123 0 : CPLError(CE_Failure, CPLE_NotSupported,
3124 : "ORG=%s layout not supported.", value);
3125 0 : return false;
3126 : }
3127 : }
3128 0 : catch (const CPLSafeIntOverflow &)
3129 : {
3130 0 : return false;
3131 : }
3132 :
3133 163 : const GUInt64 nLabelSize = atoi(keywords.GetKeyword("LBLSIZE", ""));
3134 163 : const GUInt64 nRecordSize = atoi(keywords.GetKeyword("RECSIZE", ""));
3135 163 : const GUInt64 nNLB = atoi(keywords.GetKeyword("NLB", ""));
3136 : try
3137 : {
3138 163 : nImageOffsetWithoutNBB =
3139 163 : (CPLSM(nLabelSize) + CPLSM(nRecordSize) * CPLSM(nNLB) + CPLSM(nNBB))
3140 163 : .v();
3141 163 : nImageOffsetWithoutNBB -= nNBB;
3142 : }
3143 0 : catch (const CPLSafeIntOverflow &)
3144 : {
3145 0 : return false;
3146 : }
3147 163 : return true;
3148 : }
3149 :
3150 : /************************************************************************/
3151 : /* Create() */
3152 : /************************************************************************/
3153 :
3154 62 : GDALDataset *VICARDataset::Create(const char *pszFilename, int nXSize,
3155 : int nYSize, int nBandsIn, GDALDataType eType,
3156 : char **papszOptions)
3157 : {
3158 62 : return CreateInternal(pszFilename, nXSize, nYSize, nBandsIn, eType,
3159 62 : papszOptions);
3160 : }
3161 :
3162 102 : VICARDataset *VICARDataset::CreateInternal(const char *pszFilename, int nXSize,
3163 : int nYSize, int nBandsIn,
3164 : GDALDataType eType,
3165 : char **papszOptions)
3166 : {
3167 102 : if (eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Int32 &&
3168 46 : eType != GDT_Float32 && eType != GDT_Float64 && eType != GDT_CFloat32)
3169 : {
3170 38 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
3171 38 : return nullptr;
3172 : }
3173 :
3174 64 : const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
3175 64 : if (nXSize == 0 || nYSize == 0 || nPixelOffset > INT_MAX / nXSize)
3176 : {
3177 0 : CPLError(CE_Failure, CPLE_NotSupported,
3178 : "Unsupported raster dimensions");
3179 0 : return nullptr;
3180 : }
3181 64 : const int nLineOffset = nXSize * nPixelOffset;
3182 :
3183 64 : if (nBandsIn == 0 || nBandsIn > 32767)
3184 : {
3185 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
3186 1 : return nullptr;
3187 : }
3188 :
3189 : const char *pszCompress =
3190 63 : CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE");
3191 63 : CompressMethod eCompress = COMPRESS_NONE;
3192 63 : if (EQUAL(pszCompress, "NONE"))
3193 : {
3194 53 : eCompress = COMPRESS_NONE;
3195 : }
3196 10 : else if (EQUAL(pszCompress, "BASIC"))
3197 : {
3198 6 : eCompress = COMPRESS_BASIC;
3199 : }
3200 4 : else if (EQUAL(pszCompress, "BASIC2"))
3201 : {
3202 3 : eCompress = COMPRESS_BASIC2;
3203 : }
3204 : else
3205 : {
3206 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported COMPRESS value");
3207 1 : return nullptr;
3208 : }
3209 71 : if (eCompress != COMPRESS_NONE &&
3210 9 : (!GDALDataTypeIsInteger(eType) || nBandsIn != 1))
3211 : {
3212 2 : CPLError(
3213 : CE_Failure, CPLE_NotSupported,
3214 : "BASIC/BASIC2 compression only supports one-band integer datasets");
3215 2 : return nullptr;
3216 : }
3217 :
3218 120 : std::vector<vsi_l_offset> anRecordOffsets;
3219 60 : if (eCompress != COMPRESS_NONE)
3220 : {
3221 7 : const GUInt64 nMaxEncodedSize =
3222 7 : static_cast<GUInt64>(nXSize) * nPixelOffset +
3223 7 : static_cast<GUInt64>(nXSize) * nPixelOffset / 2 + 11;
3224 : // To avoid potential later int overflows
3225 7 : if (nMaxEncodedSize > static_cast<GUInt64>(INT_MAX))
3226 : {
3227 1 : CPLError(CE_Failure, CPLE_NotSupported, "Too large scanline");
3228 1 : return nullptr;
3229 : }
3230 6 : if (nYSize > 100 * 1000 * 1000)
3231 : {
3232 1 : CPLError(CE_Failure, CPLE_NotSupported,
3233 : "Too many records for compressed dataset");
3234 1 : return nullptr;
3235 : }
3236 : try
3237 : {
3238 : // + 1 to store implicitly the size of the last record
3239 5 : anRecordOffsets.resize(nYSize + 1);
3240 : }
3241 0 : catch (const std::exception &e)
3242 : {
3243 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
3244 0 : return nullptr;
3245 : }
3246 : }
3247 :
3248 116 : CPLJSONObject oSrcJSonLabel;
3249 58 : oSrcJSonLabel.Deinit();
3250 :
3251 58 : const char *pszLabel = CSLFetchNameValue(papszOptions, "LABEL");
3252 58 : if (pszLabel)
3253 : {
3254 4 : CPLJSONDocument oJSONDocument;
3255 4 : if (pszLabel[0] == '{')
3256 : {
3257 2 : const GByte *pabyData = reinterpret_cast<const GByte *>(pszLabel);
3258 2 : if (!oJSONDocument.LoadMemory(pabyData))
3259 : {
3260 1 : return nullptr;
3261 : }
3262 : }
3263 : else
3264 : {
3265 2 : if (!oJSONDocument.Load(pszLabel))
3266 : {
3267 1 : return nullptr;
3268 : }
3269 : }
3270 :
3271 2 : oSrcJSonLabel = oJSONDocument.GetRoot();
3272 2 : if (!oSrcJSonLabel.IsValid())
3273 : {
3274 0 : return nullptr;
3275 : }
3276 : }
3277 :
3278 56 : VSILFILE *fp = VSIFOpenExL(pszFilename, "wb+", true);
3279 56 : if (fp == nullptr)
3280 : {
3281 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
3282 : VSIGetLastErrorMsg());
3283 3 : return nullptr;
3284 : }
3285 :
3286 53 : VICARDataset *poDS = new VICARDataset();
3287 53 : poDS->fpImage = fp;
3288 53 : poDS->nRasterXSize = nXSize;
3289 53 : poDS->nRasterYSize = nYSize;
3290 53 : poDS->m_nRecordSize = nLineOffset;
3291 53 : poDS->m_bIsLabelWritten = false;
3292 53 : poDS->m_bGeoRefFormatIsMIPL = EQUAL(
3293 : CSLFetchNameValueDef(papszOptions, "GEOREF_FORMAT", "MIPL"), "MIPL");
3294 53 : poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
3295 53 : poDS->m_bUseSrcMap = CPLFetchBool(papszOptions, "USE_SRC_MAP", false);
3296 : poDS->m_osLatitudeType =
3297 53 : CSLFetchNameValueDef(papszOptions, "COORDINATE_SYSTEM_NAME", "");
3298 : poDS->m_osLongitudeDirection =
3299 53 : CSLFetchNameValueDef(papszOptions, "POSITIVE_LONGITUDE_DIRECTION", "");
3300 : poDS->m_osTargetName =
3301 53 : CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
3302 53 : poDS->m_bInitToNodata = true;
3303 53 : poDS->m_oSrcJSonLabel = std::move(oSrcJSonLabel);
3304 53 : poDS->m_eCompress = eCompress;
3305 53 : poDS->m_anRecordOffsets = std::move(anRecordOffsets);
3306 53 : poDS->eAccess = GA_Update;
3307 :
3308 : /* -------------------------------------------------------------------- */
3309 : /* Create band information objects. */
3310 : /* -------------------------------------------------------------------- */
3311 53 : const vsi_l_offset nBandOffset =
3312 53 : static_cast<vsi_l_offset>(nLineOffset) * nYSize;
3313 140 : for (int i = 0; i < nBandsIn; i++)
3314 : {
3315 : GDALRasterBand *poBand;
3316 87 : if (eCompress != COMPRESS_NONE)
3317 : {
3318 5 : poBand = new VICARBASICRasterBand(poDS, i + 1, eType);
3319 : }
3320 : else
3321 : {
3322 82 : poBand = new VICARRawRasterBand(
3323 : poDS, i + 1, poDS->fpImage,
3324 82 : i * nBandOffset, // will be set later to final value since we
3325 : // need to include the label size
3326 : nPixelOffset, nLineOffset, eType,
3327 82 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN);
3328 : }
3329 87 : poDS->SetBand(i + 1, poBand);
3330 : }
3331 :
3332 53 : return poDS;
3333 : }
3334 :
3335 : /************************************************************************/
3336 : /* CreateCopy() */
3337 : /************************************************************************/
3338 :
3339 41 : GDALDataset *VICARDataset::CreateCopy(const char *pszFilename,
3340 : GDALDataset *poSrcDS, int /*bStrict*/,
3341 : char **papszOptions,
3342 : GDALProgressFunc pfnProgress,
3343 : void *pProgressData)
3344 : {
3345 41 : if (poSrcDS->GetRasterCount() == 0)
3346 : {
3347 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
3348 1 : return nullptr;
3349 : }
3350 :
3351 40 : const int nXSize = poSrcDS->GetRasterXSize();
3352 40 : const int nYSize = poSrcDS->GetRasterYSize();
3353 40 : const int nBands = poSrcDS->GetRasterCount();
3354 40 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
3355 : auto poDS = std::unique_ptr<VICARDataset>(CreateInternal(
3356 80 : pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
3357 40 : if (poDS == nullptr)
3358 8 : return nullptr;
3359 :
3360 32 : double adfGeoTransform[6] = {0.0};
3361 58 : if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None &&
3362 26 : (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 ||
3363 0 : adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 ||
3364 0 : adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0))
3365 : {
3366 26 : poDS->SetGeoTransform(adfGeoTransform);
3367 : }
3368 :
3369 32 : auto poSrcSRS = poSrcDS->GetSpatialRef();
3370 32 : if (poSrcSRS)
3371 : {
3372 22 : poDS->SetSpatialRef(poSrcSRS);
3373 : }
3374 :
3375 32 : if (poDS->m_bUseSrcLabel && !poDS->m_oSrcJSonLabel.IsValid())
3376 : {
3377 32 : char **papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
3378 32 : if (papszMD_VICAR != nullptr)
3379 : {
3380 12 : poDS->SetMetadata(papszMD_VICAR, "json:VICAR");
3381 : }
3382 : }
3383 :
3384 32 : poDS->m_bInitToNodata = false;
3385 32 : CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS.get(), nullptr,
3386 : pfnProgress, pProgressData);
3387 32 : poDS->FlushCache(false);
3388 32 : if (eErr != CE_None)
3389 : {
3390 10 : return nullptr;
3391 : }
3392 :
3393 22 : return poDS.release();
3394 : }
3395 :
3396 : /************************************************************************/
3397 : /* GDALRegister_VICAR() */
3398 : /************************************************************************/
3399 :
3400 1595 : void GDALRegister_VICAR()
3401 :
3402 : {
3403 1595 : if (GDALGetDriverByName(VICAR_DRIVER_NAME) != nullptr)
3404 302 : return;
3405 :
3406 1293 : GDALDriver *poDriver = new GDALDriver();
3407 1293 : VICARDriverSetCommonMetadata(poDriver);
3408 :
3409 1293 : poDriver->pfnOpen = VICARDataset::Open;
3410 1293 : poDriver->pfnCreate = VICARDataset::Create;
3411 1293 : poDriver->pfnCreateCopy = VICARDataset::CreateCopy;
3412 :
3413 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
3414 : }
|