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