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