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