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