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