Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ROI_PAC Raster Reader
4 : * Purpose: Implementation of the ROI_PAC raster reader
5 : * Author: Matthieu Volat (ISTerre), matthieu.volat@ujf-grenoble.fr
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2014, 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 "gdal_priv.h"
16 : #include "rawdataset.h"
17 :
18 : #include <algorithm>
19 :
20 : /************************************************************************/
21 : /* ==================================================================== */
22 : /* ROIPACDataset */
23 : /* ==================================================================== */
24 : /************************************************************************/
25 :
26 : class ROIPACDataset final : public RawDataset
27 : {
28 : VSILFILE *fpImage;
29 : VSILFILE *fpRsc;
30 :
31 : char *pszRscFilename;
32 :
33 : GDALGeoTransform m_gt{};
34 : bool bValidGeoTransform;
35 :
36 : OGRSpatialReference m_oSRS{};
37 :
38 : CPL_DISALLOW_COPY_ASSIGN(ROIPACDataset)
39 :
40 : CPLErr Close() override;
41 :
42 : public:
43 : ROIPACDataset();
44 : ~ROIPACDataset() override;
45 :
46 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
47 : static int Identify(GDALOpenInfo *poOpenInfo);
48 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
49 : int nBandsIn, GDALDataType eType,
50 : char **papszOptions);
51 :
52 : CPLErr FlushCache(bool bAtClosing) override;
53 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
54 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
55 :
56 3 : const OGRSpatialReference *GetSpatialRef() const override
57 : {
58 3 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
59 : }
60 :
61 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
62 :
63 : char **GetFileList() override;
64 : };
65 :
66 : /************************************************************************/
67 : /* getRscFilename() */
68 : /************************************************************************/
69 :
70 176 : static CPLString getRscFilename(GDALOpenInfo *poOpenInfo)
71 : {
72 176 : char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
73 176 : if (papszSiblingFiles == nullptr)
74 : {
75 : CPLString osRscFilename =
76 190 : CPLFormFilenameSafe(nullptr, poOpenInfo->pszFilename, "rsc");
77 : VSIStatBufL psRscStatBuf;
78 95 : if (VSIStatL(osRscFilename, &psRscStatBuf) != 0)
79 : {
80 95 : return "";
81 : }
82 0 : return osRscFilename;
83 : }
84 :
85 : /* ------------------------------------------------------------ */
86 : /* We need to tear apart the filename to form a .rsc */
87 : /* filename. */
88 : /* ------------------------------------------------------------ */
89 162 : const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
90 162 : const CPLString osName = CPLGetFilename(poOpenInfo->pszFilename);
91 :
92 81 : int iFile = CSLFindString(
93 162 : papszSiblingFiles, CPLFormFilenameSafe(nullptr, osName, "rsc").c_str());
94 81 : if (iFile >= 0)
95 : {
96 90 : return CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr);
97 : }
98 :
99 36 : return "";
100 : }
101 :
102 : /************************************************************************/
103 : /* ROIPACDataset() */
104 : /************************************************************************/
105 :
106 15 : ROIPACDataset::ROIPACDataset()
107 : : fpImage(nullptr), fpRsc(nullptr), pszRscFilename(nullptr),
108 15 : bValidGeoTransform(false)
109 : {
110 15 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
111 15 : }
112 :
113 : /************************************************************************/
114 : /* ~ROIPACDataset() */
115 : /************************************************************************/
116 :
117 30 : ROIPACDataset::~ROIPACDataset()
118 : {
119 15 : ROIPACDataset::Close();
120 30 : }
121 :
122 : /************************************************************************/
123 : /* Close() */
124 : /************************************************************************/
125 :
126 30 : CPLErr ROIPACDataset::Close()
127 : {
128 30 : CPLErr eErr = CE_None;
129 30 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
130 : {
131 15 : if (ROIPACDataset::FlushCache(true) != CE_None)
132 0 : eErr = CE_Failure;
133 :
134 15 : if (fpRsc != nullptr && VSIFCloseL(fpRsc) != 0)
135 : {
136 0 : eErr = CE_Failure;
137 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
138 : }
139 15 : if (fpImage != nullptr && VSIFCloseL(fpImage) != 0)
140 : {
141 0 : eErr = CE_Failure;
142 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
143 : }
144 15 : CPLFree(pszRscFilename);
145 :
146 15 : if (GDALPamDataset::Close() != CE_None)
147 0 : eErr = CE_Failure;
148 : }
149 30 : return eErr;
150 : }
151 :
152 : /************************************************************************/
153 : /* Open() */
154 : /************************************************************************/
155 :
156 15 : GDALDataset *ROIPACDataset::Open(GDALOpenInfo *poOpenInfo)
157 : {
158 : /* -------------------------------------------------------------------- */
159 : /* Confirm that the header is compatible with a ROIPAC dataset. */
160 : /* -------------------------------------------------------------------- */
161 15 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
162 : {
163 0 : return nullptr;
164 : }
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Open the .rsc file */
168 : /* -------------------------------------------------------------------- */
169 30 : CPLString osRscFilename = getRscFilename(poOpenInfo);
170 15 : if (osRscFilename.empty())
171 : {
172 0 : return nullptr;
173 : }
174 15 : VSILFILE *fpRsc = nullptr;
175 15 : if (poOpenInfo->eAccess == GA_Update)
176 : {
177 3 : fpRsc = VSIFOpenL(osRscFilename, "r+");
178 : }
179 : else
180 : {
181 12 : fpRsc = VSIFOpenL(osRscFilename, "r");
182 : }
183 15 : if (fpRsc == nullptr)
184 : {
185 0 : return nullptr;
186 : }
187 :
188 : /* -------------------------------------------------------------------- */
189 : /* Load the .rsc information. */
190 : /* -------------------------------------------------------------------- */
191 30 : CPLStringList aosRSC;
192 : while (true)
193 : {
194 189 : const char *pszLine = CPLReadLineL(fpRsc);
195 189 : if (pszLine == nullptr)
196 : {
197 15 : break;
198 : }
199 :
200 : char **papszTokens =
201 174 : CSLTokenizeString2(pszLine, " \t",
202 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES |
203 : CSLT_PRESERVEQUOTES | CSLT_PRESERVEESCAPES);
204 174 : if (papszTokens == nullptr || papszTokens[0] == nullptr ||
205 174 : papszTokens[1] == nullptr)
206 : {
207 0 : CSLDestroy(papszTokens);
208 0 : break;
209 : }
210 174 : aosRSC.SetNameValue(papszTokens[0], papszTokens[1]);
211 :
212 174 : CSLDestroy(papszTokens);
213 174 : }
214 :
215 : /* -------------------------------------------------------------------- */
216 : /* Fetch required fields. */
217 : /* -------------------------------------------------------------------- */
218 30 : if (aosRSC.FetchNameValue("WIDTH") == nullptr ||
219 15 : aosRSC.FetchNameValue("FILE_LENGTH") == nullptr)
220 : {
221 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpRsc));
222 0 : return nullptr;
223 : }
224 15 : const int nWidth = atoi(aosRSC.FetchNameValue("WIDTH"));
225 15 : const int nFileLength = atoi(aosRSC.FetchNameValue("FILE_LENGTH"));
226 :
227 15 : if (!GDALCheckDatasetDimensions(nWidth, nFileLength))
228 : {
229 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpRsc));
230 0 : return nullptr;
231 : }
232 :
233 : /* -------------------------------------------------------------------- */
234 : /* Create a corresponding GDALDataset. */
235 : /* -------------------------------------------------------------------- */
236 30 : auto poDS = std::make_unique<ROIPACDataset>();
237 15 : poDS->nRasterXSize = nWidth;
238 15 : poDS->nRasterYSize = nFileLength;
239 15 : poDS->eAccess = poOpenInfo->eAccess;
240 15 : poDS->fpRsc = fpRsc;
241 15 : poDS->pszRscFilename = CPLStrdup(osRscFilename.c_str());
242 15 : std::swap(poDS->fpImage, poOpenInfo->fpL);
243 :
244 : /* -------------------------------------------------------------------- */
245 : /* Create band information objects. */
246 : /* -------------------------------------------------------------------- */
247 15 : GDALDataType eDataType = GDT_Unknown;
248 15 : int nBands = 0;
249 :
250 : enum Interleave
251 : {
252 : UNKNOWN,
253 : LINE,
254 : PIXEL
255 15 : } eInterleave = UNKNOWN;
256 :
257 15 : const char *pszExtension = poOpenInfo->osExtension.c_str();
258 15 : if (strcmp(pszExtension, "raw") == 0)
259 : {
260 : /* ------------------------------------------------------------ */
261 : /* TODO: ROI_PAC raw images are what would be GDT_CInt8 typed, */
262 : /* but since that type do not exist, we will have to implement */
263 : /* a specific case in the RasterBand to convert it to */
264 : /* GDT_CInt16 for example */
265 : /* ------------------------------------------------------------ */
266 : #if 0
267 : eDataType = GDT_CInt8;
268 : nBands = 1;
269 : eInterleave = PIXEL;
270 : #else
271 0 : CPLError(CE_Failure, CPLE_NotSupported,
272 : "Reading ROI_PAC raw files is not supported yet.");
273 0 : return nullptr;
274 : #endif
275 : }
276 15 : else if (strcmp(pszExtension, "int") == 0 ||
277 15 : strcmp(pszExtension, "slc") == 0)
278 : {
279 0 : eDataType = GDT_CFloat32;
280 0 : nBands = 1;
281 0 : eInterleave = PIXEL;
282 : }
283 15 : else if (strcmp(pszExtension, "amp") == 0)
284 : {
285 0 : eDataType = GDT_Float32;
286 0 : nBands = 2;
287 0 : eInterleave = PIXEL;
288 : }
289 15 : else if (strcmp(pszExtension, "cor") == 0 ||
290 15 : strcmp(pszExtension, "hgt") == 0 ||
291 15 : strcmp(pszExtension, "unw") == 0 ||
292 15 : strcmp(pszExtension, "msk") == 0 ||
293 15 : strcmp(pszExtension, "trans") == 0)
294 : {
295 0 : eDataType = GDT_Float32;
296 0 : nBands = 2;
297 0 : eInterleave = LINE;
298 : }
299 15 : else if (strcmp(pszExtension, "dem") == 0)
300 : {
301 12 : eDataType = GDT_Int16;
302 12 : nBands = 1;
303 12 : eInterleave = PIXEL;
304 : }
305 3 : else if (strcmp(pszExtension, "flg") == 0)
306 : {
307 3 : eDataType = GDT_Byte;
308 3 : nBands = 1;
309 3 : eInterleave = PIXEL;
310 : }
311 : else
312 : { /* Eeek */
313 0 : return nullptr;
314 : }
315 :
316 15 : int nPixelOffset = 0;
317 15 : int nLineOffset = 0;
318 15 : vsi_l_offset nBandOffset = 0;
319 15 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
320 15 : bool bIntOverflow = false;
321 15 : if (eInterleave == LINE)
322 : {
323 0 : nPixelOffset = nDTSize;
324 0 : if (nWidth > INT_MAX / (nPixelOffset * nBands))
325 0 : bIntOverflow = true;
326 : else
327 : {
328 0 : nLineOffset = nPixelOffset * nWidth * nBands;
329 0 : nBandOffset = static_cast<vsi_l_offset>(nDTSize) * nWidth;
330 : }
331 : }
332 : else
333 : { /* PIXEL */
334 15 : nPixelOffset = nDTSize * nBands;
335 15 : if (nWidth > INT_MAX / nPixelOffset)
336 0 : bIntOverflow = true;
337 : else
338 : {
339 15 : nLineOffset = nPixelOffset * nWidth;
340 15 : nBandOffset = nDTSize;
341 :
342 15 : if (nBands > 1)
343 : {
344 : // GDAL 2.0.[0-3] and 2.1.0 had a value of nLineOffset that was
345 : // equal to the theoretical nLineOffset multiplied by nBands.
346 0 : VSIFSeekL(poDS->fpImage, 0, SEEK_END);
347 0 : const GUIntBig nWrongFileSize =
348 0 : static_cast<GUIntBig>(nDTSize) * nWidth *
349 0 : (static_cast<GUIntBig>(nFileLength - 1) * nBands * nBands +
350 : nBands);
351 0 : if (VSIFTellL(poDS->fpImage) == nWrongFileSize)
352 : {
353 0 : CPLError(
354 : CE_Warning, CPLE_AppDefined,
355 : "This file has been incorrectly generated by an older "
356 : "GDAL version whose line offset computation was "
357 : "erroneous. Taking that into account, "
358 : "but the file should be re-encoded ideally.");
359 0 : nLineOffset = nLineOffset * nBands;
360 : }
361 : }
362 : }
363 : }
364 :
365 15 : if (bIntOverflow)
366 : {
367 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
368 0 : return nullptr;
369 : }
370 :
371 30 : for (int b = 0; b < nBands; b++)
372 : {
373 : auto poBand = RawRasterBand::Create(
374 30 : poDS.get(), b + 1, poDS->fpImage, nBandOffset * b, nPixelOffset,
375 : nLineOffset, eDataType,
376 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN,
377 15 : RawRasterBand::OwnFP::NO);
378 15 : if (!poBand)
379 0 : return nullptr;
380 15 : poDS->SetBand(b + 1, std::move(poBand));
381 : }
382 :
383 : /* -------------------------------------------------------------------- */
384 : /* Interpret georeferencing, if present. */
385 : /* -------------------------------------------------------------------- */
386 15 : if (aosRSC.FetchNameValue("X_FIRST") != nullptr &&
387 12 : aosRSC.FetchNameValue("X_STEP") != nullptr &&
388 39 : aosRSC.FetchNameValue("Y_FIRST") != nullptr &&
389 12 : aosRSC.FetchNameValue("Y_STEP") != nullptr)
390 : {
391 12 : poDS->m_gt[0] = CPLAtof(aosRSC.FetchNameValue("X_FIRST"));
392 12 : poDS->m_gt[1] = CPLAtof(aosRSC.FetchNameValue("X_STEP"));
393 12 : poDS->m_gt[2] = 0.0;
394 12 : poDS->m_gt[3] = CPLAtof(aosRSC.FetchNameValue("Y_FIRST"));
395 12 : poDS->m_gt[4] = 0.0;
396 12 : poDS->m_gt[5] = CPLAtof(aosRSC.FetchNameValue("Y_STEP"));
397 12 : poDS->bValidGeoTransform = true;
398 : }
399 15 : if (aosRSC.FetchNameValue("PROJECTION") != nullptr)
400 : {
401 : /* ------------------------------------------------------------ */
402 : /* In ROI_PAC, images are georeferenced either with lat/long or */
403 : /* UTM projection. However, using UTM projection is dangerous */
404 : /* because there is no North/South field, or use of latitude */
405 : /* bands! */
406 : /* ------------------------------------------------------------ */
407 24 : OGRSpatialReference oSRS;
408 12 : if (strcmp(aosRSC.FetchNameValue("PROJECTION"), "LL") == 0)
409 : {
410 10 : if (aosRSC.FetchNameValue("DATUM") != nullptr)
411 : {
412 10 : oSRS.SetWellKnownGeogCS(aosRSC.FetchNameValue("DATUM"));
413 : }
414 : else
415 : {
416 0 : oSRS.SetWellKnownGeogCS("WGS84");
417 : }
418 : }
419 2 : else if (STARTS_WITH(aosRSC.FetchNameValue("PROJECTION"), "UTM"))
420 : {
421 2 : const char *pszZone = aosRSC.FetchNameValue("PROJECTION") + 3;
422 2 : oSRS.SetUTM(atoi(pszZone), TRUE); /* FIXME: north/south? */
423 2 : if (aosRSC.FetchNameValue("DATUM") != nullptr)
424 : {
425 2 : oSRS.SetWellKnownGeogCS(aosRSC.FetchNameValue("DATUM"));
426 : }
427 : else
428 : {
429 0 : oSRS.SetWellKnownGeogCS("NAD27");
430 : }
431 : }
432 12 : poDS->m_oSRS = std::move(oSRS);
433 12 : poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
434 : }
435 15 : if (aosRSC.FetchNameValue("Z_OFFSET") != nullptr)
436 : {
437 : const double dfOffset =
438 12 : strtod(aosRSC.FetchNameValue("Z_OFFSET"), nullptr);
439 24 : for (int b = 1; b <= nBands; b++)
440 : {
441 12 : GDALRasterBand *poBand = poDS->GetRasterBand(b);
442 12 : poBand->SetOffset(dfOffset);
443 : }
444 : }
445 15 : if (aosRSC.FetchNameValue("Z_SCALE") != nullptr)
446 : {
447 : const double dfScale =
448 12 : strtod(aosRSC.FetchNameValue("Z_SCALE"), nullptr);
449 24 : for (int b = 1; b <= nBands; b++)
450 : {
451 12 : GDALRasterBand *poBand = poDS->GetRasterBand(b);
452 12 : poBand->SetScale(dfScale);
453 : }
454 : }
455 :
456 : /* -------------------------------------------------------------------- */
457 : /* Set all the other header metadata into the ROI_PAC domain */
458 : /* -------------------------------------------------------------------- */
459 189 : for (int i = 0; i < aosRSC.size(); ++i)
460 : {
461 348 : char **papszTokens = CSLTokenizeString2(
462 174 : aosRSC[i], "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
463 348 : if (CSLCount(papszTokens) < 2 || strcmp(papszTokens[0], "WIDTH") == 0 ||
464 159 : strcmp(papszTokens[0], "FILE_LENGTH") == 0 ||
465 144 : strcmp(papszTokens[0], "X_FIRST") == 0 ||
466 132 : strcmp(papszTokens[0], "X_STEP") == 0 ||
467 120 : strcmp(papszTokens[0], "Y_FIRST") == 0 ||
468 108 : strcmp(papszTokens[0], "Y_STEP") == 0 ||
469 96 : strcmp(papszTokens[0], "PROJECTION") == 0 ||
470 84 : strcmp(papszTokens[0], "DATUM") == 0 ||
471 408 : strcmp(papszTokens[0], "Z_OFFSET") == 0 ||
472 60 : strcmp(papszTokens[0], "Z_SCALE") == 0)
473 : {
474 126 : CSLDestroy(papszTokens);
475 126 : continue;
476 : }
477 48 : poDS->SetMetadataItem(papszTokens[0], papszTokens[1], "ROI_PAC");
478 48 : CSLDestroy(papszTokens);
479 : }
480 : /* -------------------------------------------------------------------- */
481 : /* Initialize any PAM information. */
482 : /* -------------------------------------------------------------------- */
483 15 : poDS->SetDescription(poOpenInfo->pszFilename);
484 15 : poDS->TryLoadXML();
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Check for overviews. */
488 : /* -------------------------------------------------------------------- */
489 15 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
490 :
491 15 : return poDS.release();
492 : }
493 :
494 : /************************************************************************/
495 : /* Identify() */
496 : /************************************************************************/
497 :
498 58997 : int ROIPACDataset::Identify(GDALOpenInfo *poOpenInfo)
499 : {
500 : /* -------------------------------------------------------------------- */
501 : /* Check if: */
502 : /* * 1. The data file extension is known */
503 : /* -------------------------------------------------------------------- */
504 58997 : const char *pszExtension = poOpenInfo->osExtension.c_str();
505 58997 : if (strcmp(pszExtension, "raw") == 0)
506 : {
507 : /* Since gdal do not read natively CInt8, more work is needed
508 : * to read raw files */
509 25 : return false;
510 : }
511 58972 : const bool bExtensionIsValid =
512 58968 : strcmp(pszExtension, "int") == 0 || strcmp(pszExtension, "slc") == 0 ||
513 58950 : strcmp(pszExtension, "amp") == 0 || strcmp(pszExtension, "cor") == 0 ||
514 58950 : strcmp(pszExtension, "hgt") == 0 || strcmp(pszExtension, "unw") == 0 ||
515 58929 : strcmp(pszExtension, "msk") == 0 ||
516 58880 : strcmp(pszExtension, "trans") == 0 ||
517 117940 : strcmp(pszExtension, "dem") == 0 || strcmp(pszExtension, "flg") == 0;
518 58972 : if (!bExtensionIsValid)
519 : {
520 58811 : return false;
521 : }
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* * 2. there is a .rsc file */
525 : /* -------------------------------------------------------------------- */
526 322 : CPLString osRscFilename = getRscFilename(poOpenInfo);
527 161 : if (osRscFilename.empty())
528 : {
529 131 : return false;
530 : }
531 :
532 30 : return true;
533 : }
534 :
535 : /************************************************************************/
536 : /* Create() */
537 : /************************************************************************/
538 :
539 52 : GDALDataset *ROIPACDataset::Create(const char *pszFilename, int nXSize,
540 : int nYSize, int nBandsIn, GDALDataType eType,
541 : char ** /* papszOptions */)
542 : {
543 : /* -------------------------------------------------------------------- */
544 : /* Verify input options. */
545 : /* -------------------------------------------------------------------- */
546 104 : const std::string osExtension = CPLGetExtensionSafe(pszFilename);
547 52 : const char *pszExtension = osExtension.c_str();
548 52 : if (strcmp(pszExtension, "int") == 0 || strcmp(pszExtension, "slc") == 0)
549 : {
550 0 : if (nBandsIn != 1 || eType != GDT_CFloat32)
551 : {
552 0 : CPLError(CE_Failure, CPLE_AppDefined,
553 : "Attempt to create ROI_PAC %s dataset with an illegal "
554 : "number of bands (%d) and/or data type (%s).",
555 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
556 0 : return nullptr;
557 : }
558 : }
559 52 : else if (strcmp(pszExtension, "amp") == 0)
560 : {
561 0 : if (nBandsIn != 2 || eType != GDT_Float32)
562 : {
563 0 : CPLError(CE_Failure, CPLE_AppDefined,
564 : "Attempt to create ROI_PAC %s dataset with an illegal "
565 : "number of bands (%d) and/or data type (%s).",
566 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
567 0 : return nullptr;
568 : }
569 : }
570 52 : else if (strcmp(pszExtension, "cor") == 0 ||
571 52 : strcmp(pszExtension, "hgt") == 0 ||
572 52 : strcmp(pszExtension, "unw") == 0 ||
573 52 : strcmp(pszExtension, "msk") == 0 ||
574 52 : strcmp(pszExtension, "trans") == 0)
575 : {
576 0 : if (nBandsIn != 2 || eType != GDT_Float32)
577 : {
578 0 : CPLError(CE_Failure, CPLE_AppDefined,
579 : "Attempt to create ROI_PAC %s dataset with an illegal "
580 : "number of bands (%d) and/or data type (%s).",
581 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
582 0 : return nullptr;
583 : }
584 : }
585 52 : else if (strcmp(pszExtension, "dem") == 0)
586 : {
587 2 : if (nBandsIn != 1 || eType != GDT_Int16)
588 : {
589 0 : CPLError(CE_Failure, CPLE_AppDefined,
590 : "Attempt to create ROI_PAC %s dataset with an illegal "
591 : "number of bands (%d) and/or data type (%s).",
592 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
593 0 : return nullptr;
594 : }
595 : }
596 50 : else if (strcmp(pszExtension, "flg") == 0)
597 : {
598 1 : if (nBandsIn != 1 || eType != GDT_Byte)
599 : {
600 0 : CPLError(CE_Failure, CPLE_AppDefined,
601 : "Attempt to create ROI_PAC %s dataset with an illegal "
602 : "number of bands (%d) and/or data type (%s).",
603 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
604 0 : return nullptr;
605 : }
606 : }
607 : else
608 : { /* Eeek */
609 49 : CPLError(CE_Failure, CPLE_AppDefined,
610 : "Attempt to create ROI_PAC dataset with an unknown type (%s)",
611 : pszExtension);
612 49 : return nullptr;
613 : }
614 :
615 : /* -------------------------------------------------------------------- */
616 : /* Try to create the file. */
617 : /* -------------------------------------------------------------------- */
618 3 : VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
619 3 : if (fp == nullptr)
620 : {
621 0 : CPLError(CE_Failure, CPLE_OpenFailed,
622 : "Attempt to create file `%s' failed.", pszFilename);
623 0 : return nullptr;
624 : }
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* Just write out a couple of bytes to establish the binary */
628 : /* file, and then close it. */
629 : /* -------------------------------------------------------------------- */
630 3 : CPL_IGNORE_RET_VAL(VSIFWriteL("\0\0", 2, 1, fp));
631 3 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
632 :
633 : /* -------------------------------------------------------------------- */
634 : /* Open the RSC file. */
635 : /* -------------------------------------------------------------------- */
636 : const std::string osRSCFilename =
637 6 : CPLFormFilenameSafe(nullptr, pszFilename, "rsc");
638 3 : fp = VSIFOpenL(osRSCFilename.c_str(), "wt");
639 3 : if (fp == nullptr)
640 : {
641 0 : CPLError(CE_Failure, CPLE_OpenFailed,
642 : "Attempt to create file `%s' failed.", osRSCFilename.c_str());
643 0 : return nullptr;
644 : }
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Write out the header. */
648 : /* -------------------------------------------------------------------- */
649 3 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "%-40s %d\n", "WIDTH", nXSize));
650 3 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "%-40s %d\n", "FILE_LENGTH", nYSize));
651 3 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
652 :
653 3 : return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
654 : }
655 :
656 : /************************************************************************/
657 : /* FlushCache() */
658 : /************************************************************************/
659 :
660 15 : CPLErr ROIPACDataset::FlushCache(bool bAtClosing)
661 : {
662 15 : CPLErr eErr = RawDataset::FlushCache(bAtClosing);
663 :
664 15 : GDALRasterBand *band = (GetRasterCount() > 0) ? GetRasterBand(1) : nullptr;
665 :
666 15 : if (eAccess == GA_ReadOnly || band == nullptr)
667 12 : return eErr;
668 :
669 : // If opening an existing file in Update mode (i.e. "r+") we need to make
670 : // sure any existing content is cleared, otherwise the file may contain
671 : // trailing content from the previous write.
672 3 : bool bOK = VSIFTruncateL(fpRsc, 0) == 0;
673 :
674 3 : bOK &= VSIFSeekL(fpRsc, 0, SEEK_SET) == 0;
675 : /* -------------------------------------------------------------------- */
676 : /* Rewrite out the header. */
677 : /* -------------------------------------------------------------------- */
678 : /* -------------------------------------------------------------------- */
679 : /* Raster dimensions. */
680 : /* -------------------------------------------------------------------- */
681 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %d\n", "WIDTH", nRasterXSize) > 0;
682 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %d\n", "FILE_LENGTH", nRasterYSize) > 0;
683 :
684 : /* -------------------------------------------------------------------- */
685 : /* Georeferencing. */
686 : /* -------------------------------------------------------------------- */
687 3 : if (!m_oSRS.IsEmpty())
688 : {
689 3 : int bNorth = FALSE;
690 3 : int iUTMZone = m_oSRS.GetUTMZone(&bNorth);
691 3 : if (iUTMZone != 0)
692 : {
693 1 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s%d\n", "PROJECTION", "UTM",
694 1 : iUTMZone) > 0;
695 : }
696 2 : else if (m_oSRS.IsGeographic())
697 : {
698 2 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "PROJECTION", "LL") > 0;
699 : }
700 : else
701 : {
702 0 : CPLError(CE_Warning, CPLE_AppDefined,
703 : "ROI_PAC format only support Latitude/Longitude and "
704 : "UTM projections, discarding projection.");
705 : }
706 :
707 3 : if (m_oSRS.GetAttrValue("DATUM") != nullptr)
708 : {
709 3 : if (strcmp(m_oSRS.GetAttrValue("DATUM"), "WGS_1984") == 0)
710 : {
711 2 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "DATUM", "WGS84") > 0;
712 : }
713 : else
714 : {
715 1 : CPLError(CE_Warning, CPLE_AppDefined,
716 : "Datum \"%s\" probably not supported in the "
717 : "ROI_PAC format, saving it anyway",
718 : m_oSRS.GetAttrValue("DATUM"));
719 1 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "DATUM",
720 1 : m_oSRS.GetAttrValue("DATUM")) > 0;
721 : }
722 : }
723 3 : if (m_oSRS.GetAttrValue("UNIT") != nullptr)
724 : {
725 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "X_UNIT",
726 3 : m_oSRS.GetAttrValue("UNIT")) > 0;
727 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "Y_UNIT",
728 3 : m_oSRS.GetAttrValue("UNIT")) > 0;
729 : }
730 : }
731 3 : if (bValidGeoTransform)
732 : {
733 3 : if (m_gt[2] != 0 || m_gt[4] != 0)
734 : {
735 0 : CPLError(CE_Warning, CPLE_AppDefined,
736 : "ROI_PAC format do not support geotransform with "
737 : "rotation, discarding info.");
738 : }
739 : else
740 : {
741 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "X_FIRST", m_gt[0]) > 0;
742 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "X_STEP", m_gt[1]) > 0;
743 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Y_FIRST", m_gt[3]) > 0;
744 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Y_STEP", m_gt[5]) > 0;
745 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Z_OFFSET",
746 3 : band->GetOffset(nullptr)) > 0;
747 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Z_SCALE",
748 6 : band->GetScale(nullptr)) > 0;
749 : }
750 : }
751 :
752 : /* -------------------------------------------------------------------- */
753 : /* Metadata stored in the ROI_PAC domain. */
754 : /* -------------------------------------------------------------------- */
755 3 : char **papszROIPACMetadata = GetMetadata("ROI_PAC");
756 3 : for (int i = 0; i < CSLCount(papszROIPACMetadata); i++)
757 : {
758 : /* Get the tokens from the metadata item */
759 : char **papszTokens =
760 0 : CSLTokenizeString2(papszROIPACMetadata[i], "=",
761 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
762 0 : if (CSLCount(papszTokens) != 2)
763 : {
764 0 : CPLDebug("ROI_PAC",
765 : "Line of header file could not be split at = "
766 : "into two elements: %s",
767 0 : papszROIPACMetadata[i]);
768 0 : CSLDestroy(papszTokens);
769 0 : continue;
770 : }
771 :
772 : /* Don't write it out if it is one of the bits of metadata that is
773 : * written out elsewhere in this routine */
774 0 : if (strcmp(papszTokens[0], "WIDTH") == 0 ||
775 0 : strcmp(papszTokens[0], "FILE_LENGTH") == 0)
776 : {
777 0 : CSLDestroy(papszTokens);
778 0 : continue;
779 : }
780 0 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", papszTokens[0],
781 0 : papszTokens[1]) > 0;
782 0 : CSLDestroy(papszTokens);
783 : }
784 3 : if (!bOK)
785 0 : eErr = CE_Failure;
786 3 : return eErr;
787 : }
788 :
789 : /************************************************************************/
790 : /* GetGeoTransform() */
791 : /************************************************************************/
792 :
793 8 : CPLErr ROIPACDataset::GetGeoTransform(GDALGeoTransform >) const
794 : {
795 8 : gt = m_gt;
796 8 : return bValidGeoTransform ? CE_None : CE_Failure;
797 : }
798 :
799 : /************************************************************************/
800 : /* SetGeoTransform() */
801 : /************************************************************************/
802 :
803 3 : CPLErr ROIPACDataset::SetGeoTransform(const GDALGeoTransform >)
804 : {
805 3 : m_gt = gt;
806 3 : bValidGeoTransform = true;
807 3 : return CE_None;
808 : }
809 :
810 : /************************************************************************/
811 : /* SetSpatialRef() */
812 : /************************************************************************/
813 :
814 3 : CPLErr ROIPACDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
815 :
816 : {
817 3 : if (poSRS)
818 3 : m_oSRS = *poSRS;
819 : else
820 0 : m_oSRS.Clear();
821 3 : return CE_None;
822 : }
823 :
824 : /************************************************************************/
825 : /* GetFileList() */
826 : /************************************************************************/
827 :
828 4 : char **ROIPACDataset::GetFileList()
829 : {
830 : // Main data file, etc.
831 4 : char **papszFileList = RawDataset::GetFileList();
832 :
833 : // RSC file.
834 4 : papszFileList = CSLAddString(papszFileList, pszRscFilename);
835 :
836 4 : return papszFileList;
837 : }
838 :
839 : /************************************************************************/
840 : /* GDALRegister_ROIPAC() */
841 : /************************************************************************/
842 :
843 2038 : void GDALRegister_ROIPAC()
844 : {
845 2038 : if (GDALGetDriverByName("ROI_PAC") != nullptr)
846 283 : return;
847 :
848 1755 : GDALDriver *poDriver = new GDALDriver();
849 :
850 1755 : poDriver->SetDescription("ROI_PAC");
851 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ROI_PAC raster");
852 1755 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
853 1755 : "drivers/raster/roi_pac.html");
854 1755 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
855 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
856 :
857 1755 : poDriver->pfnOpen = ROIPACDataset::Open;
858 1755 : poDriver->pfnIdentify = ROIPACDataset::Identify;
859 1755 : poDriver->pfnCreate = ROIPACDataset::Create;
860 :
861 1755 : GetGDALDriverManager()->RegisterDriver(poDriver);
862 : }
|