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