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