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