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 : CPLFormFilename(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 = CPLGetPath(poOpenInfo->pszFilename);
89 170 : const CPLString osName = CPLGetFilename(poOpenInfo->pszFilename);
90 :
91 85 : int iFile = CSLFindString(papszSiblingFiles,
92 : CPLFormFilename(nullptr, osName, "rsc"));
93 85 : if (iFile >= 0)
94 : {
95 45 : return CPLFormFilename(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 = CPLGetExtension(poOpenInfo->pszFilename);
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 51408 : int ROIPACDataset::Identify(GDALOpenInfo *poOpenInfo)
504 : {
505 : /* -------------------------------------------------------------------- */
506 : /* Check if: */
507 : /* * 1. The data file extension is known */
508 : /* -------------------------------------------------------------------- */
509 51408 : const char *pszExtension = CPLGetExtension(poOpenInfo->pszFilename);
510 51407 : 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 51384 : const bool bExtensionIsValid =
517 51377 : strcmp(pszExtension, "int") == 0 || strcmp(pszExtension, "slc") == 0 ||
518 51359 : strcmp(pszExtension, "amp") == 0 || strcmp(pszExtension, "cor") == 0 ||
519 51361 : strcmp(pszExtension, "hgt") == 0 || strcmp(pszExtension, "unw") == 0 ||
520 51339 : strcmp(pszExtension, "msk") == 0 ||
521 51292 : strcmp(pszExtension, "trans") == 0 ||
522 102761 : strcmp(pszExtension, "dem") == 0 || strcmp(pszExtension, "flg") == 0;
523 51384 : if (!bExtensionIsValid)
524 : {
525 51210 : 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 52 : const char *pszExtension = CPLGetExtension(pszFilename);
552 52 : if (strcmp(pszExtension, "int") == 0 || strcmp(pszExtension, "slc") == 0)
553 : {
554 0 : if (nBandsIn != 1 || eType != GDT_CFloat32)
555 : {
556 0 : CPLError(CE_Failure, CPLE_AppDefined,
557 : "Attempt to create ROI_PAC %s dataset with an illegal "
558 : "number of bands (%d) and/or data type (%s).",
559 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
560 0 : return nullptr;
561 : }
562 : }
563 52 : else if (strcmp(pszExtension, "amp") == 0)
564 : {
565 0 : if (nBandsIn != 2 || eType != GDT_Float32)
566 : {
567 0 : CPLError(CE_Failure, CPLE_AppDefined,
568 : "Attempt to create ROI_PAC %s dataset with an illegal "
569 : "number of bands (%d) and/or data type (%s).",
570 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
571 0 : return nullptr;
572 : }
573 : }
574 52 : else if (strcmp(pszExtension, "cor") == 0 ||
575 52 : strcmp(pszExtension, "hgt") == 0 ||
576 52 : strcmp(pszExtension, "unw") == 0 ||
577 52 : strcmp(pszExtension, "msk") == 0 ||
578 52 : strcmp(pszExtension, "trans") == 0)
579 : {
580 0 : if (nBandsIn != 2 || eType != GDT_Float32)
581 : {
582 0 : CPLError(CE_Failure, CPLE_AppDefined,
583 : "Attempt to create ROI_PAC %s dataset with an illegal "
584 : "number of bands (%d) and/or data type (%s).",
585 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
586 0 : return nullptr;
587 : }
588 : }
589 52 : else if (strcmp(pszExtension, "dem") == 0)
590 : {
591 2 : if (nBandsIn != 1 || eType != GDT_Int16)
592 : {
593 0 : CPLError(CE_Failure, CPLE_AppDefined,
594 : "Attempt to create ROI_PAC %s dataset with an illegal "
595 : "number of bands (%d) and/or data type (%s).",
596 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
597 0 : return nullptr;
598 : }
599 : }
600 50 : else if (strcmp(pszExtension, "flg") == 0)
601 : {
602 1 : if (nBandsIn != 1 || eType != GDT_Byte)
603 : {
604 0 : CPLError(CE_Failure, CPLE_AppDefined,
605 : "Attempt to create ROI_PAC %s dataset with an illegal "
606 : "number of bands (%d) and/or data type (%s).",
607 : pszExtension, nBandsIn, GDALGetDataTypeName(eType));
608 0 : return nullptr;
609 : }
610 : }
611 : else
612 : { /* Eeek */
613 49 : CPLError(CE_Failure, CPLE_AppDefined,
614 : "Attempt to create ROI_PAC dataset with an unknown type (%s)",
615 : pszExtension);
616 49 : return nullptr;
617 : }
618 :
619 : /* -------------------------------------------------------------------- */
620 : /* Try to create the file. */
621 : /* -------------------------------------------------------------------- */
622 3 : VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
623 3 : if (fp == nullptr)
624 : {
625 0 : CPLError(CE_Failure, CPLE_OpenFailed,
626 : "Attempt to create file `%s' failed.", pszFilename);
627 0 : return nullptr;
628 : }
629 :
630 : /* -------------------------------------------------------------------- */
631 : /* Just write out a couple of bytes to establish the binary */
632 : /* file, and then close it. */
633 : /* -------------------------------------------------------------------- */
634 3 : CPL_IGNORE_RET_VAL(VSIFWriteL("\0\0", 2, 1, fp));
635 3 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
636 :
637 : /* -------------------------------------------------------------------- */
638 : /* Open the RSC file. */
639 : /* -------------------------------------------------------------------- */
640 3 : const char *pszRSCFilename = CPLFormFilename(nullptr, pszFilename, "rsc");
641 3 : fp = VSIFOpenL(pszRSCFilename, "wt");
642 3 : if (fp == nullptr)
643 : {
644 0 : CPLError(CE_Failure, CPLE_OpenFailed,
645 : "Attempt to create file `%s' failed.", pszRSCFilename);
646 0 : return nullptr;
647 : }
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Write out the header. */
651 : /* -------------------------------------------------------------------- */
652 3 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "%-40s %d\n", "WIDTH", nXSize));
653 3 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "%-40s %d\n", "FILE_LENGTH", nYSize));
654 3 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
655 :
656 3 : return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
657 : }
658 :
659 : /************************************************************************/
660 : /* FlushCache() */
661 : /************************************************************************/
662 :
663 15 : CPLErr ROIPACDataset::FlushCache(bool bAtClosing)
664 : {
665 15 : CPLErr eErr = RawDataset::FlushCache(bAtClosing);
666 :
667 15 : GDALRasterBand *band = (GetRasterCount() > 0) ? GetRasterBand(1) : nullptr;
668 :
669 15 : if (eAccess == GA_ReadOnly || band == nullptr)
670 12 : return eErr;
671 :
672 : // If opening an existing file in Update mode (i.e. "r+") we need to make
673 : // sure any existing content is cleared, otherwise the file may contain
674 : // trailing content from the previous write.
675 3 : bool bOK = VSIFTruncateL(fpRsc, 0) == 0;
676 :
677 3 : bOK &= VSIFSeekL(fpRsc, 0, SEEK_SET) == 0;
678 : /* -------------------------------------------------------------------- */
679 : /* Rewrite out the header. */
680 : /* -------------------------------------------------------------------- */
681 : /* -------------------------------------------------------------------- */
682 : /* Raster dimensions. */
683 : /* -------------------------------------------------------------------- */
684 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %d\n", "WIDTH", nRasterXSize) > 0;
685 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %d\n", "FILE_LENGTH", nRasterYSize) > 0;
686 :
687 : /* -------------------------------------------------------------------- */
688 : /* Georeferencing. */
689 : /* -------------------------------------------------------------------- */
690 3 : if (!m_oSRS.IsEmpty())
691 : {
692 3 : int bNorth = FALSE;
693 3 : int iUTMZone = m_oSRS.GetUTMZone(&bNorth);
694 3 : if (iUTMZone != 0)
695 : {
696 1 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s%d\n", "PROJECTION", "UTM",
697 1 : iUTMZone) > 0;
698 : }
699 2 : else if (m_oSRS.IsGeographic())
700 : {
701 2 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "PROJECTION", "LL") > 0;
702 : }
703 : else
704 : {
705 0 : CPLError(CE_Warning, CPLE_AppDefined,
706 : "ROI_PAC format only support Latitude/Longitude and "
707 : "UTM projections, discarding projection.");
708 : }
709 :
710 3 : if (m_oSRS.GetAttrValue("DATUM") != nullptr)
711 : {
712 3 : if (strcmp(m_oSRS.GetAttrValue("DATUM"), "WGS_1984") == 0)
713 : {
714 2 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "DATUM", "WGS84") > 0;
715 : }
716 : else
717 : {
718 1 : CPLError(CE_Warning, CPLE_AppDefined,
719 : "Datum \"%s\" probably not supported in the "
720 : "ROI_PAC format, saving it anyway",
721 : m_oSRS.GetAttrValue("DATUM"));
722 1 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "DATUM",
723 1 : m_oSRS.GetAttrValue("DATUM")) > 0;
724 : }
725 : }
726 3 : if (m_oSRS.GetAttrValue("UNIT") != nullptr)
727 : {
728 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "X_UNIT",
729 3 : m_oSRS.GetAttrValue("UNIT")) > 0;
730 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", "Y_UNIT",
731 3 : m_oSRS.GetAttrValue("UNIT")) > 0;
732 : }
733 : }
734 3 : if (bValidGeoTransform)
735 : {
736 3 : if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
737 : {
738 0 : CPLError(CE_Warning, CPLE_AppDefined,
739 : "ROI_PAC format do not support geotransform with "
740 : "rotation, discarding info.");
741 : }
742 : else
743 : {
744 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "X_FIRST",
745 3 : adfGeoTransform[0]) > 0;
746 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "X_STEP",
747 3 : adfGeoTransform[1]) > 0;
748 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Y_FIRST",
749 3 : adfGeoTransform[3]) > 0;
750 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Y_STEP",
751 3 : adfGeoTransform[5]) > 0;
752 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Z_OFFSET",
753 3 : band->GetOffset(nullptr)) > 0;
754 3 : bOK &= VSIFPrintfL(fpRsc, "%-40s %.16g\n", "Z_SCALE",
755 6 : band->GetScale(nullptr)) > 0;
756 : }
757 : }
758 :
759 : /* -------------------------------------------------------------------- */
760 : /* Metadata stored in the ROI_PAC domain. */
761 : /* -------------------------------------------------------------------- */
762 3 : char **papszROIPACMetadata = GetMetadata("ROI_PAC");
763 3 : for (int i = 0; i < CSLCount(papszROIPACMetadata); i++)
764 : {
765 : /* Get the tokens from the metadata item */
766 : char **papszTokens =
767 0 : CSLTokenizeString2(papszROIPACMetadata[i], "=",
768 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
769 0 : if (CSLCount(papszTokens) != 2)
770 : {
771 0 : CPLDebug("ROI_PAC",
772 : "Line of header file could not be split at = "
773 : "into two elements: %s",
774 0 : papszROIPACMetadata[i]);
775 0 : CSLDestroy(papszTokens);
776 0 : continue;
777 : }
778 :
779 : /* Don't write it out if it is one of the bits of metadata that is
780 : * written out elsewhere in this routine */
781 0 : if (strcmp(papszTokens[0], "WIDTH") == 0 ||
782 0 : strcmp(papszTokens[0], "FILE_LENGTH") == 0)
783 : {
784 0 : CSLDestroy(papszTokens);
785 0 : continue;
786 : }
787 0 : bOK &= VSIFPrintfL(fpRsc, "%-40s %s\n", papszTokens[0],
788 0 : papszTokens[1]) > 0;
789 0 : CSLDestroy(papszTokens);
790 : }
791 3 : if (!bOK)
792 0 : eErr = CE_Failure;
793 3 : return eErr;
794 : }
795 :
796 : /************************************************************************/
797 : /* GetGeoTransform() */
798 : /************************************************************************/
799 :
800 8 : CPLErr ROIPACDataset::GetGeoTransform(double *padfTransform)
801 : {
802 8 : memcpy(padfTransform, adfGeoTransform, sizeof(adfGeoTransform));
803 8 : return bValidGeoTransform ? CE_None : CE_Failure;
804 : }
805 :
806 : /************************************************************************/
807 : /* SetGeoTransform() */
808 : /************************************************************************/
809 :
810 3 : CPLErr ROIPACDataset::SetGeoTransform(double *padfTransform)
811 : {
812 3 : memcpy(adfGeoTransform, padfTransform, sizeof(adfGeoTransform));
813 3 : bValidGeoTransform = true;
814 3 : return CE_None;
815 : }
816 :
817 : /************************************************************************/
818 : /* SetSpatialRef() */
819 : /************************************************************************/
820 :
821 3 : CPLErr ROIPACDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
822 :
823 : {
824 3 : if (poSRS)
825 3 : m_oSRS = *poSRS;
826 : else
827 0 : m_oSRS.Clear();
828 3 : return CE_None;
829 : }
830 :
831 : /************************************************************************/
832 : /* GetFileList() */
833 : /************************************************************************/
834 :
835 4 : char **ROIPACDataset::GetFileList()
836 : {
837 : // Main data file, etc.
838 4 : char **papszFileList = RawDataset::GetFileList();
839 :
840 : // RSC file.
841 4 : papszFileList = CSLAddString(papszFileList, pszRscFilename);
842 :
843 4 : return papszFileList;
844 : }
845 :
846 : /************************************************************************/
847 : /* GDALRegister_ROIPAC() */
848 : /************************************************************************/
849 :
850 1595 : void GDALRegister_ROIPAC()
851 : {
852 1595 : if (GDALGetDriverByName("ROI_PAC") != nullptr)
853 302 : return;
854 :
855 1293 : GDALDriver *poDriver = new GDALDriver();
856 :
857 1293 : poDriver->SetDescription("ROI_PAC");
858 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ROI_PAC raster");
859 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
860 1293 : "drivers/raster/roi_pac.html");
861 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
862 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
863 :
864 1293 : poDriver->pfnOpen = ROIPACDataset::Open;
865 1293 : poDriver->pfnIdentify = ROIPACDataset::Identify;
866 1293 : poDriver->pfnCreate = ROIPACDataset::Create;
867 :
868 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
869 : }
|