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 = DIV_ROUND_UP(nLabelSize, m_nRecordSize) * m_nRecordSize;
1562 : std::string osLabelSize(
1563 159 : CPLSPrintf("LBLSIZE=%d", static_cast<int>(nLabelSize)));
1564 421 : while (osLabelSize.size() < strlen("LBLSIZE=") + MAX_LOG10_LBLSIZE)
1565 368 : osLabelSize += ' ';
1566 53 : osLabel = osLabelSize + osLabel;
1567 53 : CPLAssert(osLabel.size() <= nLabelSize);
1568 :
1569 : // Write label
1570 53 : VSIFSeekL(fpImage, 0, SEEK_SET);
1571 53 : VSIFWriteL(osLabel.data(), 1, osLabel.size(), fpImage);
1572 53 : const size_t nZeroPadding = nLabelSize - osLabel.size();
1573 53 : if (nZeroPadding)
1574 : {
1575 50 : VSIFWriteL(std::string(nZeroPadding, '\0').data(), 1, nZeroPadding,
1576 : fpImage);
1577 : }
1578 :
1579 53 : if (m_bInitToNodata && m_eCompress == COMPRESS_NONE)
1580 : {
1581 : const int nDTSize =
1582 20 : GDALGetDataTypeSizeBytes(GetRasterBand(1)->GetRasterDataType());
1583 20 : VSIFTruncateL(fpImage, VSIFTellL(fpImage) +
1584 20 : static_cast<vsi_l_offset>(nRasterXSize) *
1585 20 : nRasterYSize * nBands * nDTSize);
1586 : }
1587 :
1588 : // Patch band offsets to take into account label
1589 140 : for (int i = 0; i < nBands; i++)
1590 : {
1591 87 : auto poBand = dynamic_cast<VICARRawRasterBand *>(GetRasterBand(i + 1));
1592 87 : if (poBand)
1593 82 : poBand->nImgOffset += nLabelSize;
1594 : }
1595 53 : }
1596 :
1597 : /************************************************************************/
1598 : /* PatchLabel() */
1599 : /************************************************************************/
1600 :
1601 166 : void VICARDataset::PatchLabel()
1602 : {
1603 166 : if (eAccess == GA_ReadOnly || m_eCompress == COMPRESS_NONE)
1604 161 : return;
1605 :
1606 5 : VSIFSeekL(fpImage, 0, SEEK_END);
1607 5 : const vsi_l_offset nFileSize = VSIFTellL(fpImage);
1608 5 : VSIFSeekL(fpImage, 0, SEEK_SET);
1609 10 : std::string osBuffer;
1610 5 : osBuffer.resize(1024);
1611 5 : size_t nRead = VSIFReadL(&osBuffer[0], 1, 1024, fpImage);
1612 :
1613 : {
1614 5 : CPLString osEOCI1;
1615 5 : osEOCI1.Printf("%u", static_cast<unsigned>(nFileSize));
1616 36 : while (osEOCI1.size() < 10)
1617 31 : osEOCI1 += ' ';
1618 5 : size_t nPos = osBuffer.find("EOCI1=");
1619 5 : CPLAssert(nPos <= nRead - (strlen("EOCI1=") + 10));
1620 5 : memcpy(&osBuffer[nPos + strlen("EOCI1=")], osEOCI1.data(), 10);
1621 : }
1622 :
1623 : {
1624 5 : CPLString osEOCI2;
1625 5 : osEOCI2.Printf("%u", static_cast<unsigned>(nFileSize >> 32));
1626 50 : while (osEOCI2.size() < 10)
1627 45 : osEOCI2 += ' ';
1628 5 : size_t nPos = osBuffer.find("EOCI2=");
1629 5 : CPLAssert(nPos <= nRead - (strlen("EOCI2=") + 10));
1630 5 : memcpy(&osBuffer[nPos + strlen("EOCI2=")], osEOCI2.data(), 10);
1631 : }
1632 5 : VSIFSeekL(fpImage, 0, SEEK_SET);
1633 5 : VSIFWriteL(&osBuffer[0], 1, nRead, fpImage);
1634 : }
1635 :
1636 : /************************************************************************/
1637 : /* BuildLabel() */
1638 : /************************************************************************/
1639 :
1640 53 : void VICARDataset::BuildLabel()
1641 : {
1642 106 : CPLJSONObject oLabel = m_oSrcJSonLabel;
1643 53 : if (!oLabel.IsValid())
1644 : {
1645 38 : oLabel = CPLJSONObject();
1646 : }
1647 :
1648 53 : oLabel.Set("LBLSIZE", 0); // to be overridden later
1649 :
1650 53 : if (!oLabel.GetObj("TYPE").IsValid())
1651 41 : oLabel.Set("TYPE", "IMAGE");
1652 :
1653 53 : const auto eType = GetRasterBand(1)->GetRasterDataType();
1654 53 : const char *pszFormat = "";
1655 53 : CPL_IGNORE_RET_VAL(pszFormat); // Make CSA happy
1656 53 : switch (eType)
1657 : {
1658 33 : case GDT_Byte:
1659 33 : pszFormat = "BYTE";
1660 33 : break;
1661 5 : case GDT_Int16:
1662 5 : pszFormat = "HALF";
1663 5 : break;
1664 3 : case GDT_Int32:
1665 3 : pszFormat = "FULL";
1666 3 : break;
1667 4 : case GDT_Float32:
1668 4 : pszFormat = "REAL";
1669 4 : break;
1670 4 : case GDT_Float64:
1671 4 : pszFormat = "DOUB";
1672 4 : break;
1673 4 : case GDT_CFloat32:
1674 4 : pszFormat = "COMP";
1675 4 : break;
1676 0 : default:
1677 0 : CPLAssert(false);
1678 : break;
1679 : }
1680 53 : oLabel.Set("FORMAT", pszFormat);
1681 :
1682 53 : oLabel.Set("BUFSIZ", m_nRecordSize); // arbitrary value
1683 53 : oLabel.Set("DIM", 3);
1684 53 : oLabel.Set("EOL", 0);
1685 53 : oLabel.Set("RECSIZE", m_nRecordSize);
1686 53 : oLabel.Set("ORG", "BSQ");
1687 53 : oLabel.Set("NL", nRasterYSize);
1688 53 : oLabel.Set("NS", nRasterXSize);
1689 53 : oLabel.Set("NB", nBands);
1690 53 : oLabel.Set("N1", nRasterXSize);
1691 53 : oLabel.Set("N2", nRasterYSize);
1692 53 : oLabel.Set("N3", nBands);
1693 53 : oLabel.Set("N4", 0);
1694 53 : oLabel.Set("NBB", 0);
1695 53 : oLabel.Set("NLB", 0);
1696 53 : oLabel.Set("HOST", "X86-64-LINX");
1697 53 : oLabel.Set("INTFMT", "LOW");
1698 53 : oLabel.Set("REALFMT", "RIEEE");
1699 53 : oLabel.Set("BHOST", "X86-64-LINX");
1700 53 : oLabel.Set("BINTFMT", "LOW");
1701 53 : if (!oLabel.GetObj("BLTYPE").IsValid())
1702 38 : oLabel.Set("BLTYPE", "");
1703 104 : oLabel.Set("COMPRESS", m_eCompress == COMPRESS_BASIC ? "BASIC"
1704 51 : : m_eCompress == COMPRESS_BASIC2 ? "BASIC2"
1705 : : "NONE");
1706 53 : if (m_eCompress == COMPRESS_NONE)
1707 : {
1708 48 : oLabel.Set("EOCI1", 0);
1709 48 : oLabel.Set("EOCI2", 0);
1710 : }
1711 : else
1712 : {
1713 : // To be later patched. Those fake values must take 10 bytes
1714 : // (8 + 2 single quotes) so that they can be later replaced by a
1715 : // integer of maximum value 4294967295 (10 digits)
1716 5 : oLabel.Set("EOCI1", "XXXXXXXX");
1717 5 : oLabel.Set("EOCI2", "XXXXXXXX");
1718 : }
1719 :
1720 53 : if (m_bUseSrcMap)
1721 : {
1722 0 : auto oMap = oLabel.GetObj("PROPERTY/MAP");
1723 0 : if (oMap.IsValid() && oMap.GetType() == CPLJSONObject::Type::Object)
1724 : {
1725 0 : if (!m_osTargetName.empty())
1726 0 : oMap.Set("TARGET_NAME", m_osTargetName);
1727 0 : if (!m_osLatitudeType.empty())
1728 0 : oMap.Set("COORDINATE_SYSTEM_NAME", m_osLatitudeType);
1729 0 : if (!m_osLongitudeDirection.empty())
1730 0 : oMap.Set("POSITIVE_LONGITUDE_DIRECTION",
1731 : m_osLongitudeDirection);
1732 : }
1733 : }
1734 53 : else if (m_bGeoRefFormatIsMIPL)
1735 : {
1736 153 : auto oProperty = oLabel.GetObj("PROPERTY");
1737 51 : if (oProperty.IsValid())
1738 : {
1739 8 : oProperty.Delete("MAP");
1740 8 : oProperty.Delete("GEOTIFF");
1741 : }
1742 51 : if (!m_oSRS.IsEmpty())
1743 : {
1744 37 : BuildLabelPropertyMap(oLabel);
1745 : }
1746 : }
1747 : else
1748 : {
1749 6 : auto oProperty = oLabel.GetObj("PROPERTY");
1750 2 : if (oProperty.IsValid())
1751 : {
1752 1 : oProperty.Delete("MAP");
1753 1 : oProperty.Delete("GEOTIFF");
1754 : }
1755 2 : if (!m_oSRS.IsEmpty())
1756 : {
1757 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
1758 2 : BuildLabelPropertyGeoTIFF(oLabel);
1759 : #endif
1760 : }
1761 : }
1762 :
1763 53 : m_oJSonLabel = std::move(oLabel);
1764 53 : }
1765 :
1766 : /************************************************************************/
1767 : /* BuildLabelPropertyMap() */
1768 : /************************************************************************/
1769 :
1770 37 : void VICARDataset::BuildLabelPropertyMap(CPLJSONObject &oLabel)
1771 : {
1772 37 : if (m_oSRS.IsProjected() || m_oSRS.IsGeographic())
1773 : {
1774 111 : auto oProperty = GetOrCreateJSONObject(oLabel, "PROPERTY");
1775 111 : auto oMap = GetOrCreateJSONObject(oProperty, "MAP");
1776 :
1777 37 : const char *pszDatum = m_oSRS.GetAttrValue("DATUM");
1778 74 : CPLString osTargetName(m_osTargetName);
1779 37 : if (osTargetName.empty())
1780 : {
1781 37 : if (pszDatum && STARTS_WITH(pszDatum, "D_"))
1782 : {
1783 2 : osTargetName = pszDatum + 2;
1784 : }
1785 35 : else if (pszDatum)
1786 : {
1787 35 : osTargetName = pszDatum;
1788 : }
1789 : }
1790 37 : if (!osTargetName.empty())
1791 37 : oMap.Add("TARGET_NAME", osTargetName);
1792 :
1793 37 : oMap.Add("A_AXIS_RADIUS", m_oSRS.GetSemiMajor() / 1000.0);
1794 37 : oMap.Add("B_AXIS_RADIUS", m_oSRS.GetSemiMajor() / 1000.0);
1795 37 : oMap.Add("C_AXIS_RADIUS", m_oSRS.GetSemiMinor() / 1000.0);
1796 :
1797 37 : if (!m_osLatitudeType.empty())
1798 0 : oMap.Add("COORDINATE_SYSTEM_NAME", m_osLatitudeType);
1799 : else
1800 37 : oMap.Add("COORDINATE_SYSTEM_NAME", "PLANETOCENTRIC");
1801 :
1802 37 : if (!m_osLongitudeDirection.empty())
1803 0 : oMap.Add("POSITIVE_LONGITUDE_DIRECTION", m_osLongitudeDirection);
1804 : else
1805 37 : oMap.Add("POSITIVE_LONGITUDE_DIRECTION", "EAST");
1806 :
1807 37 : const char *pszProjection = m_oSRS.GetAttrValue("PROJECTION");
1808 37 : if (pszProjection == nullptr)
1809 : {
1810 35 : oMap.Add("MAP_PROJECTION_TYPE", "SIMPLE_CYLINDRICAL");
1811 35 : oMap.Add("CENTER_LONGITUDE", 0.0);
1812 35 : oMap.Add("CENTER_LATITUDE", 0.0);
1813 : }
1814 2 : else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
1815 : {
1816 0 : oMap.Add("MAP_PROJECTION_TYPE", "EQUIRECTANGULAR");
1817 0 : if (m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
1818 : {
1819 0 : CPLError(CE_Warning, CPLE_NotSupported,
1820 : "Ignoring %s. Only 0 value supported",
1821 : SRS_PP_LATITUDE_OF_ORIGIN);
1822 : }
1823 0 : oMap.Add("CENTER_LONGITUDE",
1824 : m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
1825 : const double dfCenterLat =
1826 0 : m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1827 0 : oMap.Add("CENTER_LATITUDE", dfCenterLat);
1828 : }
1829 2 : else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
1830 : {
1831 2 : oMap.Add("MAP_PROJECTION_TYPE", "SINUSOIDAL");
1832 2 : oMap.Add("CENTER_LONGITUDE",
1833 : m_oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0));
1834 2 : oMap.Add("CENTER_LATITUDE", 0.0);
1835 : }
1836 : else
1837 : {
1838 0 : CPLError(CE_Warning, CPLE_NotSupported,
1839 : "Projection %s not supported", pszProjection);
1840 : }
1841 :
1842 37 : if (oMap["MAP_PROJECTION_TYPE"].IsValid())
1843 : {
1844 37 : if (m_oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
1845 : {
1846 0 : CPLError(CE_Warning, CPLE_NotSupported,
1847 : "Ignoring %s. Only 0 value supported",
1848 : SRS_PP_FALSE_EASTING);
1849 : }
1850 37 : if (m_oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
1851 : {
1852 0 : CPLError(CE_Warning, CPLE_AppDefined,
1853 : "Ignoring %s. Only 0 value supported",
1854 : SRS_PP_FALSE_NORTHING);
1855 : }
1856 :
1857 37 : if (m_bGotTransform)
1858 : {
1859 : const double dfDegToMeter =
1860 37 : m_oSRS.GetSemiMajor() * M_PI / 180.0;
1861 37 : if (m_oSRS.IsProjected())
1862 : {
1863 2 : const double dfLinearUnits = m_oSRS.GetLinearUnits();
1864 2 : const double dfScale = m_adfGeoTransform[1] * dfLinearUnits;
1865 2 : oMap.Add("SAMPLE_PROJECTION_OFFSET",
1866 2 : -m_adfGeoTransform[0] * dfLinearUnits / dfScale -
1867 : 0.5);
1868 2 : oMap.Add("LINE_PROJECTION_OFFSET",
1869 2 : m_adfGeoTransform[3] * dfLinearUnits / dfScale -
1870 : 0.5);
1871 2 : oMap.Add("MAP_SCALE", dfScale / 1000.0);
1872 : }
1873 35 : else if (m_oSRS.IsGeographic())
1874 : {
1875 35 : const double dfScale = m_adfGeoTransform[1] * dfDegToMeter;
1876 35 : oMap.Add("SAMPLE_PROJECTION_OFFSET",
1877 35 : -m_adfGeoTransform[0] * dfDegToMeter / dfScale -
1878 : 0.5);
1879 35 : oMap.Add("LINE_PROJECTION_OFFSET",
1880 35 : m_adfGeoTransform[3] * dfDegToMeter / dfScale -
1881 : 0.5);
1882 35 : oMap.Add("MAP_SCALE", dfScale / 1000.0);
1883 : }
1884 : }
1885 : }
1886 : }
1887 : else
1888 : {
1889 0 : CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
1890 : }
1891 37 : }
1892 :
1893 : /************************************************************************/
1894 : /* BuildLabelPropertyGeoTIFF() */
1895 : /************************************************************************/
1896 :
1897 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
1898 2 : void VICARDataset::BuildLabelPropertyGeoTIFF(CPLJSONObject &oLabel)
1899 : {
1900 4 : auto oProperty = GetOrCreateJSONObject(oLabel, "PROPERTY");
1901 4 : auto oGeoTIFF = GetOrCreateJSONObject(oProperty, "GEOTIFF");
1902 :
1903 : // Ported from Vicar Open Source: Afids expects to be able to read
1904 : // NITF_NROWS and NITF_NCOLS
1905 :
1906 2 : oGeoTIFF.Add("NITF_NROWS", nRasterYSize);
1907 2 : oGeoTIFF.Add("NITF_NCOLS", nRasterXSize);
1908 :
1909 : // Create a in-memory GeoTIFF file
1910 :
1911 : const std::string osTmpFilename(
1912 2 : VSIMemGenerateHiddenFilename("vicar_tmp.tif"));
1913 : GDALDriver *poGTiffDriver =
1914 2 : GDALDriver::FromHandle(GDALGetDriverByName("GTiff"));
1915 2 : if (poGTiffDriver == nullptr)
1916 : {
1917 0 : CPLError(CE_Failure, CPLE_AppDefined, "GTiff driver not available");
1918 0 : return;
1919 : }
1920 2 : const char *const apszOptions[] = {"GEOTIFF_VERSION=1.0", nullptr};
1921 : auto poDS = std::unique_ptr<GDALDataset>(poGTiffDriver->Create(
1922 2 : osTmpFilename.c_str(), 1, 1, 1, GDT_Byte, apszOptions));
1923 2 : if (!poDS)
1924 0 : return;
1925 2 : poDS->SetSpatialRef(&m_oSRS);
1926 2 : if (m_bGotTransform)
1927 2 : poDS->SetGeoTransform(&m_adfGeoTransform[0]);
1928 4 : poDS->SetMetadataItem(GDALMD_AREA_OR_POINT,
1929 2 : GetMetadataItem(GDALMD_AREA_OR_POINT));
1930 2 : poDS.reset();
1931 :
1932 : // Open it with libtiff/libgeotiff
1933 2 : VSILFILE *fpL = VSIFOpenL(osTmpFilename.c_str(), "r");
1934 2 : if (fpL == nullptr)
1935 : {
1936 0 : VSIUnlink(osTmpFilename.c_str());
1937 0 : return;
1938 : }
1939 :
1940 2 : TIFF *hTIFF = VSI_TIFFOpen(osTmpFilename.c_str(), "r", fpL);
1941 2 : CPLAssert(hTIFF);
1942 :
1943 2 : GTIF *hGTIF = GTIFNew(hTIFF);
1944 2 : CPLAssert(hGTIF);
1945 :
1946 : // Get geotiff keys and write them as VICAR metadata
1947 34 : for (const auto &gkey : GTiffShortKeys)
1948 : {
1949 32 : unsigned short val = 0;
1950 32 : if (GDALGTIFKeyGetSHORT(hGTIF, gkey, &val, 0, 1))
1951 : {
1952 20 : oGeoTIFF.Add(
1953 20 : CPLString(GTIFKeyName(gkey)).toupper(),
1954 : CPLSPrintf("%d(%s)", val, GTIFValueNameEx(hGTIF, gkey, val)));
1955 : }
1956 : }
1957 :
1958 62 : for (const auto &gkey : GTiffDoubleKeys)
1959 : {
1960 60 : double val = 0;
1961 60 : if (GDALGTIFKeyGetDOUBLE(hGTIF, gkey, &val, 0, 1))
1962 : {
1963 12 : oGeoTIFF.Add(CPLString(GTIFKeyName(gkey)).toupper(),
1964 : CPLSPrintf("%.17g", val));
1965 : }
1966 : }
1967 :
1968 10 : for (const auto &gkey : GTiffAsciiKeys)
1969 : {
1970 : char szAscii[1024];
1971 8 : if (GDALGTIFKeyGetASCII(hGTIF, gkey, szAscii,
1972 8 : static_cast<int>(sizeof(szAscii))))
1973 : {
1974 4 : oGeoTIFF.Add(CPLString(GTIFKeyName(gkey)).toupper(), szAscii);
1975 : }
1976 : }
1977 :
1978 2 : GTIFFree(hGTIF);
1979 :
1980 : // Get geotiff tags and write them as VICAR metadata
1981 : const std::map<int, const char *> oMapTagCodeToName = {
1982 : {TIFFTAG_GEOPIXELSCALE, "MODELPIXELSCALETAG"},
1983 : {TIFFTAG_GEOTIEPOINTS, "MODELTIEPOINTTAG"},
1984 4 : {TIFFTAG_GEOTRANSMATRIX, "MODELTRANSFORMATIONTAG"}};
1985 :
1986 8 : for (const auto &kv : oMapTagCodeToName)
1987 : {
1988 6 : uint16_t nCount = 0;
1989 6 : double *padfValues = nullptr;
1990 6 : if (TIFFGetField(hTIFF, kv.first, &nCount, &padfValues))
1991 : {
1992 4 : std::string osVal("(");
1993 22 : for (uint16_t i = 0; i < nCount; ++i)
1994 : {
1995 18 : if (i > 0)
1996 14 : osVal += ',';
1997 18 : osVal += CPLSPrintf("%.17g", padfValues[i]);
1998 : }
1999 4 : osVal += ')';
2000 4 : oGeoTIFF.Add(kv.second, osVal);
2001 : }
2002 : }
2003 :
2004 2 : XTIFFClose(hTIFF);
2005 2 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
2006 2 : VSIUnlink(osTmpFilename.c_str());
2007 : }
2008 : #endif
2009 :
2010 : /************************************************************************/
2011 : /* ReadProjectionFromMapGroup() */
2012 : /************************************************************************/
2013 :
2014 27 : void VICARDataset::ReadProjectionFromMapGroup()
2015 : {
2016 27 : double dfXDim = 1.0;
2017 27 : double dfYDim = 1.0;
2018 :
2019 27 : const char *value = GetKeyword("MAP.MAP_SCALE");
2020 27 : if (strlen(value) > 0)
2021 : {
2022 27 : dfXDim = CPLAtof(value) * 1000.0;
2023 27 : dfYDim = CPLAtof(value) * -1 * 1000.0;
2024 : }
2025 :
2026 : const double dfSampleOffset_Shift =
2027 27 : CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Shift", "0.5"));
2028 :
2029 : const double dfLineOffset_Shift =
2030 27 : CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Shift", "0.5"));
2031 :
2032 : const double dfSampleOffset_Mult =
2033 27 : CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Mult", "-1.0"));
2034 :
2035 : const double dfLineOffset_Mult =
2036 27 : CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Mult", "1.0"));
2037 :
2038 : /*********** Grab LINE_PROJECTION_OFFSET ************/
2039 27 : double dfULYMap = 0.5;
2040 :
2041 27 : value = GetKeyword("MAP.LINE_PROJECTION_OFFSET");
2042 27 : if (strlen(value) > 0)
2043 : {
2044 27 : const double yulcenter = CPLAtof(value);
2045 27 : dfULYMap =
2046 27 : ((yulcenter + dfLineOffset_Shift) * -dfYDim * dfLineOffset_Mult);
2047 : }
2048 : /*********** Grab SAMPLE_PROJECTION_OFFSET ************/
2049 27 : double dfULXMap = 0.5;
2050 :
2051 27 : value = GetKeyword("MAP.SAMPLE_PROJECTION_OFFSET");
2052 27 : if (strlen(value) > 0)
2053 : {
2054 27 : const double xulcenter = CPLAtof(value);
2055 27 : dfULXMap =
2056 27 : ((xulcenter + dfSampleOffset_Shift) * dfXDim * dfSampleOffset_Mult);
2057 : }
2058 :
2059 : /* ==================================================================== */
2060 : /* Get the coordinate system. */
2061 : /* ==================================================================== */
2062 27 : bool bProjectionSet = true;
2063 :
2064 : /*********** Grab TARGET_NAME ************/
2065 : /**** This is the planets name i.e. MARS ***/
2066 54 : CPLString target_name = GetKeyword("MAP.TARGET_NAME");
2067 :
2068 : /********** Grab MAP_PROJECTION_TYPE *****/
2069 54 : const CPLString map_proj_name = GetKeyword("MAP.MAP_PROJECTION_TYPE");
2070 :
2071 : /****** Grab semi_major & convert to KM ******/
2072 27 : const double semi_major = CPLAtof(GetKeyword("MAP.A_AXIS_RADIUS")) * 1000.0;
2073 :
2074 : /****** Grab semi-minor & convert to KM ******/
2075 27 : const double semi_minor = CPLAtof(GetKeyword("MAP.C_AXIS_RADIUS")) * 1000.0;
2076 :
2077 : /*********** Grab CENTER_LAT ************/
2078 27 : const double center_lat = CPLAtof(GetKeyword("MAP.CENTER_LATITUDE"));
2079 :
2080 : /*********** Grab CENTER_LON ************/
2081 27 : const double center_lon = CPLAtof(GetKeyword("MAP.CENTER_LONGITUDE"));
2082 :
2083 : /********** Grab 1st std parallel *******/
2084 : const double first_std_parallel =
2085 27 : CPLAtof(GetKeyword("MAP.FIRST_STANDARD_PARALLEL"));
2086 :
2087 : /********** Grab 2nd std parallel *******/
2088 : const double second_std_parallel =
2089 27 : CPLAtof(GetKeyword("MAP.SECOND_STANDARD_PARALLEL"));
2090 :
2091 : /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
2092 : // Need to further study how ocentric/ographic will effect the gdal library.
2093 : // So far we will use this fact to define a sphere or ellipse for some
2094 : // projections Frank - may need to talk this over
2095 27 : bool bIsGeographic = true;
2096 27 : value = GetKeyword("MAP.COORDINATE_SYSTEM_NAME");
2097 27 : if (EQUAL(value, "PLANETOCENTRIC"))
2098 27 : bIsGeographic = false;
2099 :
2100 : /** Set oSRS projection and parameters --- all PDS supported types added
2101 : if apparently supported in oSRS "AITOFF", ** Not supported in GDAL??
2102 : "ALBERS",
2103 : "BONNE",
2104 : "BRIESEMEISTER", ** Not supported in GDAL??
2105 : "CYLINDRICAL EQUAL AREA",
2106 : "EQUIDISTANT",
2107 : "EQUIRECTANGULAR",
2108 : "GNOMONIC",
2109 : "HAMMER", ** Not supported in GDAL??
2110 : "HENDU", ** Not supported in GDAL??
2111 : "LAMBERT AZIMUTHAL EQUAL AREA",
2112 : "LAMBERT CONFORMAL",
2113 : "MERCATOR",
2114 : "MOLLWEIDE",
2115 : "OBLIQUE CYLINDRICAL",
2116 : "ORTHOGRAPHIC",
2117 : "SIMPLE CYLINDRICAL",
2118 : "SINUSOIDAL",
2119 : "STEREOGRAPHIC",
2120 : "TRANSVERSE MERCATOR",
2121 : "VAN DER GRINTEN", ** Not supported in GDAL??
2122 : "WERNER" ** Not supported in GDAL??
2123 : **/
2124 27 : CPLDebug("PDS", "using projection %s\n\n", map_proj_name.c_str());
2125 :
2126 54 : OGRSpatialReference oSRS;
2127 :
2128 27 : if ((EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
2129 39 : (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
2130 12 : (EQUAL(map_proj_name, "EQUIDISTANT")))
2131 : {
2132 15 : oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
2133 : }
2134 12 : else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
2135 : {
2136 0 : oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
2137 : }
2138 12 : else if (EQUAL(map_proj_name, "SINUSOIDAL"))
2139 : {
2140 12 : oSRS.SetSinusoidal(center_lon, 0, 0);
2141 : }
2142 0 : else if (EQUAL(map_proj_name, "MERCATOR"))
2143 : {
2144 0 : oSRS.SetMercator(center_lat, center_lon, 1, 0, 0);
2145 : }
2146 0 : else if (EQUAL(map_proj_name, "STEREOGRAPHIC"))
2147 : {
2148 0 : if ((fabs(center_lat) - 90) < 0.0000001)
2149 : {
2150 0 : oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
2151 : }
2152 : else
2153 : {
2154 0 : oSRS.SetStereographic(center_lat, center_lon, 1, 0, 0);
2155 : }
2156 : }
2157 0 : else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
2158 : {
2159 0 : oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
2160 : }
2161 0 : else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
2162 : {
2163 0 : oSRS.SetTM(center_lat, center_lon, 1, 0, 0);
2164 : }
2165 0 : else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
2166 : {
2167 0 : oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
2168 : center_lon, 0, 0);
2169 : }
2170 0 : else if (EQUAL(map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA"))
2171 : {
2172 0 : oSRS.SetLAEA(center_lat, center_lon, 0, 0);
2173 : }
2174 0 : else if (EQUAL(map_proj_name, "CYLINDRICAL_EQUAL_AREA"))
2175 : {
2176 0 : oSRS.SetCEA(first_std_parallel, center_lon, 0, 0);
2177 : }
2178 0 : else if (EQUAL(map_proj_name, "MOLLWEIDE"))
2179 : {
2180 0 : oSRS.SetMollweide(center_lon, 0, 0);
2181 : }
2182 0 : else if (EQUAL(map_proj_name, "ALBERS"))
2183 : {
2184 0 : oSRS.SetACEA(first_std_parallel, second_std_parallel, center_lat,
2185 : center_lon, 0, 0);
2186 : }
2187 0 : else if (EQUAL(map_proj_name, "BONNE"))
2188 : {
2189 0 : oSRS.SetBonne(first_std_parallel, center_lon, 0, 0);
2190 : }
2191 0 : else if (EQUAL(map_proj_name, "GNOMONIC"))
2192 : {
2193 0 : oSRS.SetGnomonic(center_lat, center_lon, 0, 0);
2194 : #ifdef FIXME
2195 : }
2196 : else if (EQUAL(map_proj_name, "OBLIQUE_CYLINDRICAL"))
2197 : {
2198 : // hope Swiss Oblique Cylindrical is the same
2199 : oSRS.SetSOC(center_lat, center_lon, 0, 0);
2200 : #endif
2201 : }
2202 : else
2203 : {
2204 0 : CPLDebug("VICAR",
2205 : "Dataset projection %s is not supported. Continuing...",
2206 : map_proj_name.c_str());
2207 0 : bProjectionSet = false;
2208 : }
2209 :
2210 27 : if (bProjectionSet)
2211 : {
2212 : // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
2213 81 : const CPLString proj_target_name = map_proj_name + " " + target_name;
2214 27 : oSRS.SetProjCS(proj_target_name); // set ProjCS keyword
2215 :
2216 : // The geographic/geocentric name will be the same basic name as the
2217 : // body name 'GCS' = Geographic/Geocentric Coordinate System
2218 54 : const CPLString geog_name = "GCS_" + target_name;
2219 :
2220 : // The datum and sphere names will be the same basic name aas the planet
2221 54 : const CPLString datum_name = "D_" + target_name;
2222 :
2223 54 : CPLString sphere_name = std::move(target_name);
2224 :
2225 : // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
2226 27 : double iflattening = 0.0;
2227 27 : if ((semi_major - semi_minor) < 0.0000001)
2228 12 : iflattening = 0;
2229 : else
2230 15 : iflattening = semi_major / (semi_major - semi_minor);
2231 :
2232 : // Set the body size but take into consideration which proj is being
2233 : // used to help w/ compatibility Notice that most PDS projections are
2234 : // spherical based on the fact that ISIS/PICS are spherical Set the body
2235 : // size but take into consideration which proj is being used to help w/
2236 : // proj4 compatibility The use of a Sphere, polar radius or ellipse here
2237 : // is based on how ISIS does it internally
2238 27 : if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
2239 54 : (fabs(center_lat) == 90))) ||
2240 27 : (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
2241 : {
2242 0 : if (bIsGeographic)
2243 : {
2244 : // Geograpraphic, so set an ellipse
2245 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
2246 : iflattening, "Reference_Meridian", 0.0);
2247 : }
2248 : else
2249 : {
2250 : // Geocentric, so force a sphere using the semi-minor axis. I
2251 : // hope...
2252 0 : sphere_name += "_polarRadius";
2253 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
2254 : 0.0, "Reference_Meridian", 0.0);
2255 : }
2256 : }
2257 27 : else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
2258 12 : (EQUAL(map_proj_name, "EQUIDISTANT")) ||
2259 12 : (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
2260 51 : (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
2261 12 : (EQUAL(map_proj_name, "SINUSOIDAL")))
2262 : {
2263 : // isis uses the spherical equation for these projections so force a
2264 : // sphere
2265 27 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
2266 : "Reference_Meridian", 0.0);
2267 : }
2268 0 : else if (EQUAL(map_proj_name, "EQUIRECTANGULAR"))
2269 : {
2270 : // isis uses local radius as a sphere, which is pre-calculated in
2271 : // the PDS label as the semi-major
2272 0 : sphere_name += "_localRadius";
2273 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
2274 : "Reference_Meridian", 0.0);
2275 : }
2276 : else
2277 : {
2278 : // All other projections: Mercator, Transverse Mercator, Lambert
2279 : // Conformal, etc. Geographic, so set an ellipse
2280 0 : if (bIsGeographic)
2281 : {
2282 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
2283 : iflattening, "Reference_Meridian", 0.0);
2284 : }
2285 : else
2286 : {
2287 : // Geocentric, so force a sphere. I hope...
2288 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
2289 : 0.0, "Reference_Meridian", 0.0);
2290 : }
2291 : }
2292 :
2293 27 : m_oSRS = std::move(oSRS);
2294 27 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2295 : }
2296 27 : if (bProjectionSet)
2297 : {
2298 27 : m_bGotTransform = true;
2299 27 : m_adfGeoTransform[0] = dfULXMap;
2300 27 : m_adfGeoTransform[1] = dfXDim;
2301 27 : m_adfGeoTransform[2] = 0.0;
2302 27 : m_adfGeoTransform[3] = dfULYMap;
2303 27 : m_adfGeoTransform[4] = 0.0;
2304 27 : m_adfGeoTransform[5] = dfYDim;
2305 : }
2306 27 : }
2307 :
2308 : /************************************************************************/
2309 : /* ReadProjectionFromGeoTIFFGroup() */
2310 : /************************************************************************/
2311 :
2312 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
2313 12 : void VICARDataset::ReadProjectionFromGeoTIFFGroup()
2314 : {
2315 12 : m_bGeoRefFormatIsMIPL = true;
2316 :
2317 : // We will build a in-memory temporary GeoTIFF file from the VICAR GEOTIFF
2318 : // metadata items.
2319 :
2320 : const std::string osTmpFilename(
2321 12 : VSIMemGenerateHiddenFilename("vicar_tmp.tif"));
2322 :
2323 : /* -------------------------------------------------------------------- */
2324 : /* Initialization of libtiff and libgeotiff. */
2325 : /* -------------------------------------------------------------------- */
2326 12 : GTiffOneTimeInit();
2327 12 : LibgeotiffOneTimeInit();
2328 :
2329 : /* -------------------------------------------------------------------- */
2330 : /* Initialize access to the memory geotiff structure. */
2331 : /* -------------------------------------------------------------------- */
2332 12 : VSILFILE *fpL = VSIFOpenL(osTmpFilename.c_str(), "w");
2333 12 : if (fpL == nullptr)
2334 0 : return;
2335 :
2336 12 : TIFF *hTIFF = VSI_TIFFOpen(osTmpFilename.c_str(), "w", fpL);
2337 :
2338 12 : if (hTIFF == nullptr)
2339 : {
2340 0 : CPLError(CE_Failure, CPLE_AppDefined,
2341 : "TIFF/GeoTIFF structure is corrupt.");
2342 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
2343 0 : return;
2344 : }
2345 :
2346 : /* -------------------------------------------------------------------- */
2347 : /* Write some minimal set of image parameters. */
2348 : /* -------------------------------------------------------------------- */
2349 12 : TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, 1);
2350 12 : TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, 1);
2351 12 : TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, 8);
2352 12 : TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
2353 12 : TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, 1);
2354 12 : TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
2355 12 : TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
2356 :
2357 : /* -------------------------------------------------------------------- */
2358 : /* Write geotiff keys from VICAR metadata */
2359 : /* -------------------------------------------------------------------- */
2360 12 : GTIF *hGTIF = GTIFNew(hTIFF);
2361 12 : CPLAssert(hGTIF);
2362 :
2363 60 : for (const auto &gkey : GTiffAsciiKeys)
2364 : {
2365 48 : const char *pszValue = GetKeyword(
2366 96 : ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
2367 : nullptr);
2368 48 : if (pszValue)
2369 : {
2370 8 : GTIFKeySet(hGTIF, gkey, TYPE_ASCII,
2371 8 : static_cast<int>(strlen(pszValue)), pszValue);
2372 : }
2373 : }
2374 :
2375 372 : for (const auto &gkey : GTiffDoubleKeys)
2376 : {
2377 360 : const char *pszValue = GetKeyword(
2378 720 : ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
2379 : nullptr);
2380 360 : if (pszValue)
2381 : {
2382 24 : GTIFKeySet(hGTIF, gkey, TYPE_DOUBLE, 1, CPLAtof(pszValue));
2383 : }
2384 : }
2385 :
2386 204 : for (const auto &gkey : GTiffShortKeys)
2387 : {
2388 192 : const char *pszValue = GetKeyword(
2389 384 : ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
2390 : nullptr);
2391 192 : if (pszValue)
2392 : {
2393 40 : GTIFKeySet(hGTIF, gkey, TYPE_SHORT, 1, atoi(pszValue));
2394 : }
2395 : }
2396 :
2397 12 : GTIFWriteKeys(hGTIF);
2398 12 : GTIFFree(hGTIF);
2399 :
2400 : /* -------------------------------------------------------------------- */
2401 : /* Write geotiff tags from VICAR metadata */
2402 : /* -------------------------------------------------------------------- */
2403 :
2404 : const std::map<const char *, int> oMapTagNameToCode = {
2405 : {"MODELPIXELSCALETAG", TIFFTAG_GEOPIXELSCALE},
2406 : {"MODELTIEPOINTTAG", TIFFTAG_GEOTIEPOINTS},
2407 : {"MODELTRANSFORMATIONTAG", TIFFTAG_GEOTRANSMATRIX},
2408 24 : };
2409 :
2410 48 : for (const auto &kv : oMapTagNameToCode)
2411 : {
2412 : const char *pszValue =
2413 36 : GetKeyword((std::string("GEOTIFF.") + kv.first).c_str(), nullptr);
2414 36 : if (pszValue)
2415 : {
2416 : // Remove leading ( and trailing ), and replace comma by space
2417 : // to separate on it.
2418 : const CPLStringList aosTokens(
2419 24 : CSLTokenizeString2(CPLString(pszValue)
2420 48 : .replaceAll('(', "")
2421 48 : .replaceAll(')', "")
2422 24 : .replaceAll(',', ' ')
2423 : .c_str(),
2424 48 : " ", 0));
2425 24 : if (!aosTokens.empty())
2426 : {
2427 48 : std::vector<double> adfValues;
2428 132 : for (int i = 0; i < aosTokens.size(); ++i)
2429 108 : adfValues.push_back(CPLAtof(aosTokens[i]));
2430 24 : TIFFSetField(hTIFF, kv.second, aosTokens.size(), &adfValues[0]);
2431 : }
2432 : }
2433 : }
2434 :
2435 : /* -------------------------------------------------------------------- */
2436 : /* Finalize the geotiff file. */
2437 : /* -------------------------------------------------------------------- */
2438 :
2439 12 : char bySmallImage = 0;
2440 :
2441 12 : TIFFWriteEncodedStrip(hTIFF, 0, &bySmallImage, 1);
2442 12 : TIFFWriteDirectory(hTIFF);
2443 :
2444 12 : XTIFFClose(hTIFF);
2445 12 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
2446 :
2447 : /* -------------------------------------------------------------------- */
2448 : /* Get georeferencing from file. */
2449 : /* -------------------------------------------------------------------- */
2450 : auto poGTiffDS =
2451 24 : std::unique_ptr<GDALDataset>(GDALDataset::Open(osTmpFilename.c_str()));
2452 12 : if (poGTiffDS)
2453 : {
2454 12 : auto poSRS = poGTiffDS->GetSpatialRef();
2455 12 : if (poSRS)
2456 4 : m_oSRS = *poSRS;
2457 :
2458 12 : if (poGTiffDS->GetGeoTransform(&m_adfGeoTransform[0]) == CE_None)
2459 : {
2460 12 : m_bGotTransform = true;
2461 : }
2462 :
2463 : const char *pszAreaOrPoint =
2464 12 : poGTiffDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
2465 12 : if (pszAreaOrPoint)
2466 4 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, pszAreaOrPoint);
2467 : }
2468 :
2469 12 : VSIUnlink(osTmpFilename.c_str());
2470 : }
2471 : #endif
2472 :
2473 : /************************************************************************/
2474 : /* Open() */
2475 : /************************************************************************/
2476 :
2477 115 : GDALDataset *VICARDataset::Open(GDALOpenInfo *poOpenInfo)
2478 : {
2479 : /* -------------------------------------------------------------------- */
2480 : /* Does this look like a VICAR dataset? */
2481 : /* -------------------------------------------------------------------- */
2482 115 : const vsi_l_offset nLabelOffset = VICARGetLabelOffset(poOpenInfo);
2483 115 : if (nLabelOffset == static_cast<vsi_l_offset>(-1))
2484 0 : return nullptr;
2485 115 : if (nLabelOffset > 0)
2486 : {
2487 4 : CPLString osSubFilename;
2488 : osSubFilename.Printf("/vsisubfile/" CPL_FRMT_GUIB ",%s",
2489 : static_cast<GUIntBig>(nLabelOffset),
2490 2 : poOpenInfo->pszFilename);
2491 4 : GDALOpenInfo oOpenInfo(osSubFilename.c_str(), poOpenInfo->eAccess);
2492 2 : return Open(&oOpenInfo);
2493 : }
2494 :
2495 226 : auto poDS = std::make_unique<VICARDataset>();
2496 113 : poDS->fpImage = poOpenInfo->fpL;
2497 113 : poOpenInfo->fpL = nullptr;
2498 113 : if (!poDS->oKeywords.Ingest(poDS->fpImage, poOpenInfo->pabyHeader))
2499 : {
2500 0 : return nullptr;
2501 : }
2502 :
2503 : /************ CHECK INSTRUMENT/DATA *****************/
2504 :
2505 113 : bool bIsDTM = false;
2506 113 : const char *value = poDS->GetKeyword("DTM.DTM_OFFSET");
2507 113 : if (!EQUAL(value, ""))
2508 : {
2509 0 : bIsDTM = true;
2510 : }
2511 :
2512 113 : bool bInstKnown = false;
2513 : // Check for HRSC
2514 113 : if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
2515 8 : bInstKnown = true;
2516 : // Check for Framing Camera on Dawn
2517 105 : else if (EQUAL(poDS->GetKeyword("INSTRUMENT_ID"), "FC2"))
2518 0 : bInstKnown = true;
2519 :
2520 : /************ Grab dimensions *****************/
2521 :
2522 113 : const int nCols = atoi(poDS->GetKeyword("NS"));
2523 113 : const int nRows = atoi(poDS->GetKeyword("NL"));
2524 113 : const int nBands = atoi(poDS->GetKeyword("NB"));
2525 :
2526 226 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
2527 113 : !GDALCheckBandCount(nBands, false))
2528 : {
2529 0 : CPLError(CE_Failure, CPLE_AppDefined,
2530 : "File %s appears to be a VICAR file, but failed to find some "
2531 : "required keywords.",
2532 : poOpenInfo->pszFilename);
2533 0 : return nullptr;
2534 : }
2535 :
2536 : const GDALDataType eDataType =
2537 113 : GetDataTypeFromFormat(poDS->GetKeyword("FORMAT"));
2538 113 : if (eDataType == GDT_Unknown)
2539 : {
2540 0 : CPLError(CE_Failure, CPLE_AppDefined,
2541 : "Could not find known VICAR label entries!\n");
2542 0 : return nullptr;
2543 : }
2544 113 : double dfNoData = 0.0;
2545 113 : if (eDataType == GDT_Byte)
2546 : {
2547 51 : dfNoData = VICAR_NULL1;
2548 : }
2549 62 : else if (eDataType == GDT_Int16)
2550 : {
2551 15 : dfNoData = VICAR_NULL2;
2552 : }
2553 47 : else if (eDataType == GDT_Float32)
2554 : {
2555 20 : dfNoData = VICAR_NULL3;
2556 : }
2557 :
2558 : /***** CHECK ENDIANNESS **************/
2559 :
2560 113 : RawRasterBand::ByteOrder eByteOrder =
2561 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2562 113 : if (GDALDataTypeIsInteger(eDataType))
2563 : {
2564 71 : value = poDS->GetKeyword("INTFMT", "LOW");
2565 71 : if (EQUAL(value, "LOW"))
2566 : {
2567 68 : eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2568 : }
2569 3 : else if (EQUAL(value, "HIGH"))
2570 : {
2571 3 : eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2572 : }
2573 : else
2574 : {
2575 0 : CPLError(CE_Failure, CPLE_NotSupported,
2576 : "INTFMT=%s layout not supported.", value);
2577 0 : return nullptr;
2578 : }
2579 : }
2580 : else
2581 : {
2582 42 : value = poDS->GetKeyword("REALFMT", "VAX");
2583 42 : if (EQUAL(value, "RIEEE"))
2584 : {
2585 29 : eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2586 : }
2587 13 : else if (EQUAL(value, "IEEE"))
2588 : {
2589 4 : eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2590 : }
2591 9 : else if (EQUAL(value, "VAX"))
2592 : {
2593 9 : eByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
2594 : }
2595 : else
2596 : {
2597 0 : CPLError(CE_Failure, CPLE_NotSupported,
2598 : "REALFMT=%s layout not supported.", value);
2599 0 : return nullptr;
2600 : }
2601 : }
2602 :
2603 : /* -------------------------------------------------------------------- */
2604 : /* Capture some information from the file that is of interest. */
2605 : /* -------------------------------------------------------------------- */
2606 113 : poDS->nRasterXSize = nCols;
2607 113 : poDS->nRasterYSize = nRows;
2608 :
2609 113 : if (poDS->GetKeyword("MAP.MAP_PROJECTION_TYPE")[0] != '\0')
2610 : {
2611 27 : poDS->ReadProjectionFromMapGroup();
2612 : }
2613 : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
2614 168 : else if (poDS->GetKeyword("GEOTIFF.GTMODELTYPEGEOKEY")[0] != '\0' ||
2615 82 : poDS->GetKeyword("GEOTIFF.MODELTIEPOINTTAG")[0] != '\0')
2616 : {
2617 12 : poDS->ReadProjectionFromGeoTIFFGroup();
2618 : }
2619 : #endif
2620 :
2621 113 : if (!poDS->m_bGotTransform)
2622 74 : poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
2623 74 : poOpenInfo->pszFilename, "wld", &poDS->m_adfGeoTransform[0]));
2624 :
2625 113 : poDS->eAccess = poOpenInfo->eAccess;
2626 113 : poDS->m_oJSonLabel = poDS->oKeywords.GetJsonObject();
2627 :
2628 : /* -------------------------------------------------------------------- */
2629 : /* Compute the line offsets. */
2630 : /* -------------------------------------------------------------------- */
2631 :
2632 : uint64_t nPixelOffset;
2633 : uint64_t nLineOffset;
2634 : uint64_t nBandOffset;
2635 : uint64_t nImageOffsetWithoutNBB;
2636 : uint64_t nNBB;
2637 : uint64_t nImageSize;
2638 113 : if (!GetSpacings(poDS->oKeywords, nPixelOffset, nLineOffset, nBandOffset,
2639 226 : nImageOffsetWithoutNBB, nNBB, nImageSize) ||
2640 113 : nImageOffsetWithoutNBB > std::numeric_limits<uint64_t>::max() -
2641 113 : (nNBB + nBandOffset * (nBands - 1)))
2642 : {
2643 0 : CPLDebug("VICAR", "Invalid spacings found");
2644 0 : return nullptr;
2645 : }
2646 :
2647 113 : poDS->m_nRecordSize = atoi(poDS->GetKeyword("RECSIZE", ""));
2648 :
2649 113 : if (nNBB != 0)
2650 : {
2651 2 : const char *pszBLType = poDS->GetKeyword("BLTYPE", nullptr);
2652 : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2653 : const char *pszVicarConf = nullptr;
2654 : #else
2655 2 : const char *pszVicarConf = CPLFindFile("gdal", "vicar.json");
2656 : #endif
2657 4 : CPLJSONDocument oDoc;
2658 2 : if (!pszVicarConf || EQUAL(pszVicarConf, "vicar.json"))
2659 : {
2660 : #ifdef EMBED_RESOURCE_FILES
2661 : oDoc.LoadMemory(VICARGetEmbeddedConf());
2662 : pszVicarConf = "__embedded__";
2663 : #endif
2664 : }
2665 :
2666 2 : if (pszBLType && pszVicarConf && poDS->m_nRecordSize > 0)
2667 : {
2668 :
2669 2 : RawRasterBand::ByteOrder eBINTByteOrder =
2670 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2671 2 : value = poDS->GetKeyword("BINTFMT", "LOW");
2672 2 : if (EQUAL(value, "LOW"))
2673 : {
2674 2 : eBINTByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2675 : }
2676 0 : else if (EQUAL(value, "HIGH"))
2677 : {
2678 0 : eBINTByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2679 : }
2680 : else
2681 : {
2682 0 : CPLError(CE_Failure, CPLE_NotSupported,
2683 : "BINTFMT=%s layout not supported.", value);
2684 : }
2685 :
2686 2 : RawRasterBand::ByteOrder eBREALByteOrder =
2687 : RawRasterBand::ByteOrder::ORDER_VAX;
2688 2 : value = poDS->GetKeyword("BREALFMT", "VAX");
2689 2 : if (EQUAL(value, "RIEEE"))
2690 : {
2691 2 : eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
2692 : }
2693 0 : else if (EQUAL(value, "IEEE"))
2694 : {
2695 0 : eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
2696 : }
2697 0 : else if (EQUAL(value, "VAX"))
2698 : {
2699 0 : eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
2700 : }
2701 : else
2702 : {
2703 0 : CPLError(CE_Failure, CPLE_NotSupported,
2704 : "BREALFMT=%s layout not supported.", value);
2705 : }
2706 :
2707 2 : if (EQUAL(pszVicarConf, "__embedded__") || oDoc.Load(pszVicarConf))
2708 : {
2709 4 : const auto oRoot = oDoc.GetRoot();
2710 2 : if (oRoot.GetType() == CPLJSONObject::Type::Object)
2711 : {
2712 6 : auto oDef = oRoot.GetObj(pszBLType);
2713 8 : if (oDef.IsValid() &&
2714 4 : oDef.GetType() == CPLJSONObject::Type::Object &&
2715 4 : static_cast<GUInt64>(oDef.GetInteger("size")) == nNBB)
2716 : {
2717 : auto poLayer =
2718 : std::unique_ptr<OGRVICARBinaryPrefixesLayer>(
2719 : new OGRVICARBinaryPrefixesLayer(
2720 2 : poDS->fpImage,
2721 4 : static_cast<int>(nImageSize /
2722 2 : poDS->m_nRecordSize),
2723 : oDef, nImageOffsetWithoutNBB,
2724 2 : poDS->m_nRecordSize, eBINTByteOrder,
2725 4 : eBREALByteOrder));
2726 2 : if (!poLayer->HasError())
2727 : {
2728 2 : poDS->m_poLayer = std::move(poLayer);
2729 : }
2730 : }
2731 : }
2732 : }
2733 : }
2734 : }
2735 :
2736 113 : poDS->m_nImageOffsetWithoutNBB =
2737 : static_cast<vsi_l_offset>(nImageOffsetWithoutNBB);
2738 :
2739 226 : CPLString osCompress = poDS->GetKeyword("COMPRESS", "NONE");
2740 113 : if (EQUAL(osCompress, "BASIC") || EQUAL(osCompress, "BASIC2"))
2741 : {
2742 16 : if (poOpenInfo->eAccess == GA_Update)
2743 : {
2744 0 : CPLError(CE_Failure, CPLE_NotSupported,
2745 : "Update of compressed VICAR file not supported");
2746 0 : return nullptr;
2747 : }
2748 16 : poDS->SetMetadataItem("COMPRESS", osCompress, "IMAGE_STRUCTURE");
2749 16 : poDS->m_eCompress =
2750 16 : EQUAL(osCompress, "BASIC") ? COMPRESS_BASIC : COMPRESS_BASIC2;
2751 16 : if (poDS->nRasterYSize > 100 * 1000 * 1000 / nBands)
2752 : {
2753 0 : CPLError(CE_Failure, CPLE_NotSupported,
2754 : "Too many records for compressed dataset");
2755 0 : return nullptr;
2756 : }
2757 16 : if (!GDALDataTypeIsInteger(eDataType))
2758 : {
2759 0 : CPLError(CE_Failure, CPLE_NotSupported,
2760 : "Data type incompatible of compression");
2761 0 : return nullptr;
2762 : }
2763 : // To avoid potential issues in basic_decode()
2764 16 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
2765 16 : if (nDTSize == 0 || poDS->nRasterXSize > INT_MAX / nDTSize)
2766 : {
2767 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large scanline");
2768 0 : return nullptr;
2769 : }
2770 16 : const int nRecords = poDS->nRasterYSize * nBands;
2771 : try
2772 : {
2773 : // + 1 to store implicitly the size of the last record
2774 16 : poDS->m_anRecordOffsets.resize(nRecords + 1);
2775 : }
2776 0 : catch (const std::exception &e)
2777 : {
2778 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2779 0 : return nullptr;
2780 : }
2781 16 : if (poDS->m_eCompress == COMPRESS_BASIC)
2782 : {
2783 5 : poDS->m_anRecordOffsets[0] =
2784 5 : poDS->m_nImageOffsetWithoutNBB + sizeof(GUInt32);
2785 : }
2786 : else
2787 : {
2788 11 : poDS->m_anRecordOffsets[0] =
2789 11 : poDS->m_nImageOffsetWithoutNBB + sizeof(GUInt32) * nRecords;
2790 : }
2791 : }
2792 97 : else if (!EQUAL(osCompress, "NONE"))
2793 : {
2794 0 : CPLError(CE_Failure, CPLE_NotSupported, "COMPRESS=%s not supported",
2795 : osCompress.c_str());
2796 0 : return nullptr;
2797 : }
2798 :
2799 : /* -------------------------------------------------------------------- */
2800 : /* Create band information objects. */
2801 : /* -------------------------------------------------------------------- */
2802 263 : for (int i = 0; i < nBands; i++)
2803 : {
2804 0 : std::unique_ptr<GDALRasterBand> poBand;
2805 :
2806 295 : if (poDS->m_eCompress == COMPRESS_BASIC ||
2807 145 : poDS->m_eCompress == COMPRESS_BASIC2)
2808 : {
2809 32 : poBand = std::make_unique<VICARBASICRasterBand>(poDS.get(), i + 1,
2810 16 : eDataType);
2811 : }
2812 : else
2813 : {
2814 : auto poRawBand = std::make_unique<VICARRawRasterBand>(
2815 134 : poDS.get(), i + 1, poDS->fpImage,
2816 0 : static_cast<vsi_l_offset>(nImageOffsetWithoutNBB + nNBB +
2817 134 : nBandOffset * i),
2818 134 : static_cast<int>(nPixelOffset), static_cast<int>(nLineOffset),
2819 134 : eDataType, eByteOrder);
2820 134 : if (!poRawBand->IsValid())
2821 : {
2822 0 : return nullptr;
2823 : }
2824 134 : poBand = std::move(poRawBand);
2825 : }
2826 :
2827 : // only set NoData if instrument is supported
2828 150 : if (bInstKnown)
2829 8 : poBand->SetNoDataValue(dfNoData);
2830 150 : if (bIsDTM)
2831 : {
2832 0 : poBand->SetScale(static_cast<double>(
2833 0 : CPLAtof(poDS->GetKeyword("DTM.DTM_SCALING_FACTOR"))));
2834 0 : poBand->SetOffset(static_cast<double>(
2835 0 : CPLAtof(poDS->GetKeyword("DTM.DTM_OFFSET"))));
2836 : const char *pszMin =
2837 0 : poDS->GetKeyword("DTM.DTM_MINIMUM_DN", nullptr);
2838 : const char *pszMax =
2839 0 : poDS->GetKeyword("DTM.DTM_MAXIMUM_DN", nullptr);
2840 0 : if (pszMin != nullptr && pszMax != nullptr)
2841 0 : poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax), 0, 0);
2842 : const char *pszNoData =
2843 0 : poDS->GetKeyword("DTM.DTM_MISSING_DN", nullptr);
2844 0 : if (pszNoData != nullptr)
2845 0 : poBand->SetNoDataValue(CPLAtofM(pszNoData));
2846 : }
2847 150 : else if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
2848 : {
2849 8 : double scale = CPLAtof(
2850 : poDS->GetKeyword("DLRTO8.REFLECTANCE_SCALING_FACTOR", "-1."));
2851 8 : if (scale < 0.)
2852 : {
2853 0 : scale = CPLAtof(
2854 : poDS->GetKeyword("HRCAL.REFLECTANCE_SCALING_FACTOR", "1."));
2855 : }
2856 8 : poBand->SetScale(scale);
2857 : double offset =
2858 8 : CPLAtof(poDS->GetKeyword("DLRTO8.REFLECTANCE_OFFSET", "-1."));
2859 8 : if (offset < 0.)
2860 : {
2861 : offset =
2862 0 : CPLAtof(poDS->GetKeyword("HRCAL.REFLECTANCE_OFFSET", "0."));
2863 : }
2864 8 : poBand->SetOffset(offset);
2865 : }
2866 150 : const char *pszMin = poDS->GetKeyword("STATISTICS.MINIMUM", nullptr);
2867 150 : const char *pszMax = poDS->GetKeyword("STATISTICS.MAXIMUM", nullptr);
2868 150 : const char *pszMean = poDS->GetKeyword("STATISTICS.MEAN", nullptr);
2869 : const char *pszStdDev =
2870 150 : poDS->GetKeyword("STATISTICS.STANDARD_DEVIATION", nullptr);
2871 150 : if (pszMin != nullptr && pszMax != nullptr && pszMean != nullptr &&
2872 : pszStdDev != nullptr)
2873 0 : poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax),
2874 0 : CPLAtofM(pszMean), CPLAtofM(pszStdDev));
2875 :
2876 150 : poDS->SetBand(i + 1, std::move(poBand));
2877 : }
2878 :
2879 : /* -------------------------------------------------------------------- */
2880 : /* Instrument-specific keywords as metadata. */
2881 : /* -------------------------------------------------------------------- */
2882 :
2883 : /****************** HRSC ******************************/
2884 :
2885 113 : if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
2886 : {
2887 16 : poDS->SetMetadataItem(
2888 : "SPACECRAFT_NAME",
2889 8 : poDS->GetKeyword("M94_INSTRUMENT.INSTRUMENT_HOST_NAME"));
2890 8 : poDS->SetMetadataItem("PRODUCT_TYPE", poDS->GetKeyword("TYPE"));
2891 :
2892 8 : if (EQUAL(poDS->GetKeyword("M94_INSTRUMENT.DETECTOR_ID"),
2893 : "MEX_HRSC_SRC"))
2894 : {
2895 : static const char *const apszKeywords[] = {
2896 : "M94_ORBIT.IMAGE_TIME",
2897 : "FILE.EVENT_TYPE",
2898 : "FILE.PROCESSING_LEVEL_ID",
2899 : "M94_INSTRUMENT.DETECTOR_ID",
2900 : "M94_CAMERAS.EXPOSURE_DURATION",
2901 : "HRCONVER.INSTRUMENT_TEMPERATURE",
2902 : nullptr};
2903 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2904 : {
2905 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
2906 0 : if (pszKeywordValue != nullptr)
2907 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2908 : }
2909 : }
2910 : else
2911 : {
2912 : static const char *const apszKeywords[] = {
2913 : "M94_ORBIT.START_TIME",
2914 : "M94_ORBIT.STOP_TIME",
2915 : "M94_INSTRUMENT.DETECTOR_ID",
2916 : "M94_CAMERAS.MACROPIXEL_SIZE",
2917 : "FILE.EVENT_TYPE",
2918 : "M94_INSTRUMENT.MISSION_PHASE_NAME",
2919 : "HRORTHO.SPICE_FILE_NAME",
2920 : "HRCONVER.MISSING_FRAMES",
2921 : "HRCONVER.OVERFLOW_FRAMES",
2922 : "HRCONVER.ERROR_FRAMES",
2923 : "HRFOOT.BEST_GROUND_SAMPLING_DISTANCE",
2924 : "DLRTO8.RADIANCE_SCALING_FACTOR",
2925 : "DLRTO8.RADIANCE_OFFSET",
2926 : "DLRTO8.REFLECTANCE_SCALING_FACTOR",
2927 : "DLRTO8.REFLECTANCE_OFFSET",
2928 : "HRCAL.RADIANCE_SCALING_FACTOR",
2929 : "HRCAL.RADIANCE_OFFSET",
2930 : "HRCAL.REFLECTANCE_SCALING_FACTOR",
2931 : "HRCAL.REFLECTANCE_OFFSET",
2932 : "HRORTHO.DTM_NAME",
2933 : "HRORTHO.EXTORI_FILE_NAME",
2934 : "HRORTHO.GEOMETRIC_CALIB_FILE_NAME",
2935 : nullptr};
2936 184 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2937 : {
2938 : const char *pszKeywordValue =
2939 176 : poDS->GetKeyword(apszKeywords[i], nullptr);
2940 176 : if (pszKeywordValue != nullptr)
2941 144 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2942 : }
2943 : }
2944 : }
2945 113 : if (bIsDTM && EQUAL(poDS->GetKeyword("MAP.TARGET_NAME"), "MARS"))
2946 : {
2947 0 : poDS->SetMetadataItem("SPACECRAFT_NAME", "MARS_EXPRESS");
2948 0 : poDS->SetMetadataItem("PRODUCT_TYPE", "DTM");
2949 : static const char *const apszKeywords[] = {
2950 : "DTM.DTM_MISSING_DN", "DTM.DTM_OFFSET",
2951 : "DTM.DTM_SCALING_FACTOR", "DTM.DTM_A_AXIS_RADIUS",
2952 : "DTM.DTM_B_AXIS_RADIUS", "DTM.DTM_C_AXIS_RADIUS",
2953 : "DTM.DTM_DESC", "DTM.DTM_MINIMUM_DN",
2954 : "DTM.DTM_MAXIMUM_DN", nullptr};
2955 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2956 : {
2957 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
2958 0 : if (pszKeywordValue != nullptr)
2959 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2960 : }
2961 : }
2962 :
2963 : /****************** DAWN ******************************/
2964 113 : else if (EQUAL(poDS->GetKeyword("INSTRUMENT_ID"), "FC2"))
2965 : {
2966 0 : poDS->SetMetadataItem("SPACECRAFT_NAME", "DAWN");
2967 : static const char *const apszKeywords[] = {
2968 : "ORBIT_NUMBER",
2969 : "FILTER_NUMBER",
2970 : "FRONT_DOOR_STATUS",
2971 : "FIRST_LINE",
2972 : "FIRST_LINE_SAMPLE",
2973 : "PRODUCER_INSTITUTION_NAME",
2974 : "SOURCE_FILE_NAME",
2975 : "PROCESSING_LEVEL_ID",
2976 : "TARGET_NAME",
2977 : "LIMB_IN_IMAGE",
2978 : "POLE_IN_IMAGE",
2979 : "REFLECTANCE_SCALING_FACTOR",
2980 : "SPICE_FILE_NAME",
2981 : "SPACECRAFT_CENTRIC_LATITUDE",
2982 : "SPACECRAFT_EASTERN_LONGITUDE",
2983 : "FOOTPRINT_POSITIVE_LONGITUDE",
2984 : nullptr};
2985 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2986 : {
2987 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
2988 0 : if (pszKeywordValue != nullptr)
2989 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
2990 : }
2991 : }
2992 113 : else if (bIsDTM && (EQUAL(poDS->GetKeyword("TARGET_NAME"), "VESTA") ||
2993 0 : EQUAL(poDS->GetKeyword("TARGET_NAME"), "CERES")))
2994 : {
2995 0 : poDS->SetMetadataItem("SPACECRAFT_NAME", "DAWN");
2996 0 : poDS->SetMetadataItem("PRODUCT_TYPE", "DTM");
2997 : static const char *const apszKeywords[] = {
2998 : "DTM_MISSING_DN",
2999 : "DTM_OFFSET",
3000 : "DTM_SCALING_FACTOR",
3001 : "DTM_A_AXIS_RADIUS",
3002 : "DTM_B_AXIS_RADIUS",
3003 : "DTM_C_AXIS_RADIUS",
3004 : "DTM_MINIMUM_DN",
3005 : "DTM_MAXIMUM_DN",
3006 : "MAP_PROJECTION_TYPE",
3007 : "COORDINATE_SYSTEM_NAME",
3008 : "POSITIVE_LONGITUDE_DIRECTION",
3009 : "MAP_SCALE",
3010 : "CENTER_LONGITUDE",
3011 : "LINE_PROJECTION_OFFSET",
3012 : "SAMPLE_PROJECTION_OFFSET",
3013 : nullptr};
3014 0 : for (int i = 0; apszKeywords[i] != nullptr; i++)
3015 : {
3016 0 : const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
3017 0 : if (pszKeywordValue != nullptr)
3018 0 : poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
3019 : }
3020 : }
3021 :
3022 : /* -------------------------------------------------------------------- */
3023 : /* Initialize any PAM information. */
3024 : /* -------------------------------------------------------------------- */
3025 113 : poDS->TryLoadXML();
3026 :
3027 : /* -------------------------------------------------------------------- */
3028 : /* Check for overviews. */
3029 : /* -------------------------------------------------------------------- */
3030 113 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
3031 :
3032 113 : return poDS.release();
3033 : }
3034 :
3035 : /************************************************************************/
3036 : /* GetKeyword() */
3037 : /************************************************************************/
3038 :
3039 3561 : const char *VICARDataset::GetKeyword(const char *pszPath,
3040 : const char *pszDefault)
3041 :
3042 : {
3043 3561 : return oKeywords.GetKeyword(pszPath, pszDefault);
3044 : }
3045 :
3046 : /************************************************************************/
3047 : /* GetDataTypeFromFormat() */
3048 : /************************************************************************/
3049 :
3050 276 : GDALDataType VICARDataset::GetDataTypeFromFormat(const char *pszFormat)
3051 : {
3052 276 : if (EQUAL(pszFormat, "BYTE"))
3053 110 : return GDT_Byte;
3054 :
3055 166 : if (EQUAL(pszFormat, "HALF") || EQUAL(pszFormat, "WORD"))
3056 39 : return GDT_Int16;
3057 :
3058 127 : if (EQUAL(pszFormat, "FULL") || EQUAL(pszFormat, "LONG"))
3059 13 : return GDT_Int32;
3060 :
3061 114 : if (EQUAL(pszFormat, "REAL"))
3062 56 : return GDT_Float32;
3063 :
3064 58 : if (EQUAL(pszFormat, "DOUB"))
3065 29 : return GDT_Float64;
3066 :
3067 29 : if (EQUAL(pszFormat, "COMP") || EQUAL(pszFormat, "COMPLEX"))
3068 29 : return GDT_CFloat32;
3069 :
3070 0 : return GDT_Unknown;
3071 : }
3072 :
3073 : /************************************************************************/
3074 : /* GetSpacings() */
3075 : /************************************************************************/
3076 :
3077 163 : bool VICARDataset::GetSpacings(const VICARKeywordHandler &keywords,
3078 : uint64_t &nPixelOffset, uint64_t &nLineOffset,
3079 : uint64_t &nBandOffset,
3080 : uint64_t &nImageOffsetWithoutNBB, uint64_t &nNBB,
3081 : uint64_t &nImageSize)
3082 : {
3083 : const GDALDataType eDataType =
3084 163 : GetDataTypeFromFormat(keywords.GetKeyword("FORMAT", ""));
3085 163 : if (eDataType == GDT_Unknown)
3086 0 : return false;
3087 163 : const uint64_t nItemSize = GDALGetDataTypeSizeBytes(eDataType);
3088 163 : const char *value = keywords.GetKeyword("ORG", "BSQ");
3089 : // number of bytes of binary prefix before each record
3090 163 : nNBB = atoi(keywords.GetKeyword("NBB", ""));
3091 163 : const uint64_t nCols64 = atoi(keywords.GetKeyword("NS", ""));
3092 163 : const uint64_t nRows64 = atoi(keywords.GetKeyword("NL", ""));
3093 163 : const uint64_t nBands64 = atoi(keywords.GetKeyword("NB", ""));
3094 : try
3095 : {
3096 163 : if (EQUAL(value, "BIP"))
3097 : {
3098 6 : nPixelOffset = (CPLSM(nItemSize) * CPLSM(nBands64)).v();
3099 6 : nBandOffset = nItemSize;
3100 6 : nLineOffset =
3101 6 : (CPLSM(nNBB) + CPLSM(nPixelOffset) * CPLSM(nCols64)).v();
3102 6 : nImageSize = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
3103 : }
3104 157 : else if (EQUAL(value, "BIL"))
3105 : {
3106 6 : nPixelOffset = nItemSize;
3107 6 : nBandOffset = (CPLSM(nItemSize) * CPLSM(nCols64)).v();
3108 6 : nLineOffset =
3109 6 : (CPLSM(nNBB) + CPLSM(nBandOffset) * CPLSM(nBands64)).v();
3110 6 : nImageSize = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
3111 : }
3112 151 : else if (EQUAL(value, "BSQ"))
3113 : {
3114 151 : nPixelOffset = nItemSize;
3115 151 : nLineOffset =
3116 151 : (CPLSM(nNBB) + CPLSM(nPixelOffset) * CPLSM(nCols64)).v();
3117 151 : nBandOffset = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
3118 151 : nImageSize = (CPLSM(nBandOffset) * CPLSM(nBands64)).v();
3119 : }
3120 : else
3121 : {
3122 0 : CPLError(CE_Failure, CPLE_NotSupported,
3123 : "ORG=%s layout not supported.", value);
3124 0 : return false;
3125 : }
3126 : }
3127 0 : catch (const CPLSafeIntOverflow &)
3128 : {
3129 0 : return false;
3130 : }
3131 :
3132 163 : const uint64_t nLabelSize = atoi(keywords.GetKeyword("LBLSIZE", ""));
3133 163 : const uint64_t nRecordSize = atoi(keywords.GetKeyword("RECSIZE", ""));
3134 163 : const uint64_t nNLB = atoi(keywords.GetKeyword("NLB", ""));
3135 : try
3136 : {
3137 163 : nImageOffsetWithoutNBB =
3138 163 : (CPLSM(nLabelSize) + CPLSM(nRecordSize) * CPLSM(nNLB) + CPLSM(nNBB))
3139 163 : .v();
3140 163 : nImageOffsetWithoutNBB -= nNBB;
3141 : }
3142 0 : catch (const CPLSafeIntOverflow &)
3143 : {
3144 0 : return false;
3145 : }
3146 163 : return true;
3147 : }
3148 :
3149 : /************************************************************************/
3150 : /* Create() */
3151 : /************************************************************************/
3152 :
3153 62 : GDALDataset *VICARDataset::Create(const char *pszFilename, int nXSize,
3154 : int nYSize, int nBandsIn, GDALDataType eType,
3155 : char **papszOptions)
3156 : {
3157 62 : return CreateInternal(pszFilename, nXSize, nYSize, nBandsIn, eType,
3158 62 : papszOptions);
3159 : }
3160 :
3161 102 : VICARDataset *VICARDataset::CreateInternal(const char *pszFilename, int nXSize,
3162 : int nYSize, int nBandsIn,
3163 : GDALDataType eType,
3164 : char **papszOptions)
3165 : {
3166 102 : if (eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Int32 &&
3167 46 : eType != GDT_Float32 && eType != GDT_Float64 && eType != GDT_CFloat32)
3168 : {
3169 38 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
3170 38 : return nullptr;
3171 : }
3172 :
3173 64 : const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
3174 64 : if (nXSize == 0 || nYSize == 0 || nPixelOffset > INT_MAX / nXSize)
3175 : {
3176 0 : CPLError(CE_Failure, CPLE_NotSupported,
3177 : "Unsupported raster dimensions");
3178 0 : return nullptr;
3179 : }
3180 64 : const int nLineOffset = nXSize * nPixelOffset;
3181 :
3182 64 : if (nBandsIn == 0 || nBandsIn > 32767)
3183 : {
3184 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
3185 1 : return nullptr;
3186 : }
3187 :
3188 : const char *pszCompress =
3189 63 : CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE");
3190 63 : CompressMethod eCompress = COMPRESS_NONE;
3191 63 : if (EQUAL(pszCompress, "NONE"))
3192 : {
3193 53 : eCompress = COMPRESS_NONE;
3194 : }
3195 10 : else if (EQUAL(pszCompress, "BASIC"))
3196 : {
3197 6 : eCompress = COMPRESS_BASIC;
3198 : }
3199 4 : else if (EQUAL(pszCompress, "BASIC2"))
3200 : {
3201 3 : eCompress = COMPRESS_BASIC2;
3202 : }
3203 : else
3204 : {
3205 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported COMPRESS value");
3206 1 : return nullptr;
3207 : }
3208 71 : if (eCompress != COMPRESS_NONE &&
3209 9 : (!GDALDataTypeIsInteger(eType) || nBandsIn != 1))
3210 : {
3211 2 : CPLError(
3212 : CE_Failure, CPLE_NotSupported,
3213 : "BASIC/BASIC2 compression only supports one-band integer datasets");
3214 2 : return nullptr;
3215 : }
3216 :
3217 120 : std::vector<vsi_l_offset> anRecordOffsets;
3218 60 : if (eCompress != COMPRESS_NONE)
3219 : {
3220 7 : const GUInt64 nMaxEncodedSize =
3221 7 : static_cast<GUInt64>(nXSize) * nPixelOffset +
3222 7 : static_cast<GUInt64>(nXSize) * nPixelOffset / 2 + 11;
3223 : // To avoid potential later int overflows
3224 7 : if (nMaxEncodedSize > static_cast<GUInt64>(INT_MAX))
3225 : {
3226 1 : CPLError(CE_Failure, CPLE_NotSupported, "Too large scanline");
3227 1 : return nullptr;
3228 : }
3229 6 : if (nYSize > 100 * 1000 * 1000)
3230 : {
3231 1 : CPLError(CE_Failure, CPLE_NotSupported,
3232 : "Too many records for compressed dataset");
3233 1 : return nullptr;
3234 : }
3235 : try
3236 : {
3237 : // + 1 to store implicitly the size of the last record
3238 5 : anRecordOffsets.resize(nYSize + 1);
3239 : }
3240 0 : catch (const std::exception &e)
3241 : {
3242 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
3243 0 : return nullptr;
3244 : }
3245 : }
3246 :
3247 116 : CPLJSONObject oSrcJSonLabel;
3248 58 : oSrcJSonLabel.Deinit();
3249 :
3250 58 : const char *pszLabel = CSLFetchNameValue(papszOptions, "LABEL");
3251 58 : if (pszLabel)
3252 : {
3253 4 : CPLJSONDocument oJSONDocument;
3254 4 : if (pszLabel[0] == '{')
3255 : {
3256 2 : const GByte *pabyData = reinterpret_cast<const GByte *>(pszLabel);
3257 2 : if (!oJSONDocument.LoadMemory(pabyData))
3258 : {
3259 1 : return nullptr;
3260 : }
3261 : }
3262 : else
3263 : {
3264 2 : if (!oJSONDocument.Load(pszLabel))
3265 : {
3266 1 : return nullptr;
3267 : }
3268 : }
3269 :
3270 2 : oSrcJSonLabel = oJSONDocument.GetRoot();
3271 2 : if (!oSrcJSonLabel.IsValid())
3272 : {
3273 0 : return nullptr;
3274 : }
3275 : }
3276 :
3277 56 : VSILFILE *fp = VSIFOpenExL(pszFilename, "wb+", true);
3278 56 : if (fp == nullptr)
3279 : {
3280 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
3281 : VSIGetLastErrorMsg());
3282 3 : return nullptr;
3283 : }
3284 :
3285 53 : VICARDataset *poDS = new VICARDataset();
3286 53 : poDS->fpImage = fp;
3287 53 : poDS->nRasterXSize = nXSize;
3288 53 : poDS->nRasterYSize = nYSize;
3289 53 : poDS->m_nRecordSize = nLineOffset;
3290 53 : poDS->m_bIsLabelWritten = false;
3291 53 : poDS->m_bGeoRefFormatIsMIPL = EQUAL(
3292 : CSLFetchNameValueDef(papszOptions, "GEOREF_FORMAT", "MIPL"), "MIPL");
3293 53 : poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
3294 53 : poDS->m_bUseSrcMap = CPLFetchBool(papszOptions, "USE_SRC_MAP", false);
3295 : poDS->m_osLatitudeType =
3296 53 : CSLFetchNameValueDef(papszOptions, "COORDINATE_SYSTEM_NAME", "");
3297 : poDS->m_osLongitudeDirection =
3298 53 : CSLFetchNameValueDef(papszOptions, "POSITIVE_LONGITUDE_DIRECTION", "");
3299 : poDS->m_osTargetName =
3300 53 : CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
3301 53 : poDS->m_bInitToNodata = true;
3302 53 : poDS->m_oSrcJSonLabel = std::move(oSrcJSonLabel);
3303 53 : poDS->m_eCompress = eCompress;
3304 53 : poDS->m_anRecordOffsets = std::move(anRecordOffsets);
3305 53 : poDS->eAccess = GA_Update;
3306 :
3307 : /* -------------------------------------------------------------------- */
3308 : /* Create band information objects. */
3309 : /* -------------------------------------------------------------------- */
3310 53 : const vsi_l_offset nBandOffset =
3311 53 : static_cast<vsi_l_offset>(nLineOffset) * nYSize;
3312 140 : for (int i = 0; i < nBandsIn; i++)
3313 : {
3314 : GDALRasterBand *poBand;
3315 87 : if (eCompress != COMPRESS_NONE)
3316 : {
3317 5 : poBand = new VICARBASICRasterBand(poDS, i + 1, eType);
3318 : }
3319 : else
3320 : {
3321 82 : poBand = new VICARRawRasterBand(
3322 : poDS, i + 1, poDS->fpImage,
3323 82 : i * nBandOffset, // will be set later to final value since we
3324 : // need to include the label size
3325 : nPixelOffset, nLineOffset, eType,
3326 82 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN);
3327 : }
3328 87 : poDS->SetBand(i + 1, poBand);
3329 : }
3330 :
3331 53 : return poDS;
3332 : }
3333 :
3334 : /************************************************************************/
3335 : /* CreateCopy() */
3336 : /************************************************************************/
3337 :
3338 41 : GDALDataset *VICARDataset::CreateCopy(const char *pszFilename,
3339 : GDALDataset *poSrcDS, int /*bStrict*/,
3340 : char **papszOptions,
3341 : GDALProgressFunc pfnProgress,
3342 : void *pProgressData)
3343 : {
3344 41 : if (poSrcDS->GetRasterCount() == 0)
3345 : {
3346 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
3347 1 : return nullptr;
3348 : }
3349 :
3350 40 : const int nXSize = poSrcDS->GetRasterXSize();
3351 40 : const int nYSize = poSrcDS->GetRasterYSize();
3352 40 : const int nBands = poSrcDS->GetRasterCount();
3353 40 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
3354 : auto poDS = std::unique_ptr<VICARDataset>(CreateInternal(
3355 80 : pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
3356 40 : if (poDS == nullptr)
3357 8 : return nullptr;
3358 :
3359 32 : double adfGeoTransform[6] = {0.0};
3360 58 : if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None &&
3361 26 : (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 ||
3362 0 : adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 ||
3363 0 : adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0))
3364 : {
3365 26 : poDS->SetGeoTransform(adfGeoTransform);
3366 : }
3367 :
3368 32 : auto poSrcSRS = poSrcDS->GetSpatialRef();
3369 32 : if (poSrcSRS)
3370 : {
3371 22 : poDS->SetSpatialRef(poSrcSRS);
3372 : }
3373 :
3374 32 : if (poDS->m_bUseSrcLabel && !poDS->m_oSrcJSonLabel.IsValid())
3375 : {
3376 32 : char **papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
3377 32 : if (papszMD_VICAR != nullptr)
3378 : {
3379 12 : poDS->SetMetadata(papszMD_VICAR, "json:VICAR");
3380 : }
3381 : }
3382 :
3383 32 : poDS->m_bInitToNodata = false;
3384 32 : CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS.get(), nullptr,
3385 : pfnProgress, pProgressData);
3386 32 : poDS->FlushCache(false);
3387 32 : if (eErr != CE_None)
3388 : {
3389 10 : return nullptr;
3390 : }
3391 :
3392 22 : return poDS.release();
3393 : }
3394 :
3395 : /************************************************************************/
3396 : /* GDALRegister_VICAR() */
3397 : /************************************************************************/
3398 :
3399 1911 : void GDALRegister_VICAR()
3400 :
3401 : {
3402 1911 : if (GDALGetDriverByName(VICAR_DRIVER_NAME) != nullptr)
3403 282 : return;
3404 :
3405 1629 : GDALDriver *poDriver = new GDALDriver();
3406 1629 : VICARDriverSetCommonMetadata(poDriver);
3407 :
3408 1629 : poDriver->pfnOpen = VICARDataset::Open;
3409 1629 : poDriver->pfnCreate = VICARDataset::Create;
3410 1629 : poDriver->pfnCreateCopy = VICARDataset::CreateCopy;
3411 :
3412 1629 : GetGDALDriverManager()->RegisterDriver(poDriver);
3413 : }
|