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