Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISCE Raster Reader
4 : * Purpose: Implementation of the ISCE raster reader
5 : * Author: Matthieu Volat (ISTerre), matthieu.volat@ujf-grenoble.fr
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2015, Matthieu Volat <matthieu.volat@ujf-grenoble.fr>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdal_frmts.h"
14 : #include "ogr_spatialref.h"
15 : #include "rawdataset.h"
16 :
17 : #include <algorithm>
18 :
19 : static const char *const apszISCE2GDALDatatypes[] = {
20 : "BYTE:Byte", "CHAR:Byte", "SHORT:Int16", "INT:Int32",
21 : "LONG:Int64", "FLOAT:Float32", "DOUBLE:Float64", "CBYTE:Unknown",
22 : "CCHAR:Unknown", "CSHORT:CInt16", "CINT:CInt32", "CLONG:CInt64",
23 : "CFLOAT:CFloat32", "CDOUBLE:CFloat64", nullptr};
24 :
25 : static const char *const apszGDAL2ISCEDatatypes[] = {
26 : "Byte:BYTE", "Int16:SHORT", "Int32:INT", "Int64:LONG",
27 : "Float32:FLOAT", "Float64:DOUBLE", "CInt16:CSHORT", "CInt32:CINT",
28 : "CInt64:CLONG", "CFloat32:CFLOAT", "CFloat64:CDOUBLE", nullptr};
29 :
30 : enum Scheme
31 : {
32 : BIL = 0,
33 : BIP = 1,
34 : BSQ = 2
35 : };
36 :
37 : static const char *const apszSchemeNames[] = {"BIL", "BIP", "BSQ", nullptr};
38 :
39 : /************************************************************************/
40 : /* ==================================================================== */
41 : /* ISCEDataset */
42 : /* ==================================================================== */
43 : /************************************************************************/
44 :
45 : class ISCERasterBand;
46 :
47 : class ISCEDataset final : public RawDataset
48 : {
49 : friend class ISCERasterBand;
50 :
51 : VSILFILE *fpImage;
52 :
53 : char *pszXMLFilename;
54 :
55 : enum Scheme eScheme;
56 :
57 : CPL_DISALLOW_COPY_ASSIGN(ISCEDataset)
58 :
59 : CPLErr Close() override;
60 :
61 : public:
62 : ISCEDataset();
63 : ~ISCEDataset() override;
64 :
65 : CPLErr FlushCache(bool bAtClosing) override;
66 : char **GetFileList() override;
67 :
68 : static int Identify(GDALOpenInfo *poOpenInfo);
69 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
70 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo, bool bFileSizeCheck);
71 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
72 : int nBandsIn, GDALDataType eType,
73 : char **papszOptions);
74 : };
75 :
76 : /************************************************************************/
77 : /* ==================================================================== */
78 : /* ISCERasterBand */
79 : /* ==================================================================== */
80 : /************************************************************************/
81 :
82 : class ISCERasterBand final : public RawRasterBand
83 : {
84 : CPL_DISALLOW_COPY_ASSIGN(ISCERasterBand)
85 :
86 : public:
87 : ISCERasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
88 : vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
89 : GDALDataType eDataType, int bNativeOrder);
90 : };
91 :
92 : /************************************************************************/
93 : /* getXMLFilename() */
94 : /************************************************************************/
95 :
96 30392 : static CPLString getXMLFilename(GDALOpenInfo *poOpenInfo)
97 : {
98 60775 : CPLString osXMLFilename;
99 :
100 30386 : if (poOpenInfo->fpL == nullptr)
101 28935 : return CPLString();
102 :
103 1451 : char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
104 1449 : if (papszSiblingFiles == nullptr)
105 : {
106 : osXMLFilename =
107 17 : CPLFormFilenameSafe(nullptr, poOpenInfo->pszFilename, "xml");
108 : VSIStatBufL psXMLStatBuf;
109 34 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
110 17 : if (VSIStatL(osXMLFilename, &psXMLStatBuf) != 0)
111 : {
112 17 : osXMLFilename = "";
113 : }
114 : }
115 : else
116 : {
117 : /* ------------------------------------------------------------ */
118 : /* We need to tear apart the filename to form a .xml */
119 : /* filename. */
120 : /* ------------------------------------------------------------ */
121 2864 : const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
122 2864 : const CPLString osName = CPLGetFilename(poOpenInfo->pszFilename);
123 :
124 : const int iFile =
125 1432 : CSLFindString(papszSiblingFiles,
126 2864 : CPLFormFilenameSafe(nullptr, osName, "xml").c_str());
127 1432 : if (iFile >= 0)
128 : {
129 : osXMLFilename =
130 200 : CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr);
131 : }
132 : }
133 :
134 1449 : return osXMLFilename;
135 : }
136 :
137 : /************************************************************************/
138 : /* ISCEDataset() */
139 : /************************************************************************/
140 :
141 70 : ISCEDataset::ISCEDataset()
142 70 : : fpImage(nullptr), pszXMLFilename(nullptr), eScheme(BIL)
143 : {
144 70 : }
145 :
146 : /************************************************************************/
147 : /* ~ISCEDataset() */
148 : /************************************************************************/
149 :
150 140 : ISCEDataset::~ISCEDataset()
151 :
152 : {
153 70 : ISCEDataset::Close();
154 140 : }
155 :
156 : /************************************************************************/
157 : /* Close() */
158 : /************************************************************************/
159 :
160 140 : CPLErr ISCEDataset::Close()
161 : {
162 140 : CPLErr eErr = CE_None;
163 140 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
164 : {
165 70 : if (ISCEDataset::FlushCache(true) != CE_None)
166 0 : eErr = CE_Failure;
167 :
168 70 : if (fpImage)
169 : {
170 70 : if (VSIFCloseL(fpImage) != 0)
171 : {
172 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
173 0 : eErr = CE_Failure;
174 : }
175 : }
176 70 : CPLFree(pszXMLFilename);
177 :
178 70 : if (GDALPamDataset::Close() != CE_None)
179 0 : eErr = CE_Failure;
180 : }
181 140 : return eErr;
182 : }
183 :
184 : /************************************************************************/
185 : /* FlushCache() */
186 : /************************************************************************/
187 :
188 70 : CPLErr ISCEDataset::FlushCache(bool bAtClosing)
189 : {
190 70 : CPLErr eErr = RawDataset::FlushCache(bAtClosing);
191 :
192 70 : GDALRasterBand *band = (GetRasterCount() > 0) ? GetRasterBand(1) : nullptr;
193 :
194 70 : if (eAccess == GA_ReadOnly || band == nullptr)
195 32 : return eErr;
196 :
197 : /* -------------------------------------------------------------------- */
198 : /* Recreate a XML doc with the dataset information. */
199 : /* -------------------------------------------------------------------- */
200 38 : char sBuf[64] = {'\0'};
201 38 : CPLXMLNode *psDocNode = CPLCreateXMLNode(nullptr, CXT_Element, "imageFile");
202 :
203 : CPLXMLNode *psTmpNode =
204 38 : CPLCreateXMLNode(psDocNode, CXT_Element, "property");
205 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "WIDTH");
206 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterXSize);
207 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
208 :
209 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
210 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "LENGTH");
211 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterYSize);
212 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
213 :
214 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
215 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "NUMBER_BANDS");
216 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nBands);
217 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
218 :
219 38 : const char *sType = GDALGetDataTypeName(band->GetRasterDataType());
220 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
221 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "DATA_TYPE");
222 38 : CPLCreateXMLElementAndValue(
223 : psTmpNode, "value",
224 : CSLFetchNameValue(const_cast<char **>(apszGDAL2ISCEDatatypes), sType));
225 :
226 38 : const char *pszScheme = apszSchemeNames[eScheme];
227 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
228 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "SCHEME");
229 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", pszScheme);
230 :
231 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
232 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "BYTE_ORDER");
233 : #ifdef CPL_LSB
234 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", "l");
235 : #else
236 : CPLCreateXMLElementAndValue(psTmpNode, "value", "b");
237 : #endif
238 :
239 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
240 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "ACCESS_MODE");
241 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", "read");
242 :
243 38 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
244 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "FILE_NAME");
245 38 : CPLCreateXMLElementAndValue(psTmpNode, "value",
246 76 : CPLGetBasenameSafe(pszXMLFilename).c_str());
247 :
248 : /* -------------------------------------------------------------------- */
249 : /* Then, add the ISCE domain metadata. */
250 : /* -------------------------------------------------------------------- */
251 38 : char **papszISCEMetadata = GetMetadata("ISCE");
252 38 : for (int i = 0; i < CSLCount(papszISCEMetadata); i++)
253 : {
254 : /* Get the tokens from the metadata item */
255 : char **papszTokens =
256 0 : CSLTokenizeString2(papszISCEMetadata[i], "=",
257 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
258 0 : if (CSLCount(papszTokens) != 2)
259 : {
260 0 : CPLDebug("ISCE",
261 : "Line of header file could not be split at = into two"
262 : " elements: %s",
263 0 : papszISCEMetadata[i]);
264 0 : CSLDestroy(papszTokens);
265 0 : continue;
266 : }
267 :
268 : /* Don't write it out if it is one of the bits of metadata that is
269 : * written out elsewhere in this routine */
270 0 : if (EQUAL(papszTokens[0], "WIDTH") || EQUAL(papszTokens[0], "LENGTH") ||
271 0 : EQUAL(papszTokens[0], "NUMBER_BANDS") ||
272 0 : EQUAL(papszTokens[0], "DATA_TYPE") ||
273 0 : EQUAL(papszTokens[0], "SCHEME") ||
274 0 : EQUAL(papszTokens[0], "BYTE_ORDER"))
275 : {
276 0 : CSLDestroy(papszTokens);
277 0 : continue;
278 : }
279 :
280 0 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
281 0 : CPLAddXMLAttributeAndValue(psTmpNode, "name", papszTokens[0]);
282 0 : CPLCreateXMLElementAndValue(psTmpNode, "value", papszTokens[1]);
283 :
284 0 : CSLDestroy(papszTokens);
285 : }
286 :
287 : /* -------------------------------------------------------------------- */
288 : /* Create the "Coordinate" component elements, possibly with */
289 : /* georeferencing. */
290 : /* -------------------------------------------------------------------- */
291 : CPLXMLNode *psCoordinate1Node, *psCoordinate2Node;
292 : double adfGeoTransform[6];
293 :
294 : /* Coordinate 1 */
295 38 : psCoordinate1Node = CPLCreateXMLNode(psDocNode, CXT_Element, "component");
296 38 : CPLAddXMLAttributeAndValue(psCoordinate1Node, "name", "Coordinate1");
297 38 : CPLCreateXMLElementAndValue(psCoordinate1Node, "factorymodule",
298 : "isceobj.Image");
299 38 : CPLCreateXMLElementAndValue(psCoordinate1Node, "factoryname",
300 : "createCoordinate");
301 38 : CPLCreateXMLElementAndValue(psCoordinate1Node, "doc",
302 : "First coordinate of a 2D image (width).");
303 : /* Property name */
304 38 : psTmpNode = CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
305 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "name");
306 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate_name");
307 : /* Property family */
308 38 : psTmpNode = CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
309 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "family");
310 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate");
311 : /* Property size */
312 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterXSize);
313 38 : psTmpNode = CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
314 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "size");
315 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
316 :
317 : /* Coordinate 2 */
318 38 : psCoordinate2Node = CPLCreateXMLNode(psDocNode, CXT_Element, "component");
319 38 : CPLAddXMLAttributeAndValue(psCoordinate2Node, "name", "Coordinate2");
320 38 : CPLCreateXMLElementAndValue(psCoordinate2Node, "factorymodule",
321 : "isceobj.Image");
322 38 : CPLCreateXMLElementAndValue(psCoordinate2Node, "factoryname",
323 : "createCoordinate");
324 : /* Property name */
325 38 : psTmpNode = CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
326 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "name");
327 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate_name");
328 : /* Property family */
329 38 : psTmpNode = CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
330 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "family");
331 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate");
332 : /* Property size */
333 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterYSize);
334 38 : psTmpNode = CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
335 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "size");
336 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
337 :
338 38 : if (GetGeoTransform(adfGeoTransform) == CE_None)
339 : {
340 38 : if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
341 : {
342 0 : CPLError(CE_Warning, CPLE_AppDefined,
343 : "ISCE format do not support geotransform with "
344 : "rotation, discarding info.");
345 : }
346 : else
347 : {
348 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%g", adfGeoTransform[0]);
349 : psTmpNode =
350 38 : CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
351 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "startingValue");
352 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
353 :
354 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%g", adfGeoTransform[1]);
355 : psTmpNode =
356 38 : CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
357 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "delta");
358 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
359 :
360 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%g", adfGeoTransform[3]);
361 : psTmpNode =
362 38 : CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
363 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "startingValue");
364 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
365 :
366 38 : CPLsnprintf(sBuf, sizeof(sBuf), "%g", adfGeoTransform[5]);
367 : psTmpNode =
368 38 : CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
369 38 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "delta");
370 38 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
371 : }
372 : }
373 :
374 : /* -------------------------------------------------------------------- */
375 : /* Write the XML file. */
376 : /* -------------------------------------------------------------------- */
377 38 : if (!CPLSerializeXMLTreeToFile(psDocNode, pszXMLFilename))
378 0 : eErr = CE_Failure;
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* Free the XML Doc. */
382 : /* -------------------------------------------------------------------- */
383 38 : CPLDestroyXMLNode(psDocNode);
384 :
385 38 : return eErr;
386 : }
387 :
388 : /************************************************************************/
389 : /* GetFileList() */
390 : /************************************************************************/
391 :
392 3 : char **ISCEDataset::GetFileList()
393 : {
394 : /* Main data file, etc. */
395 3 : char **papszFileList = RawDataset::GetFileList();
396 :
397 : /* XML file. */
398 3 : papszFileList = CSLAddString(papszFileList, pszXMLFilename);
399 :
400 3 : return papszFileList;
401 : }
402 :
403 : /************************************************************************/
404 : /* Identify() */
405 : /************************************************************************/
406 :
407 30283 : int ISCEDataset::Identify(GDALOpenInfo *poOpenInfo)
408 : {
409 : /* -------------------------------------------------------------------- */
410 : /* TODO: This function is unusable now: */
411 : /* * we can't just check for the presence of a XML file */
412 : /* * we cannot parse it to check basic tree (Identify() is */
413 : /* supposed to be faster than this */
414 : /* * we could read only a few bytes and strstr() for */
415 : /* "imageData", but what if a file is padded with comments */
416 : /* and/or whitespaces? it would still be legit, but the */
417 : /* driver would fail... */
418 : /* -------------------------------------------------------------------- */
419 : /* -------------------------------------------------------------------- */
420 : /* Check if there is a .xml file */
421 : /* -------------------------------------------------------------------- */
422 60560 : CPLString osXMLFilename = getXMLFilename(poOpenInfo);
423 30282 : if (osXMLFilename.empty())
424 : {
425 30175 : return false;
426 : }
427 :
428 102 : return true;
429 : }
430 :
431 : /************************************************************************/
432 : /* Open() */
433 : /************************************************************************/
434 :
435 30229 : GDALDataset *ISCEDataset::Open(GDALOpenInfo *poOpenInfo)
436 : {
437 30229 : return Open(poOpenInfo, true);
438 : }
439 :
440 30281 : GDALDataset *ISCEDataset::Open(GDALOpenInfo *poOpenInfo, bool bFileSizeCheck)
441 : {
442 : /* -------------------------------------------------------------------- */
443 : /* Confirm that the header is compatible with a ISCE dataset. */
444 : /* -------------------------------------------------------------------- */
445 30281 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
446 : {
447 30175 : return nullptr;
448 : }
449 :
450 : /* -------------------------------------------------------------------- */
451 : /* Open and parse the .xml file */
452 : /* -------------------------------------------------------------------- */
453 204 : const CPLString osXMLFilename = getXMLFilename(poOpenInfo);
454 100 : CPLXMLNode *psNode = CPLParseXMLFile(osXMLFilename);
455 100 : if (psNode == nullptr || CPLGetXMLNode(psNode, "=imageFile") == nullptr)
456 : {
457 10 : CPLDestroyXMLNode(psNode);
458 10 : return nullptr;
459 : }
460 90 : CPLXMLNode *psCur = CPLGetXMLNode(psNode, "=imageFile")->psChild;
461 180 : CPLStringList aosXmlProps;
462 783 : while (psCur != nullptr)
463 : {
464 693 : if (EQUAL(psCur->pszValue, "property"))
465 : {
466 : /* Top-level property */
467 629 : const char *pszName = CPLGetXMLValue(psCur, "name", nullptr);
468 629 : const char *pszValue = CPLGetXMLValue(psCur, "value", nullptr);
469 629 : if (pszName != nullptr && pszValue != nullptr)
470 : {
471 611 : aosXmlProps.SetNameValue(pszName, pszValue);
472 : }
473 : }
474 64 : else if (EQUAL(psCur->pszValue, "component"))
475 : {
476 : /* "components" elements in ISCE store set of properties. */
477 : /* For now, they are avoided as I am not sure the full */
478 : /* scope of these. An exception is made for the ones named */
479 : /* Coordinate1 and Coordinate2, because they may have the */
480 : /* georeferencing information. */
481 64 : const char *pszCurName = CPLGetXMLValue(psCur, "name", nullptr);
482 64 : if (pszCurName != nullptr && (EQUAL(pszCurName, "Coordinate1") ||
483 32 : EQUAL(pszCurName, "Coordinate2")))
484 : {
485 : /* We need two subproperties: startingValue and delta. */
486 : /* To simplify parsing code, we will store them in */
487 : /* aosXmlProps with the coordinate name prefixed to */
488 : /* the property name. */
489 64 : CPLXMLNode *psCur2 = psCur->psChild;
490 613 : while (psCur2 != nullptr)
491 : {
492 549 : if (!EQUAL(psCur2->pszValue, "property"))
493 : {
494 229 : psCur2 = psCur2->psNext;
495 229 : continue; /* Skip non property elements */
496 : }
497 :
498 : const char *pszCur2Name =
499 320 : CPLGetXMLValue(psCur2, "name", nullptr),
500 : *pszCur2Value =
501 320 : CPLGetXMLValue(psCur2, "value", nullptr);
502 :
503 320 : if (pszCur2Name == nullptr || pszCur2Value == nullptr)
504 : {
505 0 : psCur2 = psCur2->psNext;
506 0 : continue; /* Skip malformatted elements */
507 : }
508 :
509 320 : if (EQUAL(pszCur2Name, "startingValue") ||
510 256 : EQUAL(pszCur2Name, "delta"))
511 : {
512 : char szPropName[32];
513 128 : snprintf(szPropName, sizeof(szPropName), "%s%s",
514 : pszCurName, pszCur2Name);
515 :
516 128 : aosXmlProps.SetNameValue(szPropName, pszCur2Value);
517 : }
518 320 : psCur2 = psCur2->psNext;
519 : }
520 : }
521 : }
522 693 : psCur = psCur->psNext;
523 : }
524 :
525 90 : CPLDestroyXMLNode(psNode);
526 :
527 : /* -------------------------------------------------------------------- */
528 : /* Fetch required fields. */
529 : /* -------------------------------------------------------------------- */
530 90 : if (aosXmlProps.FetchNameValue("WIDTH") == nullptr ||
531 90 : aosXmlProps.FetchNameValue("LENGTH") == nullptr ||
532 90 : aosXmlProps.FetchNameValue("NUMBER_BANDS") == nullptr ||
533 252 : aosXmlProps.FetchNameValue("DATA_TYPE") == nullptr ||
534 72 : aosXmlProps.FetchNameValue("SCHEME") == nullptr)
535 : {
536 18 : return nullptr;
537 : }
538 72 : const int nWidth = atoi(aosXmlProps.FetchNameValue("WIDTH"));
539 72 : const int nHeight = atoi(aosXmlProps.FetchNameValue("LENGTH"));
540 72 : const int nBands = atoi(aosXmlProps.FetchNameValue("NUMBER_BANDS"));
541 :
542 144 : if (!GDALCheckDatasetDimensions(nWidth, nHeight) ||
543 72 : !GDALCheckBandCount(nBands, FALSE))
544 : {
545 2 : return nullptr;
546 : }
547 :
548 : /* -------------------------------------------------------------------- */
549 : /* Update byte order info if image specify something. */
550 : /* -------------------------------------------------------------------- */
551 70 : bool bNativeOrder = true;
552 :
553 70 : const char *pszByteOrder = aosXmlProps.FetchNameValue("BYTE_ORDER");
554 70 : if (pszByteOrder != nullptr)
555 : {
556 : #ifdef CPL_LSB
557 70 : if (EQUAL(pszByteOrder, "b"))
558 : #else
559 : if (EQUAL(pszByteOrder, "l"))
560 : #endif
561 0 : bNativeOrder = false;
562 : }
563 :
564 : /* -------------------------------------------------------------------- */
565 : /* Create a corresponding GDALDataset. */
566 : /* -------------------------------------------------------------------- */
567 140 : auto poDS = std::make_unique<ISCEDataset>();
568 70 : poDS->nRasterXSize = nWidth;
569 70 : poDS->nRasterYSize = nHeight;
570 70 : poDS->eAccess = poOpenInfo->eAccess;
571 70 : poDS->pszXMLFilename = CPLStrdup(osXMLFilename.c_str());
572 70 : std::swap(poDS->fpImage, poOpenInfo->fpL);
573 :
574 : /* -------------------------------------------------------------------- */
575 : /* Create band information objects. */
576 : /* -------------------------------------------------------------------- */
577 70 : const char *pszDataType = CSLFetchNameValue(
578 : apszISCE2GDALDatatypes, aosXmlProps.FetchNameValue("DATA_TYPE"));
579 70 : if (pszDataType == nullptr)
580 : {
581 0 : return nullptr;
582 : }
583 70 : const GDALDataType eDataType = GDALGetDataTypeByName(pszDataType);
584 70 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
585 70 : if (nDTSize == 0)
586 : {
587 0 : return nullptr;
588 : }
589 70 : const char *pszScheme = aosXmlProps.FetchNameValue("SCHEME");
590 70 : int nPixelOffset = 0;
591 70 : int nLineOffset = 0;
592 70 : vsi_l_offset nBandOffset = 0;
593 70 : bool bIntOverflow = false;
594 70 : if (EQUAL(pszScheme, "BIL"))
595 : {
596 0 : poDS->eScheme = BIL;
597 0 : nPixelOffset = nDTSize;
598 0 : if (nWidth > INT_MAX / (nPixelOffset * nBands))
599 0 : bIntOverflow = true;
600 : else
601 : {
602 0 : nLineOffset = nPixelOffset * nWidth * nBands;
603 0 : nBandOffset = nDTSize * static_cast<vsi_l_offset>(nWidth);
604 : }
605 : }
606 70 : else if (EQUAL(pszScheme, "BIP"))
607 : {
608 70 : poDS->eScheme = BIP;
609 70 : nPixelOffset = nDTSize * nBands;
610 70 : if (nWidth > INT_MAX / nPixelOffset)
611 0 : bIntOverflow = true;
612 : else
613 : {
614 70 : nLineOffset = nPixelOffset * nWidth;
615 70 : if (nBands > 1 && nLineOffset < INT_MAX / nBands)
616 : {
617 : // GDAL 2.1.0 had a value of nLineOffset that was equal to the
618 : // theoretical nLineOffset multiplied by nBands...
619 30 : VSIFSeekL(poDS->fpImage, 0, SEEK_END);
620 30 : const GUIntBig nWrongFileSize =
621 30 : static_cast<GUIntBig>(nDTSize) * nWidth *
622 30 : (static_cast<GUIntBig>(nHeight - 1) * nBands * nBands +
623 : nBands);
624 30 : if (VSIFTellL(poDS->fpImage) == nWrongFileSize)
625 : {
626 0 : CPLError(
627 : CE_Warning, CPLE_AppDefined,
628 : "This file has been incorrectly generated by an older "
629 : "GDAL version whose line offset computation was "
630 : "erroneous. "
631 : "Taking that into account, but the file should be "
632 : "re-encoded ideally");
633 0 : nLineOffset = nLineOffset * nBands;
634 : }
635 : }
636 70 : nBandOffset = nDTSize;
637 : }
638 : }
639 0 : else if (EQUAL(pszScheme, "BSQ"))
640 : {
641 0 : poDS->eScheme = BSQ;
642 0 : nPixelOffset = nDTSize;
643 0 : if (nWidth > INT_MAX / nPixelOffset)
644 0 : bIntOverflow = true;
645 : else
646 : {
647 0 : nLineOffset = nPixelOffset * nWidth;
648 0 : nBandOffset = nLineOffset * static_cast<vsi_l_offset>(nHeight);
649 : }
650 : }
651 : else
652 : {
653 0 : CPLError(CE_Failure, CPLE_OpenFailed,
654 : "Unknown scheme \"%s\" within ISCE raster.", pszScheme);
655 0 : return nullptr;
656 : }
657 :
658 70 : if (bIntOverflow)
659 : {
660 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
661 0 : return nullptr;
662 : }
663 :
664 102 : if (bFileSizeCheck &&
665 32 : !RAWDatasetCheckMemoryUsage(poDS->nRasterXSize, poDS->nRasterYSize,
666 : nBands, nDTSize, nPixelOffset, nLineOffset,
667 32 : 0, nBandOffset, poDS->fpImage))
668 : {
669 0 : return nullptr;
670 : }
671 :
672 206 : for (int b = 0; b < nBands; b++)
673 : {
674 : auto poBand = std::make_unique<ISCERasterBand>(
675 136 : poDS.get(), b + 1, poDS->fpImage, nBandOffset * b, nPixelOffset,
676 136 : nLineOffset, eDataType, bNativeOrder);
677 136 : if (!poBand->IsValid())
678 0 : return nullptr;
679 136 : poDS->SetBand(b + 1, std::move(poBand));
680 : }
681 :
682 : /* -------------------------------------------------------------------- */
683 : /* Interpret georeferencing, if present. */
684 : /* -------------------------------------------------------------------- */
685 70 : if (aosXmlProps.FetchNameValue("Coordinate1startingValue") != nullptr &&
686 32 : aosXmlProps.FetchNameValue("Coordinate1delta") != nullptr &&
687 134 : aosXmlProps.FetchNameValue("Coordinate2startingValue") != nullptr &&
688 32 : aosXmlProps.FetchNameValue("Coordinate2delta") != nullptr)
689 : {
690 : double adfGeoTransform[6];
691 32 : adfGeoTransform[0] =
692 32 : CPLAtof(aosXmlProps.FetchNameValue("Coordinate1startingValue"));
693 32 : adfGeoTransform[1] =
694 32 : CPLAtof(aosXmlProps.FetchNameValue("Coordinate1delta"));
695 32 : adfGeoTransform[2] = 0.0;
696 32 : adfGeoTransform[3] =
697 32 : CPLAtof(aosXmlProps.FetchNameValue("Coordinate2startingValue"));
698 32 : adfGeoTransform[4] = 0.0;
699 32 : adfGeoTransform[5] =
700 32 : CPLAtof(aosXmlProps.FetchNameValue("Coordinate2delta"));
701 32 : poDS->SetGeoTransform(adfGeoTransform);
702 :
703 : /* ISCE format seems not to have a projection field, but uses */
704 : /* WGS84. */
705 32 : poDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
706 : }
707 :
708 : /* -------------------------------------------------------------------- */
709 : /* Set all the other header metadata into the ISCE domain */
710 : /* -------------------------------------------------------------------- */
711 707 : for (int i = 0; i < aosXmlProps.size(); i++)
712 : {
713 : const CPLStringList aosTokens(CSLTokenizeString2(
714 637 : aosXmlProps[i], "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
715 1274 : if (aosTokens.size() < 2 || EQUAL(aosTokens[0], "WIDTH") ||
716 567 : EQUAL(aosTokens[0], "LENGTH") ||
717 497 : EQUAL(aosTokens[0], "NUMBER_BANDS") ||
718 427 : EQUAL(aosTokens[0], "DATA_TYPE") || EQUAL(aosTokens[0], "SCHEME") ||
719 287 : EQUAL(aosTokens[0], "BYTE_ORDER") ||
720 217 : EQUAL(aosTokens[0], "Coordinate1startingValue") ||
721 185 : EQUAL(aosTokens[0], "Coordinate1delta") ||
722 1395 : EQUAL(aosTokens[0], "Coordinate2startingValue") ||
723 121 : EQUAL(aosTokens[0], "Coordinate2delta"))
724 : {
725 548 : continue;
726 : }
727 89 : poDS->SetMetadataItem(aosTokens[0], aosTokens[1], "ISCE");
728 : }
729 :
730 : /* -------------------------------------------------------------------- */
731 : /* Initialize any PAM information. */
732 : /* -------------------------------------------------------------------- */
733 70 : poDS->SetDescription(poOpenInfo->pszFilename);
734 70 : poDS->TryLoadXML();
735 :
736 : /* -------------------------------------------------------------------- */
737 : /* Check for overviews. */
738 : /* -------------------------------------------------------------------- */
739 70 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
740 :
741 70 : return poDS.release();
742 : }
743 :
744 : /************************************************************************/
745 : /* Create() */
746 : /************************************************************************/
747 :
748 62 : GDALDataset *ISCEDataset::Create(const char *pszFilename, int nXSize,
749 : int nYSize, int nBandsIn, GDALDataType eType,
750 : char **papszOptions)
751 : {
752 62 : const char *sType = GDALGetDataTypeName(eType);
753 62 : const char *pszScheme = CSLFetchNameValueDef(papszOptions, "SCHEME", "BIP");
754 :
755 : /* -------------------------------------------------------------------- */
756 : /* Try to create the file. */
757 : /* -------------------------------------------------------------------- */
758 62 : VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
759 62 : if (fp == nullptr)
760 : {
761 3 : CPLError(CE_Failure, CPLE_OpenFailed,
762 : "Attempt to create file `%s' failed.", pszFilename);
763 3 : return nullptr;
764 : }
765 :
766 : /* -------------------------------------------------------------------- */
767 : /* Just write out a couple of bytes to establish the binary */
768 : /* file, and then close it. */
769 : /* -------------------------------------------------------------------- */
770 59 : CPL_IGNORE_RET_VAL(VSIFWriteL("\0\0", 2, 1, fp));
771 59 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
772 :
773 : /* -------------------------------------------------------------------- */
774 : /* Create a minimal XML document. */
775 : /* -------------------------------------------------------------------- */
776 59 : CPLXMLNode *psDocNode = CPLCreateXMLNode(nullptr, CXT_Element, "imageFile");
777 :
778 : CPLXMLNode *psTmpNode =
779 59 : CPLCreateXMLNode(psDocNode, CXT_Element, "property");
780 59 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "WIDTH");
781 59 : char sBuf[64] = {'\0'};
782 59 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nXSize);
783 59 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
784 :
785 59 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
786 59 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "LENGTH");
787 59 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nYSize);
788 59 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
789 :
790 59 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
791 59 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "NUMBER_BANDS");
792 59 : CPLsnprintf(sBuf, sizeof(sBuf), "%d", nBandsIn);
793 59 : CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
794 :
795 59 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
796 59 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "DATA_TYPE");
797 59 : CPLCreateXMLElementAndValue(
798 : psTmpNode, "value",
799 : CSLFetchNameValue(const_cast<char **>(apszGDAL2ISCEDatatypes), sType));
800 :
801 59 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
802 59 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "SCHEME");
803 59 : CPLCreateXMLElementAndValue(psTmpNode, "value", pszScheme);
804 :
805 59 : psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
806 59 : CPLAddXMLAttributeAndValue(psTmpNode, "name", "BYTE_ORDER");
807 : #ifdef CPL_LSB
808 59 : CPLCreateXMLElementAndValue(psTmpNode, "value", "l");
809 : #else
810 : CPLCreateXMLElementAndValue(psTmpNode, "value", "b");
811 : #endif
812 :
813 : /* -------------------------------------------------------------------- */
814 : /* Write the XML file. */
815 : /* -------------------------------------------------------------------- */
816 : const std::string osXMLFilename =
817 118 : CPLFormFilenameSafe(nullptr, pszFilename, "xml");
818 59 : CPLSerializeXMLTreeToFile(psDocNode, osXMLFilename.c_str());
819 :
820 : /* -------------------------------------------------------------------- */
821 : /* Free the XML Doc. */
822 : /* -------------------------------------------------------------------- */
823 59 : CPLDestroyXMLNode(psDocNode);
824 :
825 118 : GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
826 59 : return Open(&oOpenInfo, false);
827 : }
828 :
829 : /************************************************************************/
830 : /* ISCERasterBand() */
831 : /************************************************************************/
832 :
833 136 : ISCERasterBand::ISCERasterBand(GDALDataset *poDSIn, int nBandIn,
834 : VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
835 : int nPixelOffsetIn, int nLineOffsetIn,
836 136 : GDALDataType eDataTypeIn, int bNativeOrderIn)
837 : : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
838 : nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
839 136 : RawRasterBand::OwnFP::NO)
840 : {
841 136 : }
842 :
843 : /************************************************************************/
844 : /* GDALRegister_ISCE() */
845 : /************************************************************************/
846 :
847 1889 : void GDALRegister_ISCE()
848 : {
849 1889 : if (GDALGetDriverByName("ISCE") != nullptr)
850 282 : return;
851 :
852 1607 : GDALDriver *poDriver = new GDALDriver();
853 :
854 1607 : poDriver->SetDescription("ISCE");
855 1607 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ISCE raster");
856 1607 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isce.html");
857 1607 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
858 : "Byte Int16 Int32 Int64 Float32"
859 : " Float64 CInt16 CInt64 CFloat32 "
860 1607 : " CFloat64");
861 1607 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
862 : "<CreationOptionList>"
863 : " <Option name='SCHEME' type='string-select'>"
864 : " <Value>BIP</Value>"
865 : " <Value>BIL</Value>"
866 : " <Value>BSQ</Value>"
867 : " </Option>"
868 1607 : "</CreationOptionList>");
869 1607 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
870 1607 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
871 :
872 1607 : poDriver->pfnOpen = ISCEDataset::Open;
873 1607 : poDriver->pfnCreate = ISCEDataset::Create;
874 :
875 1607 : GetGDALDriverManager()->RegisterDriver(poDriver);
876 : }
|