Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISIS Version 2 Driver
4 : * Purpose: Implementation of ISIS2Dataset
5 : * Author: Trent Hare (thare@usgs.gov),
6 : * Robert Soricone (rsoricone@usgs.gov)
7 : * Ludovic Mercier (ludovic.mercier@gmail.com)
8 : * Frank Warmerdam (warmerdam@pobox.com)
9 : *
10 : * NOTE: Original code authored by Trent and Robert and placed in the public
11 : * domain as per US government policy. I have (within my rights) appropriated
12 : * it and placed it under the following license. This is not intended to
13 : * diminish Trent and Roberts contribution.
14 : ******************************************************************************
15 : * Copyright (c) 2006, Frank Warmerdam <warmerdam@pobox.com>
16 : * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
17 : *
18 : * SPDX-License-Identifier: MIT
19 : ****************************************************************************/
20 :
21 : constexpr int NULL1 = 0;
22 : constexpr int NULL2 = -32768;
23 : constexpr double NULL3 = -3.4028226550889044521e+38;
24 :
25 : constexpr int RECORD_SIZE = 512;
26 :
27 : #include "cpl_string.h"
28 : #include "gdal_frmts.h"
29 : #include "nasakeywordhandler.h"
30 : #include "ogr_spatialref.h"
31 : #include "rawdataset.h"
32 : #include "pdsdrivercore.h"
33 :
34 : /************************************************************************/
35 : /* ==================================================================== */
36 : /* ISISDataset version2 */
37 : /* ==================================================================== */
38 : /************************************************************************/
39 :
40 : class ISIS2Dataset final : public RawDataset
41 : {
42 : VSILFILE *fpImage; // image data file.
43 : CPLString osExternalCube;
44 :
45 : NASAKeywordHandler oKeywords;
46 :
47 : int bGotTransform;
48 : double adfGeoTransform[6];
49 :
50 : OGRSpatialReference m_oSRS{};
51 :
52 : int parse_label(const char *file, char *keyword, char *value);
53 : int strstrip(char instr[], char outstr[], int position);
54 :
55 : CPLString oTempResult;
56 :
57 : static void CleanString(CPLString &osInput);
58 :
59 : const char *GetKeyword(const char *pszPath, const char *pszDefault = "");
60 : const char *GetKeywordSub(const char *pszPath, int iSubscript,
61 : const char *pszDefault = "");
62 :
63 : CPLErr Close() override;
64 :
65 : public:
66 : ISIS2Dataset();
67 : virtual ~ISIS2Dataset();
68 :
69 : virtual CPLErr GetGeoTransform(double *padfTransform) override;
70 : const OGRSpatialReference *GetSpatialRef() const override;
71 :
72 : virtual char **GetFileList() override;
73 :
74 : static GDALDataset *Open(GDALOpenInfo *);
75 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
76 : int nBandsIn, GDALDataType eType,
77 : char **papszParamList);
78 :
79 : // Write related.
80 : static int WriteRaster(const std::string &osFilename, bool includeLabel,
81 : GUIntBig iRecord, GUIntBig iLabelRecords,
82 : GDALDataType eType, const char *pszInterleaving);
83 :
84 : static int WriteLabel(const std::string &osFilename,
85 : const std::string &osRasterFile,
86 : const std::string &sObjectTag, unsigned int nXSize,
87 : unsigned int nYSize, unsigned int nBandsIn,
88 : GDALDataType eType, GUIntBig iRecords,
89 : const char *pszInterleaving, GUIntBig &iLabelRecords,
90 : bool bRelaunch = false);
91 : static int WriteQUBE_Information(VSILFILE *fpLabel, unsigned int iLevel,
92 : unsigned int &nWritingBytes,
93 : unsigned int nXSize, unsigned int nYSize,
94 : unsigned int nBandsIn, GDALDataType eType,
95 : const char *pszInterleaving);
96 :
97 : static unsigned int WriteKeyword(VSILFILE *fpLabel, unsigned int iLevel,
98 : CPLString key, CPLString value);
99 : static unsigned int WriteFormatting(VSILFILE *fpLabel, CPLString data);
100 : static GUIntBig RecordSizeCalculation(unsigned int nXSize,
101 : unsigned int nYSize,
102 : unsigned int nBands,
103 : GDALDataType eType);
104 : };
105 :
106 : /************************************************************************/
107 : /* ISIS2Dataset() */
108 : /************************************************************************/
109 :
110 45 : ISIS2Dataset::ISIS2Dataset() : fpImage(nullptr), bGotTransform(FALSE)
111 : {
112 45 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
113 45 : adfGeoTransform[0] = 0.0;
114 45 : adfGeoTransform[1] = 1.0;
115 45 : adfGeoTransform[2] = 0.0;
116 45 : adfGeoTransform[3] = 0.0;
117 45 : adfGeoTransform[4] = 0.0;
118 45 : adfGeoTransform[5] = 1.0;
119 45 : }
120 :
121 : /************************************************************************/
122 : /* ~ISIS2Dataset() */
123 : /************************************************************************/
124 :
125 90 : ISIS2Dataset::~ISIS2Dataset()
126 :
127 : {
128 45 : ISIS2Dataset::Close();
129 90 : }
130 :
131 : /************************************************************************/
132 : /* Close() */
133 : /************************************************************************/
134 :
135 88 : CPLErr ISIS2Dataset::Close()
136 : {
137 88 : CPLErr eErr = CE_None;
138 88 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
139 : {
140 45 : if (ISIS2Dataset::FlushCache(true) != CE_None)
141 0 : eErr = CE_Failure;
142 45 : if (fpImage != nullptr)
143 : {
144 43 : if (VSIFCloseL(fpImage) != 0)
145 0 : eErr = CE_Failure;
146 : }
147 45 : if (GDALPamDataset::Close() != CE_None)
148 0 : eErr = CE_Failure;
149 : }
150 88 : return eErr;
151 : }
152 :
153 : /************************************************************************/
154 : /* GetFileList() */
155 : /************************************************************************/
156 :
157 3 : char **ISIS2Dataset::GetFileList()
158 :
159 : {
160 3 : char **papszFileList = GDALPamDataset::GetFileList();
161 :
162 3 : if (!osExternalCube.empty())
163 1 : papszFileList = CSLAddString(papszFileList, osExternalCube);
164 :
165 3 : return papszFileList;
166 : }
167 :
168 : /************************************************************************/
169 : /* GetSpatialRef() */
170 : /************************************************************************/
171 :
172 1 : const OGRSpatialReference *ISIS2Dataset::GetSpatialRef() const
173 : {
174 1 : if (!m_oSRS.IsEmpty())
175 1 : return &m_oSRS;
176 0 : return GDALPamDataset::GetSpatialRef();
177 : }
178 :
179 : /************************************************************************/
180 : /* GetGeoTransform() */
181 : /************************************************************************/
182 :
183 14 : CPLErr ISIS2Dataset::GetGeoTransform(double *padfTransform)
184 :
185 : {
186 14 : if (bGotTransform)
187 : {
188 1 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
189 1 : return CE_None;
190 : }
191 :
192 13 : return GDALPamDataset::GetGeoTransform(padfTransform);
193 : }
194 :
195 : /************************************************************************/
196 : /* Open() */
197 : /************************************************************************/
198 :
199 45 : GDALDataset *ISIS2Dataset::Open(GDALOpenInfo *poOpenInfo)
200 : {
201 : /* -------------------------------------------------------------------- */
202 : /* Does this look like a CUBE or an IMAGE Primary Data Object? */
203 : /* -------------------------------------------------------------------- */
204 45 : if (!ISIS2DriverIdentify(poOpenInfo) || poOpenInfo->fpL == nullptr)
205 0 : return nullptr;
206 :
207 45 : VSILFILE *fpQube = poOpenInfo->fpL;
208 45 : poOpenInfo->fpL = nullptr;
209 :
210 90 : auto poDS = std::make_unique<ISIS2Dataset>();
211 :
212 45 : if (!poDS->oKeywords.Ingest(fpQube, 0))
213 : {
214 0 : VSIFCloseL(fpQube);
215 0 : return nullptr;
216 : }
217 :
218 45 : VSIFCloseL(fpQube);
219 :
220 : /* -------------------------------------------------------------------- */
221 : /* We assume the user is pointing to the label (i.e. .lab) file. */
222 : /* -------------------------------------------------------------------- */
223 : // QUBE can be inline or detached and point to an image name
224 : // ^QUBE = 76
225 : // ^QUBE = ("ui31s015.img",6441<BYTES>) - has another label on the image
226 : // ^QUBE = "ui31s015.img" - which implies no label or skip value
227 :
228 45 : const char *pszQube = poDS->GetKeyword("^QUBE");
229 45 : int nQube = 0;
230 45 : int bByteLocation = FALSE;
231 90 : CPLString osTargetFile = poOpenInfo->pszFilename;
232 :
233 45 : if (pszQube[0] == '"')
234 : {
235 0 : const CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename);
236 0 : CPLString osFilename = pszQube;
237 0 : poDS->CleanString(osFilename);
238 0 : osTargetFile = CPLFormCIFilename(osTPath, osFilename, nullptr);
239 0 : poDS->osExternalCube = osTargetFile;
240 : }
241 45 : else if (pszQube[0] == '(')
242 : {
243 6 : const CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename);
244 6 : CPLString osFilename = poDS->GetKeywordSub("^QUBE", 1, "");
245 3 : poDS->CleanString(osFilename);
246 3 : osTargetFile = CPLFormCIFilename(osTPath, osFilename, nullptr);
247 3 : poDS->osExternalCube = osTargetFile;
248 :
249 3 : nQube = atoi(poDS->GetKeywordSub("^QUBE", 2, "1"));
250 3 : if (strstr(poDS->GetKeywordSub("^QUBE", 2, "1"), "<BYTES>") != nullptr)
251 0 : bByteLocation = true;
252 : }
253 : else
254 : {
255 42 : nQube = atoi(pszQube);
256 42 : if (strstr(pszQube, "<BYTES>") != nullptr)
257 0 : bByteLocation = true;
258 : }
259 :
260 : /* -------------------------------------------------------------------- */
261 : /* Check if file an ISIS2 header file? Read a few lines of text */
262 : /* searching for something starting with nrows or ncols. */
263 : /* -------------------------------------------------------------------- */
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Checks to see if this is valid ISIS2 cube */
267 : /* SUFFIX_ITEM tag in .cub file should be (0,0,0); no side-planes */
268 : /* -------------------------------------------------------------------- */
269 45 : const int s_ix = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 1));
270 45 : const int s_iy = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 2));
271 45 : const int s_iz = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 3));
272 :
273 45 : if (s_ix != 0 || s_iy != 0 || s_iz != 0)
274 : {
275 0 : CPLError(CE_Failure, CPLE_OpenFailed,
276 : "*** ISIS 2 cube file has invalid SUFFIX_ITEMS parameters:\n"
277 : "*** gdal isis2 driver requires (0, 0, 0), thus no sideplanes "
278 : "or backplanes\n"
279 : "found: (%i, %i, %i)\n\n",
280 : s_ix, s_iy, s_iz);
281 0 : return nullptr;
282 : }
283 :
284 : /**************** end SUFFIX_ITEM check ***********************/
285 :
286 : /*********** Grab layout type (BSQ, BIP, BIL) ************/
287 : // AXIS_NAME = (SAMPLE,LINE,BAND)
288 : /***********************************************************/
289 :
290 45 : char szLayout[10] = "BSQ"; // default to band seq.
291 45 : const char *value = poDS->GetKeyword("QUBE.AXIS_NAME", "");
292 45 : if (EQUAL(value, "(SAMPLE,LINE,BAND)"))
293 45 : strcpy(szLayout, "BSQ");
294 0 : else if (EQUAL(value, "(BAND,LINE,SAMPLE)"))
295 0 : strcpy(szLayout, "BIP");
296 0 : else if (EQUAL(value, "(SAMPLE,BAND,LINE)") || EQUAL(value, ""))
297 0 : strcpy(szLayout, "BSQ");
298 : else
299 : {
300 0 : CPLError(CE_Failure, CPLE_OpenFailed,
301 : "%s layout not supported. Abort\n\n", value);
302 0 : return nullptr;
303 : }
304 :
305 : /*********** Grab samples lines band ************/
306 45 : const int nCols = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 1));
307 45 : const int nRows = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 2));
308 45 : const int nBands = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 3));
309 :
310 : /*********** Grab Qube record bytes **********/
311 45 : const int record_bytes = atoi(poDS->GetKeyword("RECORD_BYTES"));
312 45 : if (record_bytes < 0)
313 : {
314 0 : return nullptr;
315 : }
316 :
317 45 : GUIntBig nSkipBytes = 0;
318 45 : if (nQube > 0 && bByteLocation)
319 0 : nSkipBytes = (nQube - 1);
320 45 : else if (nQube > 0)
321 45 : nSkipBytes = static_cast<GUIntBig>(nQube - 1) * record_bytes;
322 : else
323 0 : nSkipBytes = 0;
324 :
325 : /*********** Grab samples lines band ************/
326 45 : char chByteOrder = 'M'; // default to MSB
327 90 : CPLString osCoreItemType = poDS->GetKeyword("QUBE.CORE_ITEM_TYPE");
328 45 : if ((EQUAL(osCoreItemType, "PC_INTEGER")) ||
329 60 : (EQUAL(osCoreItemType, "PC_UNSIGNED_INTEGER")) ||
330 15 : (EQUAL(osCoreItemType, "PC_REAL")))
331 : {
332 43 : chByteOrder = 'I';
333 : }
334 :
335 : /******** Grab format type - isis2 only supports 8,16,32 *******/
336 45 : GDALDataType eDataType = GDT_Byte;
337 45 : bool bNoDataSet = false;
338 45 : double dfNoData = 0.0;
339 :
340 45 : int itype = atoi(poDS->GetKeyword("QUBE.CORE_ITEM_BYTES", ""));
341 45 : switch (itype)
342 : {
343 20 : case 1:
344 20 : eDataType = GDT_Byte;
345 20 : dfNoData = NULL1;
346 20 : bNoDataSet = true;
347 20 : break;
348 10 : case 2:
349 10 : if (strstr(osCoreItemType, "UNSIGNED") != nullptr)
350 : {
351 5 : dfNoData = 0;
352 5 : eDataType = GDT_UInt16;
353 : }
354 : else
355 : {
356 5 : dfNoData = NULL2;
357 5 : eDataType = GDT_Int16;
358 : }
359 10 : bNoDataSet = true;
360 10 : break;
361 10 : case 4:
362 10 : eDataType = GDT_Float32;
363 10 : dfNoData = NULL3;
364 10 : bNoDataSet = true;
365 10 : break;
366 5 : case 8:
367 5 : eDataType = GDT_Float64;
368 5 : dfNoData = NULL3;
369 5 : bNoDataSet = true;
370 5 : break;
371 0 : default:
372 0 : CPLError(CE_Failure, CPLE_AppDefined,
373 : "Itype of %d is not supported in ISIS 2.", itype);
374 0 : return nullptr;
375 : }
376 :
377 : /*********** Grab Cellsize ************/
378 45 : double dfXDim = 1.0;
379 45 : double dfYDim = 1.0;
380 :
381 45 : value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_SCALE");
382 45 : if (strlen(value) > 0)
383 : {
384 : // Convert km to m
385 2 : dfXDim = static_cast<float>(CPLAtof(value) * 1000.0);
386 2 : dfYDim = static_cast<float>(CPLAtof(value) * 1000.0 * -1);
387 : }
388 :
389 : /*********** Grab LINE_PROJECTION_OFFSET ************/
390 45 : double dfULYMap = 0.5;
391 :
392 : value =
393 45 : poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
394 45 : if (strlen(value) > 0)
395 : {
396 2 : const double yulcenter = static_cast<float>(CPLAtof(value)) * dfYDim;
397 2 : dfULYMap = yulcenter - (dfYDim / 2);
398 : }
399 :
400 : /*********** Grab SAMPLE_PROJECTION_OFFSET ************/
401 45 : double dfULXMap = 0.5;
402 :
403 : value =
404 45 : poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET");
405 45 : if (strlen(value) > 0)
406 : {
407 2 : const double xulcenter = static_cast<float>(CPLAtof(value)) * dfXDim;
408 2 : dfULXMap = xulcenter - (dfXDim / 2);
409 : }
410 :
411 : /*********** Grab TARGET_NAME ************/
412 : /**** This is the planets name i.e. MARS ***/
413 90 : const CPLString target_name = poDS->GetKeyword("QUBE.TARGET_NAME");
414 :
415 : /*********** Grab MAP_PROJECTION_TYPE ************/
416 : CPLString map_proj_name =
417 90 : poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE");
418 45 : poDS->CleanString(map_proj_name);
419 :
420 : /*********** Grab SEMI-MAJOR ************/
421 : const double semi_major =
422 45 : CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) *
423 45 : 1000.0;
424 :
425 : /*********** Grab semi-minor ************/
426 : const double semi_minor =
427 45 : CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) *
428 45 : 1000.0;
429 :
430 : /*********** Grab CENTER_LAT ************/
431 : const double center_lat =
432 45 : CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
433 :
434 : /*********** Grab CENTER_LON ************/
435 : const double center_lon =
436 45 : CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
437 :
438 : /*********** Grab 1st std parallel ************/
439 45 : const double first_std_parallel = CPLAtof(
440 : poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
441 :
442 : /*********** Grab 2nd std parallel ************/
443 45 : const double second_std_parallel = CPLAtof(
444 : poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL"));
445 :
446 : /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
447 : // Need to further study how ocentric/ographic will effect the gdal library.
448 : // So far we will use this fact to define a sphere or ellipse for some
449 : // projections Frank - may need to talk this over
450 45 : bool bIsGeographic = true;
451 : value =
452 45 : poDS->GetKeyword("CUBE.IMAGE_MAP_PROJECTION.PROJECTION_LATITUDE_TYPE");
453 45 : if (EQUAL(value, "\"PLANETOCENTRIC\""))
454 0 : bIsGeographic = false;
455 :
456 45 : CPLDebug("ISIS2", "using projection %s", map_proj_name.c_str());
457 :
458 90 : OGRSpatialReference oSRS;
459 45 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
460 45 : bool bProjectionSet = true;
461 :
462 : // Set oSRS projection and parameters
463 45 : if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
464 90 : (EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
465 45 : (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")))
466 : {
467 2 : oSRS.OGRSpatialReference::SetEquirectangular2(0.0, center_lon,
468 : center_lat, 0, 0);
469 : }
470 43 : else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
471 : {
472 0 : oSRS.OGRSpatialReference::SetOrthographic(center_lat, center_lon, 0, 0);
473 : }
474 86 : else if ((EQUAL(map_proj_name, "SINUSOIDAL")) ||
475 43 : (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")))
476 : {
477 0 : oSRS.OGRSpatialReference::SetSinusoidal(center_lon, 0, 0);
478 : }
479 43 : else if (EQUAL(map_proj_name, "MERCATOR"))
480 : {
481 0 : oSRS.OGRSpatialReference::SetMercator(center_lat, center_lon, 1, 0, 0);
482 : }
483 43 : else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
484 : {
485 0 : oSRS.OGRSpatialReference::SetPS(center_lat, center_lon, 1, 0, 0);
486 : }
487 43 : else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
488 : {
489 0 : oSRS.OGRSpatialReference::SetTM(center_lat, center_lon, 1, 0, 0);
490 : }
491 43 : else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
492 : {
493 0 : oSRS.OGRSpatialReference::SetLCC(first_std_parallel,
494 : second_std_parallel, center_lat,
495 : center_lon, 0, 0);
496 : }
497 43 : else if (EQUAL(map_proj_name, ""))
498 : {
499 : /* no projection */
500 43 : bProjectionSet = false;
501 : }
502 : else
503 : {
504 0 : CPLDebug("ISIS2",
505 : "Dataset projection %s is not supported. Continuing...",
506 : map_proj_name.c_str());
507 0 : bProjectionSet = false;
508 : }
509 :
510 45 : if (bProjectionSet)
511 : {
512 : // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
513 6 : const CPLString proj_target_name = map_proj_name + " " + target_name;
514 2 : oSRS.SetProjCS(proj_target_name); // set ProjCS keyword
515 :
516 : // The geographic/geocentric name will be the same basic name as the
517 : // body name 'GCS' = Geographic/Geocentric Coordinate System
518 4 : const CPLString geog_name = "GCS_" + target_name;
519 :
520 : // The datum and sphere names will be the same basic name aas the planet
521 4 : const CPLString datum_name = "D_" + target_name;
522 : // Might not be IAU defined so don't add.
523 4 : CPLString sphere_name = target_name; // + "_IAU_IAG");
524 :
525 : // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
526 2 : double iflattening = 0.0;
527 2 : if ((semi_major - semi_minor) < 0.0000001)
528 2 : iflattening = 0;
529 : else
530 0 : iflattening = semi_major / (semi_major - semi_minor);
531 :
532 : // Set the body size but take into consideration which proj is being
533 : // used to help w/ proj4 compatibility The use of a Sphere, polar radius
534 : // or ellipse here is based on how ISIS does it internally
535 2 : if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
536 4 : (fabs(center_lat) == 90))) ||
537 2 : (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
538 : {
539 0 : if (bIsGeographic)
540 : {
541 : // Geograpraphic, so set an ellipse
542 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
543 : iflattening, "Reference_Meridian", 0.0);
544 : }
545 : else
546 : {
547 : // Geocentric, so force a sphere using the semi-minor axis. I
548 : // hope...
549 0 : sphere_name += "_polarRadius";
550 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
551 : 0.0, "Reference_Meridian", 0.0);
552 : }
553 : }
554 2 : else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
555 0 : (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
556 0 : (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
557 2 : (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")) ||
558 0 : (EQUAL(map_proj_name, "SINUSOIDAL")))
559 : {
560 : // ISIS uses the spherical equation for these projections so force
561 : // a sphere.
562 2 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
563 : "Reference_Meridian", 0.0);
564 : }
565 0 : else if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
566 0 : (EQUAL(map_proj_name, "EQUIRECTANGULAR")))
567 : {
568 : // Calculate localRadius using ISIS3 simple elliptical method
569 : // not the more standard Radius of Curvature method
570 : // PI = 4 * atan(1);
571 0 : if (center_lon == 0)
572 : { // No need to calculate local radius
573 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
574 : 0.0, "Reference_Meridian", 0.0);
575 : }
576 : else
577 : {
578 0 : const double radLat = center_lat * M_PI / 180; // in radians
579 : const double localRadius =
580 0 : semi_major * semi_minor /
581 0 : sqrt(pow(semi_minor * cos(radLat), 2) +
582 0 : pow(semi_major * sin(radLat), 2));
583 0 : sphere_name += "_localRadius";
584 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, localRadius,
585 : 0.0, "Reference_Meridian", 0.0);
586 0 : CPLDebug("ISIS2", "local radius: %f", localRadius);
587 : }
588 : }
589 : else
590 : {
591 : // All other projections: Mercator, Transverse Mercator, Lambert
592 : // Conformal, etc. Geographic, so set an ellipse
593 0 : if (bIsGeographic)
594 : {
595 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
596 : iflattening, "Reference_Meridian", 0.0);
597 : }
598 : else
599 : {
600 : // Geocentric, so force a sphere. I hope...
601 0 : oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
602 : 0.0, "Reference_Meridian", 0.0);
603 : }
604 : }
605 :
606 : // translate back into a projection string.
607 2 : poDS->m_oSRS = std::move(oSRS);
608 : }
609 :
610 : /* END ISIS2 Label Read */
611 : /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Did we get the required keywords? If not we return with */
615 : /* this never having been considered to be a match. This isn't */
616 : /* an error! */
617 : /* -------------------------------------------------------------------- */
618 90 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
619 45 : !GDALCheckBandCount(nBands, false))
620 : {
621 2 : return nullptr;
622 : }
623 :
624 : /* -------------------------------------------------------------------- */
625 : /* Capture some information from the file that is of interest. */
626 : /* -------------------------------------------------------------------- */
627 43 : poDS->nRasterXSize = nCols;
628 43 : poDS->nRasterYSize = nRows;
629 :
630 : /* -------------------------------------------------------------------- */
631 : /* Open target binary file. */
632 : /* -------------------------------------------------------------------- */
633 :
634 43 : if (poOpenInfo->eAccess == GA_ReadOnly)
635 19 : poDS->fpImage = VSIFOpenL(osTargetFile, "rb");
636 : else
637 24 : poDS->fpImage = VSIFOpenL(osTargetFile, "r+b");
638 :
639 43 : if (poDS->fpImage == nullptr)
640 : {
641 0 : CPLError(CE_Failure, CPLE_OpenFailed,
642 : "Failed to open %s with write permission.\n%s",
643 0 : osTargetFile.c_str(), VSIStrerror(errno));
644 0 : return nullptr;
645 : }
646 :
647 43 : poDS->eAccess = poOpenInfo->eAccess;
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Compute the line offset. */
651 : /* -------------------------------------------------------------------- */
652 43 : int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
653 : int nLineOffset, nPixelOffset;
654 : vsi_l_offset nBandOffset;
655 :
656 43 : if (EQUAL(szLayout, "BIP"))
657 : {
658 0 : nPixelOffset = nItemSize * nBands;
659 0 : if (nPixelOffset > INT_MAX / nBands)
660 : {
661 0 : return nullptr;
662 : }
663 0 : nLineOffset = nPixelOffset * nCols;
664 0 : nBandOffset = nItemSize;
665 : }
666 43 : else if (EQUAL(szLayout, "BSQ"))
667 : {
668 43 : nPixelOffset = nItemSize;
669 43 : if (nPixelOffset > INT_MAX / nCols)
670 : {
671 0 : return nullptr;
672 : }
673 43 : nLineOffset = nPixelOffset * nCols;
674 43 : nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
675 : }
676 : else /* assume BIL */
677 : {
678 0 : nPixelOffset = nItemSize;
679 0 : if (nPixelOffset > INT_MAX / nBands ||
680 0 : nPixelOffset * nBands > INT_MAX / nCols)
681 : {
682 0 : return nullptr;
683 : }
684 0 : nLineOffset = nItemSize * nBands * nCols;
685 0 : nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols;
686 : }
687 :
688 : /* -------------------------------------------------------------------- */
689 : /* Create band information objects. */
690 : /* -------------------------------------------------------------------- */
691 132 : for (int i = 0; i < nBands; i++)
692 : {
693 : auto poBand = RawRasterBand::Create(
694 178 : poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i,
695 : nPixelOffset, nLineOffset, eDataType,
696 : chByteOrder == 'I' || chByteOrder == 'L'
697 89 : ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
698 : : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
699 89 : RawRasterBand::OwnFP::NO);
700 89 : if (!poBand)
701 0 : return nullptr;
702 :
703 89 : if (bNoDataSet)
704 89 : poBand->SetNoDataValue(dfNoData);
705 :
706 : // Set offset/scale values at the PAM level.
707 89 : poBand->SetOffset(CPLAtofM(poDS->GetKeyword("QUBE.CORE_BASE", "0.0")));
708 178 : poBand->SetScale(
709 89 : CPLAtofM(poDS->GetKeyword("QUBE.CORE_MULTIPLIER", "1.0")));
710 :
711 89 : poDS->SetBand(i + 1, std::move(poBand));
712 : }
713 :
714 : /* -------------------------------------------------------------------- */
715 : /* Check for a .prj file. For isis2 I would like to keep this in */
716 : /* -------------------------------------------------------------------- */
717 86 : const CPLString osPath = CPLGetPath(poOpenInfo->pszFilename);
718 86 : const CPLString osName = CPLGetBasename(poOpenInfo->pszFilename);
719 43 : const char *pszPrjFile = CPLFormCIFilename(osPath, osName, "prj");
720 :
721 43 : VSILFILE *fp = VSIFOpenL(pszPrjFile, "r");
722 43 : if (fp != nullptr)
723 : {
724 0 : VSIFCloseL(fp);
725 :
726 0 : char **papszLines = CSLLoad(pszPrjFile);
727 :
728 0 : poDS->m_oSRS.importFromESRI(papszLines);
729 :
730 0 : CSLDestroy(papszLines);
731 : }
732 :
733 43 : if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
734 : {
735 2 : poDS->bGotTransform = TRUE;
736 2 : poDS->adfGeoTransform[0] = dfULXMap;
737 2 : poDS->adfGeoTransform[1] = dfXDim;
738 2 : poDS->adfGeoTransform[2] = 0.0;
739 2 : poDS->adfGeoTransform[3] = dfULYMap;
740 2 : poDS->adfGeoTransform[4] = 0.0;
741 2 : poDS->adfGeoTransform[5] = dfYDim;
742 : }
743 :
744 43 : if (!poDS->bGotTransform)
745 41 : poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "cbw",
746 41 : poDS->adfGeoTransform);
747 :
748 43 : if (!poDS->bGotTransform)
749 41 : poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "wld",
750 41 : poDS->adfGeoTransform);
751 :
752 : /* -------------------------------------------------------------------- */
753 : /* Initialize any PAM information. */
754 : /* -------------------------------------------------------------------- */
755 43 : poDS->SetDescription(poOpenInfo->pszFilename);
756 43 : poDS->TryLoadXML();
757 :
758 : /* -------------------------------------------------------------------- */
759 : /* Check for overviews. */
760 : /* -------------------------------------------------------------------- */
761 43 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
762 :
763 43 : return poDS.release();
764 : }
765 :
766 : /************************************************************************/
767 : /* GetKeyword() */
768 : /************************************************************************/
769 :
770 943 : const char *ISIS2Dataset::GetKeyword(const char *pszPath,
771 : const char *pszDefault)
772 :
773 : {
774 943 : return oKeywords.GetKeyword(pszPath, pszDefault);
775 : }
776 :
777 : /************************************************************************/
778 : /* GetKeywordSub() */
779 : /************************************************************************/
780 :
781 279 : const char *ISIS2Dataset::GetKeywordSub(const char *pszPath, int iSubscript,
782 : const char *pszDefault)
783 :
784 : {
785 279 : const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
786 :
787 279 : if (pszResult == nullptr)
788 0 : return pszDefault;
789 :
790 279 : if (pszResult[0] != '(')
791 0 : return pszDefault;
792 :
793 : char **papszTokens =
794 279 : CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
795 :
796 279 : if (iSubscript <= CSLCount(papszTokens))
797 : {
798 279 : oTempResult = papszTokens[iSubscript - 1];
799 279 : CSLDestroy(papszTokens);
800 279 : return oTempResult.c_str();
801 : }
802 :
803 0 : CSLDestroy(papszTokens);
804 0 : return pszDefault;
805 : }
806 :
807 : /************************************************************************/
808 : /* CleanString() */
809 : /* */
810 : /* Removes single or double quotes, and converts spaces to underscores. */
811 : /* The change is made in-place to CPLString. */
812 : /************************************************************************/
813 :
814 48 : void ISIS2Dataset::CleanString(CPLString &osInput)
815 :
816 : {
817 53 : if ((osInput.size() < 2) ||
818 5 : ((osInput.at(0) != '"' || osInput.back() != '"') &&
819 5 : (osInput.at(0) != '\'' || osInput.back() != '\'')))
820 48 : return;
821 :
822 0 : char *pszWrk = CPLStrdup(osInput.c_str() + 1);
823 :
824 0 : pszWrk[strlen(pszWrk) - 1] = '\0';
825 :
826 0 : for (int i = 0; pszWrk[i] != '\0'; i++)
827 : {
828 0 : if (pszWrk[i] == ' ')
829 0 : pszWrk[i] = '_';
830 : }
831 :
832 0 : osInput = pszWrk;
833 0 : CPLFree(pszWrk);
834 : }
835 :
836 : /************************************************************************/
837 : /* Create() */
838 : /************************************************************************/
839 : /**
840 : * Hidden Creation Options:
841 : * INTERLEAVE=BSQ/BIP/BIL: Force the generation specified type of interleaving.
842 : * BSQ --- band sequental (default),
843 : * BIP --- band interleaved by pixel,
844 : * BIL --- band interleaved by line.
845 : * OBJECT=QUBE/IMAGE/SPECTRAL_QUBE, if null default is QUBE
846 : */
847 :
848 62 : GDALDataset *ISIS2Dataset::Create(const char *pszFilename, int nXSize,
849 : int nYSize, int nBandsIn, GDALDataType eType,
850 : char **papszParamList)
851 : {
852 :
853 : /* Verify settings. In Isis 2 core pixel values can be represented in
854 : * three different ways : 1, 2 4, or 8 Bytes */
855 62 : if (eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Float32 &&
856 27 : eType != GDT_UInt16 && eType != GDT_Float64)
857 : {
858 24 : CPLError(
859 : CE_Failure, CPLE_AppDefined,
860 : "The ISIS2 driver does not supporting creating files of type %s.",
861 : GDALGetDataTypeName(eType));
862 24 : return nullptr;
863 : }
864 :
865 : /* (SAMPLE, LINE, BAND) - Band Sequential (BSQ) - default choice
866 : (SAMPLE, BAND, LINE) - Band Interleaved by Line (BIL)
867 : (BAND, SAMPLE, LINE) - Band Interleaved by Pixel (BIP) */
868 38 : const char *pszInterleaving = "(SAMPLE,LINE,BAND)";
869 : const char *pszInterleavingParam =
870 38 : CSLFetchNameValue(papszParamList, "INTERLEAVE");
871 38 : if (pszInterleavingParam)
872 : {
873 0 : if (STARTS_WITH_CI(pszInterleavingParam, "bip"))
874 0 : pszInterleaving = "(BAND,SAMPLE,LINE)";
875 0 : else if (STARTS_WITH_CI(pszInterleavingParam, "bil"))
876 0 : pszInterleaving = "(SAMPLE,BAND,LINE)";
877 : else
878 0 : pszInterleaving = "(SAMPLE,LINE,BAND)";
879 : }
880 :
881 : /* default labeling method is attached */
882 38 : bool bAttachedLabelingMethod = true;
883 : /* check if labeling method is set : check the all three first chars */
884 : const char *pszLabelingMethod =
885 38 : CSLFetchNameValue(papszParamList, "LABELING_METHOD");
886 38 : if (pszLabelingMethod)
887 : {
888 1 : if (STARTS_WITH_CI(pszLabelingMethod, "det" /* "detached" */))
889 : {
890 1 : bAttachedLabelingMethod = false;
891 : }
892 1 : if (STARTS_WITH_CI(pszLabelingMethod, "att" /* attached" */))
893 : {
894 0 : bAttachedLabelingMethod = true;
895 : }
896 : }
897 :
898 : /* set the label and data files */
899 76 : CPLString osLabelFile, osRasterFile, osOutFile;
900 38 : if (bAttachedLabelingMethod)
901 : {
902 37 : osLabelFile = "";
903 37 : osRasterFile = pszFilename;
904 37 : osOutFile = osRasterFile;
905 : }
906 : else
907 : {
908 1 : CPLString sExtension = "cub";
909 : const char *pszExtension =
910 1 : CSLFetchNameValue(papszParamList, "IMAGE_EXTENSION");
911 1 : if (pszExtension)
912 : {
913 1 : sExtension = pszExtension;
914 : }
915 :
916 1 : if (EQUAL(CPLGetExtension(pszFilename), sExtension))
917 : {
918 0 : CPLError(CE_Failure, CPLE_AppDefined,
919 : "IMAGE_EXTENSION (%s) cannot match LABEL file extension.",
920 : sExtension.c_str());
921 0 : return nullptr;
922 : }
923 :
924 1 : osLabelFile = pszFilename;
925 1 : osRasterFile = CPLResetExtension(osLabelFile, sExtension);
926 1 : osOutFile = osLabelFile;
927 : }
928 :
929 38 : const char *pszObject = CSLFetchNameValue(papszParamList, "OBJECT");
930 76 : CPLString sObject = "QUBE"; // default choice
931 38 : if (pszObject)
932 : {
933 0 : if (EQUAL(pszObject, "IMAGE"))
934 : {
935 0 : sObject = "IMAGE";
936 : }
937 0 : if (EQUAL(pszObject, "SPECTRAL_QUBE"))
938 : {
939 0 : sObject = "SPECTRAL_QUBE";
940 : }
941 : }
942 :
943 : GUIntBig iRecords =
944 38 : ISIS2Dataset::RecordSizeCalculation(nXSize, nYSize, nBandsIn, eType);
945 38 : GUIntBig iLabelRecords(2);
946 :
947 38 : CPLDebug("ISIS2", "irecord = %i", static_cast<int>(iRecords));
948 :
949 38 : if (bAttachedLabelingMethod)
950 : {
951 37 : ISIS2Dataset::WriteLabel(osRasterFile, "", sObject, nXSize, nYSize,
952 : nBandsIn, eType, iRecords, pszInterleaving,
953 : iLabelRecords, true);
954 : }
955 : else
956 : {
957 1 : ISIS2Dataset::WriteLabel(osLabelFile, osRasterFile, sObject, nXSize,
958 : nYSize, nBandsIn, eType, iRecords,
959 : pszInterleaving, iLabelRecords);
960 : }
961 :
962 38 : if (!ISIS2Dataset::WriteRaster(osRasterFile, bAttachedLabelingMethod,
963 : iRecords, iLabelRecords, eType,
964 : pszInterleaving))
965 13 : return nullptr;
966 :
967 25 : return GDALDataset::FromHandle(GDALOpen(osOutFile, GA_Update));
968 : }
969 :
970 : /************************************************************************/
971 : /* WriteRaster() */
972 : /************************************************************************/
973 :
974 38 : int ISIS2Dataset::WriteRaster(const std::string &osFilename, bool includeLabel,
975 : GUIntBig iRecords, GUIntBig iLabelRecords,
976 : CPL_UNUSED GDALDataType eType,
977 : CPL_UNUSED const char *pszInterleaving)
978 : {
979 38 : VSILFILE *fpBin = VSIFOpenL(osFilename.c_str(), includeLabel ? "ab" : "wb");
980 38 : if (fpBin == nullptr)
981 : {
982 3 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
983 3 : osFilename.c_str(), VSIStrerror(errno));
984 3 : return FALSE;
985 : }
986 :
987 35 : GUIntBig nSize = iRecords * RECORD_SIZE;
988 35 : CPLDebug("ISIS2", "nSize = %i", static_cast<int>(nSize));
989 :
990 35 : if (includeLabel)
991 34 : nSize = iLabelRecords * RECORD_SIZE + nSize;
992 :
993 : // write last byte
994 35 : const GByte byZero(0);
995 70 : if (VSIFSeekL(fpBin, nSize - 1, SEEK_SET) != 0 ||
996 35 : VSIFWriteL(&byZero, 1, 1, fpBin) != 1)
997 : {
998 10 : CPLError(CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s",
999 10 : osFilename.c_str(), VSIStrerror(errno));
1000 10 : VSIFCloseL(fpBin);
1001 10 : return FALSE;
1002 : }
1003 25 : VSIFCloseL(fpBin);
1004 :
1005 25 : return TRUE;
1006 : }
1007 :
1008 : /************************************************************************/
1009 : /* RecordSizeCalculation() */
1010 : /************************************************************************/
1011 38 : GUIntBig ISIS2Dataset::RecordSizeCalculation(unsigned int nXSize,
1012 : unsigned int nYSize,
1013 : unsigned int nBandsIn,
1014 : GDALDataType eType)
1015 :
1016 : {
1017 38 : const GUIntBig n = static_cast<GUIntBig>(nXSize) * nYSize * nBandsIn *
1018 38 : (GDALGetDataTypeSize(eType) / 8);
1019 : // size of pds file is a multiple of RECORD_SIZE Bytes.
1020 38 : CPLDebug("ISIS2", "n = %i", static_cast<int>(n));
1021 38 : CPLDebug("ISIS2", "RECORD SIZE = %i", RECORD_SIZE);
1022 38 : CPLDebug("ISIS2", "nXSize = %i", nXSize);
1023 38 : CPLDebug("ISIS2", "nYSize = %i", nYSize);
1024 38 : CPLDebug("ISIS2", "nBands = %i", nBandsIn);
1025 38 : CPLDebug("ISIS2", "DataTypeSize = %i", GDALGetDataTypeSize(eType));
1026 38 : return static_cast<GUIntBig>(ceil(static_cast<float>(n) / RECORD_SIZE));
1027 : }
1028 :
1029 : /************************************************************************/
1030 : /* WriteQUBE_Information() */
1031 : /************************************************************************/
1032 :
1033 35 : int ISIS2Dataset::WriteQUBE_Information(
1034 : VSILFILE *fpLabel, unsigned int iLevel, unsigned int &nWritingBytes,
1035 : unsigned int nXSize, unsigned int nYSize, unsigned int nBandsIn,
1036 : GDALDataType eType, const char *pszInterleaving)
1037 :
1038 : {
1039 35 : nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "");
1040 35 : nWritingBytes +=
1041 35 : ISIS2Dataset::WriteFormatting(fpLabel, "/* Qube structure */");
1042 35 : nWritingBytes +=
1043 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "OBJECT", "QUBE");
1044 35 : iLevel++;
1045 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "AXES", "3");
1046 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "AXIS_NAME",
1047 : pszInterleaving);
1048 35 : nWritingBytes +=
1049 35 : ISIS2Dataset::WriteFormatting(fpLabel, "/* Core description */");
1050 :
1051 35 : CPLDebug("ISIS2", "%d,%d,%d", nXSize, nYSize, nBandsIn);
1052 :
1053 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1054 : fpLabel, iLevel, "CORE_ITEMS",
1055 70 : CPLString().Printf("(%d,%d,%d)", nXSize, nYSize, nBandsIn));
1056 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_NAME",
1057 : "\"RAW DATA NUMBER\"");
1058 35 : nWritingBytes +=
1059 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_UNIT", "\"N/A\"");
1060 : // TODO change for eType
1061 :
1062 35 : if (eType == GDT_Byte)
1063 : {
1064 22 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1065 : fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER");
1066 22 : nWritingBytes +=
1067 22 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "1");
1068 : }
1069 13 : else if (eType == GDT_UInt16)
1070 : {
1071 3 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1072 : fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER");
1073 3 : nWritingBytes +=
1074 3 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "2");
1075 : }
1076 10 : else if (eType == GDT_Int16)
1077 : {
1078 3 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1079 : fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_INTEGER");
1080 3 : nWritingBytes +=
1081 3 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "2");
1082 : }
1083 7 : else if (eType == GDT_Float32)
1084 : {
1085 4 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1086 : fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL");
1087 4 : nWritingBytes +=
1088 4 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "4");
1089 : }
1090 3 : else if (eType == GDT_Float64)
1091 : {
1092 3 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1093 : fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL");
1094 3 : nWritingBytes +=
1095 3 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "8");
1096 : }
1097 :
1098 : // TODO add core null value
1099 :
1100 35 : nWritingBytes +=
1101 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_BASE", "0.0");
1102 35 : nWritingBytes +=
1103 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_MULTIPLIER", "1.0");
1104 35 : nWritingBytes +=
1105 35 : ISIS2Dataset::WriteFormatting(fpLabel, "/* Suffix description */");
1106 35 : nWritingBytes +=
1107 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "SUFFIX_BYTES", "4");
1108 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "SUFFIX_ITEMS",
1109 : "( 0, 0, 0)");
1110 35 : iLevel--;
1111 35 : nWritingBytes +=
1112 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "END_OBJECT", "QUBE");
1113 :
1114 35 : return TRUE;
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* WriteLabel() */
1119 : /* */
1120 : /* osRasterFile : name of raster file but if it is empty we */
1121 : /* have only one file with an attached label */
1122 : /* sObjectTag : QUBE, IMAGE or SPECTRAL_QUBE */
1123 : /* bRelaunch : flag to allow recursive call */
1124 : /************************************************************************/
1125 :
1126 38 : int ISIS2Dataset::WriteLabel(const std::string &osFilename,
1127 : const std::string &osRasterFile,
1128 : const std::string &sObjectTag, unsigned int nXSize,
1129 : unsigned int nYSize, unsigned int nBandsIn,
1130 : GDALDataType eType, GUIntBig iRecords,
1131 : const char *pszInterleaving,
1132 : GUIntBig &iLabelRecords, CPL_UNUSED bool bRelaunch)
1133 : {
1134 38 : CPLDebug("ISIS2", "Write Label filename = %s, rasterfile = %s",
1135 : osFilename.c_str(), osRasterFile.c_str());
1136 38 : bool bAttachedLabel = EQUAL(osRasterFile.c_str(), "");
1137 :
1138 38 : VSILFILE *fpLabel = VSIFOpenL(osFilename.c_str(), "w");
1139 :
1140 38 : if (fpLabel == nullptr)
1141 : {
1142 3 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
1143 3 : osFilename.c_str(), VSIStrerror(errno));
1144 3 : return FALSE;
1145 : }
1146 :
1147 35 : const unsigned int iLevel(0);
1148 35 : unsigned int nWritingBytes(0);
1149 :
1150 : /* write common header */
1151 35 : nWritingBytes +=
1152 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "PDS_VERSION_ID", "PDS3");
1153 35 : nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "");
1154 35 : nWritingBytes += ISIS2Dataset::WriteFormatting(
1155 : fpLabel, "/* File identification and structure */");
1156 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "RECORD_TYPE",
1157 : "FIXED_LENGTH");
1158 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1159 70 : fpLabel, iLevel, "RECORD_BYTES", CPLString().Printf("%d", RECORD_SIZE));
1160 35 : nWritingBytes +=
1161 35 : ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "FILE_RECORDS",
1162 70 : CPLString().Printf(CPL_FRMT_GUIB, iRecords));
1163 35 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1164 : fpLabel, iLevel, "LABEL_RECORDS",
1165 70 : CPLString().Printf(CPL_FRMT_GUIB, iLabelRecords));
1166 35 : if (!bAttachedLabel)
1167 : {
1168 1 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1169 : fpLabel, iLevel, "FILE_NAME", CPLGetFilename(osRasterFile.c_str()));
1170 : }
1171 35 : nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "");
1172 :
1173 35 : nWritingBytes += ISIS2Dataset::WriteFormatting(
1174 : fpLabel, "/* Pointers to Data Objects */");
1175 :
1176 35 : if (bAttachedLabel)
1177 : {
1178 34 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1179 68 : fpLabel, iLevel, CPLString().Printf("^%s", sObjectTag.c_str()),
1180 68 : CPLString().Printf(CPL_FRMT_GUIB, iLabelRecords + 1));
1181 : }
1182 : else
1183 : {
1184 1 : nWritingBytes += ISIS2Dataset::WriteKeyword(
1185 2 : fpLabel, iLevel, CPLString().Printf("^%s", sObjectTag.c_str()),
1186 2 : CPLString().Printf("(\"%s\",1)",
1187 1 : CPLGetFilename(osRasterFile.c_str())));
1188 : }
1189 :
1190 35 : if (EQUAL(sObjectTag.c_str(), "QUBE"))
1191 : {
1192 35 : ISIS2Dataset::WriteQUBE_Information(fpLabel, iLevel, nWritingBytes,
1193 : nXSize, nYSize, nBandsIn, eType,
1194 : pszInterleaving);
1195 : }
1196 :
1197 35 : nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "END");
1198 :
1199 : // check if file record is correct
1200 35 : const unsigned int q = nWritingBytes / RECORD_SIZE;
1201 35 : if (q <= iLabelRecords)
1202 : {
1203 : // correct we add space after the label end for complete from
1204 : // iLabelRecords
1205 35 : unsigned int nSpaceBytesToWrite = static_cast<unsigned int>(
1206 35 : iLabelRecords * RECORD_SIZE - nWritingBytes);
1207 35 : VSIFPrintfL(fpLabel, "%*c", nSpaceBytesToWrite, ' ');
1208 : }
1209 : else
1210 : {
1211 0 : iLabelRecords = q + 1;
1212 0 : ISIS2Dataset::WriteLabel(osFilename, osRasterFile, sObjectTag, nXSize,
1213 : nYSize, nBandsIn, eType, iRecords,
1214 : pszInterleaving, iLabelRecords);
1215 : }
1216 35 : VSIFCloseL(fpLabel);
1217 :
1218 35 : return TRUE;
1219 : }
1220 :
1221 : /************************************************************************/
1222 : /* WriteKeyword() */
1223 : /************************************************************************/
1224 :
1225 666 : unsigned int ISIS2Dataset::WriteKeyword(VSILFILE *fpLabel, unsigned int iLevel,
1226 : CPLString key, CPLString value)
1227 :
1228 : {
1229 666 : CPLString tab = "";
1230 666 : iLevel *= 4; // each struct is indented by 4 spaces.
1231 :
1232 666 : return VSIFPrintfL(fpLabel, "%*s%s=%s\n", iLevel, tab.c_str(), key.c_str(),
1233 1332 : value.c_str());
1234 : }
1235 :
1236 : /************************************************************************/
1237 : /* WriteFormatting() */
1238 : /************************************************************************/
1239 :
1240 315 : unsigned int ISIS2Dataset::WriteFormatting(VSILFILE *fpLabel, CPLString data)
1241 :
1242 : {
1243 315 : return VSIFPrintfL(fpLabel, "%s\n", data.c_str());
1244 : }
1245 :
1246 : /************************************************************************/
1247 : /* GDALRegister_ISIS2() */
1248 : /************************************************************************/
1249 :
1250 1595 : void GDALRegister_ISIS2()
1251 :
1252 : {
1253 1595 : if (GDALGetDriverByName(ISIS2_DRIVER_NAME) != nullptr)
1254 302 : return;
1255 :
1256 1293 : GDALDriver *poDriver = new GDALDriver();
1257 1293 : ISIS2DriverSetCommonMetadata(poDriver);
1258 :
1259 1293 : poDriver->pfnOpen = ISIS2Dataset::Open;
1260 1293 : poDriver->pfnCreate = ISIS2Dataset::Create;
1261 :
1262 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1263 : }
|