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