Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ERMapper .ers Driver
4 : * Purpose: Implementation of .ers driver.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_string.h"
15 : #include "ershdrnode.h"
16 : #include "gdal_frmts.h"
17 : #include "gdal_proxy.h"
18 : #include "ogr_spatialref.h"
19 : #include "rawdataset.h"
20 :
21 : #include <limits>
22 :
23 : /************************************************************************/
24 : /* ==================================================================== */
25 : /* ERSDataset */
26 : /* ==================================================================== */
27 : /************************************************************************/
28 :
29 : class ERSRasterBand;
30 :
31 : class ERSDataset final : public RawDataset
32 : {
33 : friend class ERSRasterBand;
34 :
35 : VSILFILE *fpImage; // Image data file.
36 : GDALDataset *poDepFile;
37 :
38 : int bGotTransform;
39 : double adfGeoTransform[6];
40 : OGRSpatialReference m_oSRS{};
41 :
42 : CPLString osRawFilename;
43 :
44 : int bHDRDirty;
45 : ERSHdrNode *poHeader;
46 :
47 : const char *Find(const char *, const char *);
48 :
49 : int nGCPCount;
50 : GDAL_GCP *pasGCPList;
51 : OGRSpatialReference m_oGCPSRS{};
52 :
53 : void ReadGCPs();
54 :
55 : int bHasNoDataValue;
56 : double dfNoDataValue;
57 :
58 : CPLString osProj, osProjForced;
59 : CPLString osDatum, osDatumForced;
60 : CPLString osUnits, osUnitsForced;
61 : void WriteProjectionInfo(const char *pszProj, const char *pszDatum,
62 : const char *pszUnits);
63 :
64 : CPLStringList oERSMetadataList;
65 :
66 : protected:
67 : int CloseDependentDatasets() override;
68 :
69 : CPLErr Close() override;
70 :
71 : public:
72 : ERSDataset();
73 : ~ERSDataset() override;
74 :
75 : CPLErr FlushCache(bool bAtClosing) override;
76 : CPLErr GetGeoTransform(double *padfTransform) override;
77 : CPLErr SetGeoTransform(double *padfTransform) override;
78 : const OGRSpatialReference *GetSpatialRef() const override;
79 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
80 : char **GetFileList(void) override;
81 :
82 : int GetGCPCount() override;
83 : const OGRSpatialReference *GetGCPSpatialRef() const override;
84 : const GDAL_GCP *GetGCPs() override;
85 : CPLErr SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
86 : const OGRSpatialReference *poSRS) override;
87 :
88 : char **GetMetadataDomainList() override;
89 : const char *GetMetadataItem(const char *pszName,
90 : const char *pszDomain = "") override;
91 : char **GetMetadata(const char *pszDomain = "") override;
92 :
93 : static GDALDataset *Open(GDALOpenInfo *);
94 : static int Identify(GDALOpenInfo *);
95 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
96 : int nBandsIn, GDALDataType eType,
97 : char **papszParamList);
98 : };
99 :
100 : /************************************************************************/
101 : /* ERSDataset() */
102 : /************************************************************************/
103 :
104 62 : ERSDataset::ERSDataset()
105 : : fpImage(nullptr), poDepFile(nullptr), bGotTransform(FALSE),
106 : bHDRDirty(FALSE), poHeader(nullptr), nGCPCount(0), pasGCPList(nullptr),
107 62 : bHasNoDataValue(FALSE), dfNoDataValue(0.0)
108 : {
109 62 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
110 62 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
111 62 : adfGeoTransform[0] = 0.0;
112 62 : adfGeoTransform[1] = 1.0;
113 62 : adfGeoTransform[2] = 0.0;
114 62 : adfGeoTransform[3] = 0.0;
115 62 : adfGeoTransform[4] = 0.0;
116 62 : adfGeoTransform[5] = 1.0;
117 62 : }
118 :
119 : /************************************************************************/
120 : /* ~ERSDataset() */
121 : /************************************************************************/
122 :
123 124 : ERSDataset::~ERSDataset()
124 :
125 : {
126 62 : ERSDataset::Close();
127 124 : }
128 :
129 : /************************************************************************/
130 : /* Close() */
131 : /************************************************************************/
132 :
133 123 : CPLErr ERSDataset::Close()
134 : {
135 123 : CPLErr eErr = CE_None;
136 123 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
137 : {
138 62 : if (ERSDataset::FlushCache(true) != CE_None)
139 0 : eErr = CE_Failure;
140 :
141 62 : if (fpImage != nullptr)
142 : {
143 60 : VSIFCloseL(fpImage);
144 : }
145 :
146 62 : ERSDataset::CloseDependentDatasets();
147 :
148 62 : if (nGCPCount > 0)
149 : {
150 3 : GDALDeinitGCPs(nGCPCount, pasGCPList);
151 3 : CPLFree(pasGCPList);
152 : }
153 :
154 62 : if (poHeader != nullptr)
155 62 : delete poHeader;
156 :
157 62 : if (GDALPamDataset::Close() != CE_None)
158 0 : eErr = CE_Failure;
159 : }
160 123 : return eErr;
161 : }
162 :
163 : /************************************************************************/
164 : /* CloseDependentDatasets() */
165 : /************************************************************************/
166 :
167 62 : int ERSDataset::CloseDependentDatasets()
168 : {
169 62 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
170 :
171 62 : if (poDepFile != nullptr)
172 : {
173 1 : bHasDroppedRef = TRUE;
174 :
175 2 : for (int iBand = 0; iBand < nBands; iBand++)
176 : {
177 1 : delete papoBands[iBand];
178 1 : papoBands[iBand] = nullptr;
179 : }
180 1 : nBands = 0;
181 :
182 1 : GDALClose((GDALDatasetH)poDepFile);
183 1 : poDepFile = nullptr;
184 : }
185 :
186 62 : return bHasDroppedRef;
187 : }
188 :
189 : /************************************************************************/
190 : /* FlushCache() */
191 : /************************************************************************/
192 :
193 63 : CPLErr ERSDataset::FlushCache(bool bAtClosing)
194 :
195 : {
196 63 : CPLErr eErr = CE_None;
197 63 : if (bHDRDirty)
198 : {
199 37 : VSILFILE *fpERS = VSIFOpenL(GetDescription(), "w");
200 37 : if (fpERS == nullptr)
201 : {
202 0 : eErr = CE_Failure;
203 0 : CPLError(CE_Failure, CPLE_OpenFailed,
204 0 : "Unable to rewrite %s header.", GetDescription());
205 : }
206 : else
207 : {
208 37 : if (VSIFPrintfL(fpERS, "DatasetHeader Begin\n") <= 0)
209 0 : eErr = CE_Failure;
210 37 : poHeader->WriteSelf(fpERS, 1);
211 37 : if (VSIFPrintfL(fpERS, "DatasetHeader End\n") <= 0)
212 0 : eErr = CE_Failure;
213 37 : if (VSIFCloseL(fpERS) != 0)
214 0 : eErr = CE_Failure;
215 : }
216 : }
217 :
218 63 : if (RawDataset::FlushCache(bAtClosing) != CE_None)
219 0 : eErr = CE_Failure;
220 63 : return eErr;
221 : }
222 :
223 : /************************************************************************/
224 : /* GetMetadataDomainList() */
225 : /************************************************************************/
226 :
227 0 : char **ERSDataset::GetMetadataDomainList()
228 : {
229 0 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
230 0 : TRUE, "ERS", nullptr);
231 : }
232 :
233 : /************************************************************************/
234 : /* GetMetadataItem() */
235 : /************************************************************************/
236 :
237 60 : const char *ERSDataset::GetMetadataItem(const char *pszName,
238 : const char *pszDomain)
239 : {
240 60 : if (pszDomain != nullptr && EQUAL(pszDomain, "ERS") && pszName != nullptr)
241 : {
242 15 : if (EQUAL(pszName, "PROJ"))
243 5 : return osProj.size() ? osProj.c_str() : nullptr;
244 10 : if (EQUAL(pszName, "DATUM"))
245 5 : return osDatum.size() ? osDatum.c_str() : nullptr;
246 5 : if (EQUAL(pszName, "UNITS"))
247 5 : return osUnits.size() ? osUnits.c_str() : nullptr;
248 : }
249 45 : return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
250 : }
251 :
252 : /************************************************************************/
253 : /* GetMetadata() */
254 : /************************************************************************/
255 :
256 17 : char **ERSDataset::GetMetadata(const char *pszDomain)
257 :
258 : {
259 17 : if (pszDomain != nullptr && EQUAL(pszDomain, "ERS"))
260 : {
261 1 : oERSMetadataList.Clear();
262 1 : if (!osProj.empty())
263 : oERSMetadataList.AddString(
264 1 : CPLSPrintf("%s=%s", "PROJ", osProj.c_str()));
265 1 : if (!osDatum.empty())
266 : oERSMetadataList.AddString(
267 1 : CPLSPrintf("%s=%s", "DATUM", osDatum.c_str()));
268 1 : if (!osUnits.empty())
269 : oERSMetadataList.AddString(
270 1 : CPLSPrintf("%s=%s", "UNITS", osUnits.c_str()));
271 1 : return oERSMetadataList.List();
272 : }
273 :
274 16 : return GDALPamDataset::GetMetadata(pszDomain);
275 : }
276 :
277 : /************************************************************************/
278 : /* GetGCPCount() */
279 : /************************************************************************/
280 :
281 3 : int ERSDataset::GetGCPCount()
282 :
283 : {
284 3 : return nGCPCount;
285 : }
286 :
287 : /************************************************************************/
288 : /* GetGCPSpatialRef() */
289 : /************************************************************************/
290 :
291 1 : const OGRSpatialReference *ERSDataset::GetGCPSpatialRef() const
292 :
293 : {
294 1 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
295 : }
296 :
297 : /************************************************************************/
298 : /* GetGCPs() */
299 : /************************************************************************/
300 :
301 1 : const GDAL_GCP *ERSDataset::GetGCPs()
302 :
303 : {
304 1 : return pasGCPList;
305 : }
306 :
307 : /************************************************************************/
308 : /* SetGCPs() */
309 : /************************************************************************/
310 :
311 1 : CPLErr ERSDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
312 : const OGRSpatialReference *poSRS)
313 :
314 : {
315 : /* -------------------------------------------------------------------- */
316 : /* Clean old gcps. */
317 : /* -------------------------------------------------------------------- */
318 1 : m_oGCPSRS.Clear();
319 :
320 1 : if (nGCPCount > 0)
321 : {
322 0 : GDALDeinitGCPs(nGCPCount, pasGCPList);
323 0 : CPLFree(pasGCPList);
324 :
325 0 : pasGCPList = nullptr;
326 0 : nGCPCount = 0;
327 : }
328 :
329 : /* -------------------------------------------------------------------- */
330 : /* Copy new ones. */
331 : /* -------------------------------------------------------------------- */
332 1 : nGCPCount = nGCPCountIn;
333 1 : pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
334 1 : if (poSRS)
335 1 : m_oGCPSRS = *poSRS;
336 :
337 : /* -------------------------------------------------------------------- */
338 : /* Setup the header contents corresponding to these GCPs. */
339 : /* -------------------------------------------------------------------- */
340 1 : bHDRDirty = TRUE;
341 :
342 1 : poHeader->Set("RasterInfo.WarpControl.WarpType", "Polynomial");
343 1 : if (nGCPCount > 6)
344 0 : poHeader->Set("RasterInfo.WarpControl.WarpOrder", "2");
345 : else
346 1 : poHeader->Set("RasterInfo.WarpControl.WarpOrder", "1");
347 1 : poHeader->Set("RasterInfo.WarpControl.WarpSampling", "Nearest");
348 :
349 : /* -------------------------------------------------------------------- */
350 : /* Translate the projection. */
351 : /* -------------------------------------------------------------------- */
352 : char szERSProj[32], szERSDatum[32], szERSUnits[32];
353 :
354 1 : m_oGCPSRS.exportToERM(szERSProj, szERSDatum, szERSUnits);
355 :
356 : /* Write the above computed values, unless they have been overridden by */
357 : /* the creation options PROJ, DATUM or UNITS */
358 :
359 1 : poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Datum",
360 2 : CPLString().Printf("\"%s\"", (osDatum.size())
361 0 : ? osDatum.c_str()
362 1 : : szERSDatum));
363 1 : poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Projection",
364 2 : CPLString().Printf("\"%s\"", (osProj.size()) ? osProj.c_str()
365 1 : : szERSProj));
366 1 : poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.CoordinateType",
367 2 : CPLString().Printf("EN"));
368 1 : poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Units",
369 2 : CPLString().Printf("\"%s\"", (osUnits.size())
370 0 : ? osUnits.c_str()
371 1 : : szERSUnits));
372 1 : poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Rotation", "0:0:0.0");
373 :
374 : /* -------------------------------------------------------------------- */
375 : /* Translate the GCPs. */
376 : /* -------------------------------------------------------------------- */
377 1 : CPLString osControlPoints = "{\n";
378 :
379 5 : for (int iGCP = 0; iGCP < nGCPCount; iGCP++)
380 : {
381 8 : CPLString osLine;
382 :
383 8 : CPLString osId = pasGCPList[iGCP].pszId;
384 4 : if (osId.empty())
385 4 : osId.Printf("%d", iGCP + 1);
386 :
387 : osLine.Printf(
388 : "\t\t\t\t\"%s\"\tYes\tYes\t%.6f\t%.6f\t%.15g\t%.15g\t%.15g\n",
389 4 : osId.c_str(), pasGCPList[iGCP].dfGCPPixel,
390 4 : pasGCPList[iGCP].dfGCPLine, pasGCPList[iGCP].dfGCPX,
391 4 : pasGCPList[iGCP].dfGCPY, pasGCPList[iGCP].dfGCPZ);
392 4 : osControlPoints += osLine;
393 : }
394 1 : osControlPoints += "\t\t}";
395 :
396 1 : poHeader->Set("RasterInfo.WarpControl.ControlPoints", osControlPoints);
397 :
398 2 : return CE_None;
399 : }
400 :
401 : /************************************************************************/
402 : /* GetSpatialRef() */
403 : /************************************************************************/
404 :
405 3 : const OGRSpatialReference *ERSDataset::GetSpatialRef() const
406 :
407 : {
408 : // try xml first
409 3 : const auto poSRS = GDALPamDataset::GetSpatialRef();
410 3 : if (poSRS)
411 0 : return poSRS;
412 :
413 3 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
414 : }
415 :
416 : /************************************************************************/
417 : /* SetSpatialRef() */
418 : /************************************************************************/
419 :
420 34 : CPLErr ERSDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
421 :
422 : {
423 34 : if (poSRS == nullptr && m_oSRS.IsEmpty())
424 0 : return CE_None;
425 34 : if (poSRS != nullptr && poSRS->IsSame(&m_oSRS))
426 0 : return CE_None;
427 :
428 34 : m_oSRS.Clear();
429 34 : if (poSRS)
430 34 : m_oSRS = *poSRS;
431 :
432 : char szERSProj[32], szERSDatum[32], szERSUnits[32];
433 :
434 34 : m_oSRS.exportToERM(szERSProj, szERSDatum, szERSUnits);
435 :
436 : /* Write the above computed values, unless they have been overridden by */
437 : /* the creation options PROJ, DATUM or UNITS */
438 34 : if (!osProjForced.empty())
439 1 : osProj = osProjForced;
440 : else
441 33 : osProj = szERSProj;
442 34 : if (!osDatumForced.empty())
443 1 : osDatum = osDatumForced;
444 : else
445 33 : osDatum = szERSDatum;
446 34 : if (!osUnitsForced.empty())
447 1 : osUnits = osUnitsForced;
448 : else
449 33 : osUnits = szERSUnits;
450 :
451 34 : WriteProjectionInfo(osProj, osDatum, osUnits);
452 :
453 34 : return CE_None;
454 : }
455 :
456 : /************************************************************************/
457 : /* WriteProjectionInfo() */
458 : /************************************************************************/
459 :
460 36 : void ERSDataset::WriteProjectionInfo(const char *pszProj, const char *pszDatum,
461 : const char *pszUnits)
462 : {
463 36 : bHDRDirty = TRUE;
464 36 : poHeader->Set("CoordinateSpace.Datum",
465 72 : CPLString().Printf("\"%s\"", pszDatum));
466 36 : poHeader->Set("CoordinateSpace.Projection",
467 72 : CPLString().Printf("\"%s\"", pszProj));
468 36 : poHeader->Set("CoordinateSpace.CoordinateType", CPLString().Printf("EN"));
469 36 : poHeader->Set("CoordinateSpace.Units",
470 72 : CPLString().Printf("\"%s\"", pszUnits));
471 36 : poHeader->Set("CoordinateSpace.Rotation", "0:0:0.0");
472 :
473 : /* -------------------------------------------------------------------- */
474 : /* It seems that CoordinateSpace needs to come before */
475 : /* RasterInfo. Try moving it up manually. */
476 : /* -------------------------------------------------------------------- */
477 36 : int iRasterInfo = -1;
478 36 : int iCoordSpace = -1;
479 :
480 250 : for (int i = 0; i < poHeader->nItemCount; i++)
481 : {
482 250 : if (EQUAL(poHeader->papszItemName[i], "RasterInfo"))
483 34 : iRasterInfo = i;
484 :
485 250 : if (EQUAL(poHeader->papszItemName[i], "CoordinateSpace"))
486 : {
487 36 : iCoordSpace = i;
488 36 : break;
489 : }
490 : }
491 :
492 36 : if (iCoordSpace > iRasterInfo && iRasterInfo != -1)
493 : {
494 68 : for (int i = iCoordSpace; i > 0 && i != iRasterInfo; i--)
495 : {
496 :
497 34 : ERSHdrNode *poTemp = poHeader->papoItemChild[i];
498 34 : poHeader->papoItemChild[i] = poHeader->papoItemChild[i - 1];
499 34 : poHeader->papoItemChild[i - 1] = poTemp;
500 :
501 34 : char *pszTemp = poHeader->papszItemName[i];
502 34 : poHeader->papszItemName[i] = poHeader->papszItemName[i - 1];
503 34 : poHeader->papszItemName[i - 1] = pszTemp;
504 :
505 34 : pszTemp = poHeader->papszItemValue[i];
506 34 : poHeader->papszItemValue[i] = poHeader->papszItemValue[i - 1];
507 34 : poHeader->papszItemValue[i - 1] = pszTemp;
508 : }
509 : }
510 36 : }
511 :
512 : /************************************************************************/
513 : /* GetGeoTransform() */
514 : /************************************************************************/
515 :
516 22 : CPLErr ERSDataset::GetGeoTransform(double *padfTransform)
517 :
518 : {
519 22 : if (bGotTransform)
520 : {
521 22 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
522 22 : return CE_None;
523 : }
524 :
525 0 : return GDALPamDataset::GetGeoTransform(padfTransform);
526 : }
527 :
528 : /************************************************************************/
529 : /* SetGeoTransform() */
530 : /************************************************************************/
531 :
532 32 : CPLErr ERSDataset::SetGeoTransform(double *padfTransform)
533 :
534 : {
535 32 : if (memcmp(padfTransform, adfGeoTransform, sizeof(double) * 6) == 0)
536 0 : return CE_None;
537 :
538 32 : if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
539 : {
540 0 : CPLError(CE_Failure, CPLE_AppDefined,
541 : "Rotated and skewed geotransforms not currently supported for "
542 : "ERS driver.");
543 0 : return CE_Failure;
544 : }
545 :
546 32 : bGotTransform = TRUE;
547 32 : memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
548 :
549 32 : bHDRDirty = TRUE;
550 :
551 32 : poHeader->Set("RasterInfo.CellInfo.Xdimension",
552 64 : CPLString().Printf("%.15g", fabs(adfGeoTransform[1])));
553 32 : poHeader->Set("RasterInfo.CellInfo.Ydimension",
554 64 : CPLString().Printf("%.15g", fabs(adfGeoTransform[5])));
555 32 : poHeader->Set("RasterInfo.RegistrationCoord.Eastings",
556 64 : CPLString().Printf("%.15g", adfGeoTransform[0]));
557 32 : poHeader->Set("RasterInfo.RegistrationCoord.Northings",
558 64 : CPLString().Printf("%.15g", adfGeoTransform[3]));
559 :
560 64 : if (CPLAtof(poHeader->Find("RasterInfo.RegistrationCellX", "0")) != 0.0 ||
561 32 : CPLAtof(poHeader->Find("RasterInfo.RegistrationCellY", "0")) != 0.0)
562 : {
563 : // Reset RegistrationCellX/Y to 0 if the header gets rewritten (#5493)
564 0 : poHeader->Set("RasterInfo.RegistrationCellX", "0");
565 0 : poHeader->Set("RasterInfo.RegistrationCellY", "0");
566 : }
567 :
568 32 : return CE_None;
569 : }
570 :
571 : /************************************************************************/
572 : /* ERSDMS2Dec() */
573 : /* */
574 : /* Convert ERS DMS format to decimal degrees. Input is like */
575 : /* "-180:00:00". */
576 : /************************************************************************/
577 :
578 10 : static double ERSDMS2Dec(const char *pszDMS)
579 :
580 : {
581 10 : char **papszTokens = CSLTokenizeStringComplex(pszDMS, ":", FALSE, FALSE);
582 :
583 10 : if (CSLCount(papszTokens) != 3)
584 : {
585 0 : CSLDestroy(papszTokens);
586 0 : return CPLAtof(pszDMS);
587 : }
588 :
589 10 : double dfResult = fabs(CPLAtof(papszTokens[0])) +
590 10 : CPLAtof(papszTokens[1]) / 60.0 +
591 10 : CPLAtof(papszTokens[2]) / 3600.0;
592 :
593 10 : if (CPLAtof(papszTokens[0]) < 0)
594 8 : dfResult *= -1;
595 :
596 10 : CSLDestroy(papszTokens);
597 10 : return dfResult;
598 : }
599 :
600 : /************************************************************************/
601 : /* GetFileList() */
602 : /************************************************************************/
603 :
604 10 : char **ERSDataset::GetFileList()
605 :
606 : {
607 : static thread_local int nRecLevel = 0;
608 10 : if (nRecLevel > 0)
609 0 : return nullptr;
610 :
611 : // Main data file, etc.
612 10 : char **papszFileList = GDALPamDataset::GetFileList();
613 :
614 : // Add raw data file if we have one.
615 10 : if (!osRawFilename.empty())
616 10 : papszFileList = CSLAddString(papszFileList, osRawFilename);
617 :
618 : // If we have a dependent file, merge its list of files in.
619 10 : if (poDepFile)
620 : {
621 0 : nRecLevel++;
622 0 : char **papszDepFiles = poDepFile->GetFileList();
623 0 : nRecLevel--;
624 0 : papszFileList = CSLInsertStrings(papszFileList, -1, papszDepFiles);
625 0 : CSLDestroy(papszDepFiles);
626 : }
627 :
628 10 : return papszFileList;
629 : }
630 :
631 : /************************************************************************/
632 : /* ReadGCPs() */
633 : /* */
634 : /* Read the GCPs from the header. */
635 : /************************************************************************/
636 :
637 2 : void ERSDataset::ReadGCPs()
638 :
639 : {
640 : const char *pszCP =
641 2 : poHeader->Find("RasterInfo.WarpControl.ControlPoints", nullptr);
642 :
643 2 : if (pszCP == nullptr)
644 0 : return;
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Parse the control points. They will look something like: */
648 : /* */
649 : /* "1035" Yes No 2344.650885 3546.419458 483270.73 3620906.21 3.105 */
650 : /* -------------------------------------------------------------------- */
651 2 : char **papszTokens = CSLTokenizeStringComplex(pszCP, "{ \t}", TRUE, FALSE);
652 2 : int nItemCount = CSLCount(papszTokens);
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Work out if we have elevation values or not. */
656 : /* -------------------------------------------------------------------- */
657 : int nItemsPerLine;
658 :
659 2 : if (nItemCount == 7)
660 0 : nItemsPerLine = 7;
661 2 : else if (nItemCount == 8)
662 0 : nItemsPerLine = 8;
663 2 : else if (nItemCount < 14)
664 : {
665 0 : CPLDebug("ERS", "Invalid item count for ControlPoints");
666 0 : CSLDestroy(papszTokens);
667 0 : return;
668 : }
669 2 : else if (EQUAL(papszTokens[8], "Yes") || EQUAL(papszTokens[8], "No"))
670 0 : nItemsPerLine = 7;
671 2 : else if (EQUAL(papszTokens[9], "Yes") || EQUAL(papszTokens[9], "No"))
672 2 : nItemsPerLine = 8;
673 : else
674 : {
675 0 : CPLDebug("ERS", "Invalid format for ControlPoints");
676 0 : CSLDestroy(papszTokens);
677 0 : return;
678 : }
679 :
680 : /* -------------------------------------------------------------------- */
681 : /* Setup GCPs. */
682 : /* -------------------------------------------------------------------- */
683 2 : CPLAssert(nGCPCount == 0);
684 :
685 2 : nGCPCount = nItemCount / nItemsPerLine;
686 2 : pasGCPList = (GDAL_GCP *)CPLCalloc(nGCPCount, sizeof(GDAL_GCP));
687 2 : GDALInitGCPs(nGCPCount, pasGCPList);
688 :
689 10 : for (int iGCP = 0; iGCP < nGCPCount; iGCP++)
690 : {
691 8 : GDAL_GCP *psGCP = pasGCPList + iGCP;
692 :
693 8 : CPLFree(psGCP->pszId);
694 8 : psGCP->pszId = CPLStrdup(papszTokens[iGCP * nItemsPerLine + 0]);
695 8 : psGCP->dfGCPPixel = CPLAtof(papszTokens[iGCP * nItemsPerLine + 3]);
696 8 : psGCP->dfGCPLine = CPLAtof(papszTokens[iGCP * nItemsPerLine + 4]);
697 8 : psGCP->dfGCPX = CPLAtof(papszTokens[iGCP * nItemsPerLine + 5]);
698 8 : psGCP->dfGCPY = CPLAtof(papszTokens[iGCP * nItemsPerLine + 6]);
699 8 : if (nItemsPerLine == 8)
700 8 : psGCP->dfGCPZ = CPLAtof(papszTokens[iGCP * nItemsPerLine + 7]);
701 : }
702 :
703 2 : CSLDestroy(papszTokens);
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* Parse the GCP projection. */
707 : /* -------------------------------------------------------------------- */
708 : osProj =
709 2 : poHeader->Find("RasterInfo.WarpControl.CoordinateSpace.Projection", "");
710 : osDatum =
711 2 : poHeader->Find("RasterInfo.WarpControl.CoordinateSpace.Datum", "");
712 : osUnits =
713 2 : poHeader->Find("RasterInfo.WarpControl.CoordinateSpace.Units", "");
714 :
715 2 : m_oGCPSRS.importFromERM(!osProj.empty() ? osProj.c_str() : "RAW",
716 2 : !osDatum.empty() ? osDatum.c_str() : "WGS84",
717 2 : !osUnits.empty() ? osUnits.c_str() : "METERS");
718 : }
719 :
720 : /************************************************************************/
721 : /* ==================================================================== */
722 : /* ERSRasterBand */
723 : /* ==================================================================== */
724 : /************************************************************************/
725 :
726 : class ERSRasterBand final : public RawRasterBand
727 : {
728 : public:
729 : ERSRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
730 : vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
731 : GDALDataType eDataType, int bNativeOrder);
732 :
733 : double GetNoDataValue(int *pbSuccess = nullptr) override;
734 : CPLErr SetNoDataValue(double) override;
735 : };
736 :
737 : /************************************************************************/
738 : /* ERSRasterBand() */
739 : /************************************************************************/
740 :
741 102 : ERSRasterBand::ERSRasterBand(GDALDataset *poDSIn, int nBandIn,
742 : VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
743 : int nPixelOffsetIn, int nLineOffsetIn,
744 102 : GDALDataType eDataTypeIn, int bNativeOrderIn)
745 : : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
746 : nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
747 102 : RawRasterBand::OwnFP::NO)
748 : {
749 102 : }
750 :
751 : /************************************************************************/
752 : /* GetNoDataValue() */
753 : /************************************************************************/
754 :
755 15 : double ERSRasterBand::GetNoDataValue(int *pbSuccess)
756 : {
757 15 : ERSDataset *poGDS = cpl::down_cast<ERSDataset *>(poDS);
758 15 : if (poGDS->bHasNoDataValue)
759 : {
760 1 : if (pbSuccess)
761 1 : *pbSuccess = TRUE;
762 1 : return poGDS->dfNoDataValue;
763 : }
764 :
765 14 : return RawRasterBand::GetNoDataValue(pbSuccess);
766 : }
767 :
768 : /************************************************************************/
769 : /* SetNoDataValue() */
770 : /************************************************************************/
771 :
772 1 : CPLErr ERSRasterBand::SetNoDataValue(double dfNoDataValue)
773 : {
774 1 : ERSDataset *poGDS = cpl::down_cast<ERSDataset *>(poDS);
775 1 : if (!poGDS->bHasNoDataValue || poGDS->dfNoDataValue != dfNoDataValue)
776 : {
777 1 : poGDS->bHasNoDataValue = TRUE;
778 1 : poGDS->dfNoDataValue = dfNoDataValue;
779 :
780 1 : poGDS->bHDRDirty = TRUE;
781 1 : poGDS->poHeader->Set("RasterInfo.NullCellValue",
782 2 : CPLString().Printf("%.16g", dfNoDataValue));
783 : }
784 1 : return CE_None;
785 : }
786 :
787 : /************************************************************************/
788 : /* Identify() */
789 : /************************************************************************/
790 :
791 53931 : int ERSDataset::Identify(GDALOpenInfo *poOpenInfo)
792 :
793 : {
794 : /* -------------------------------------------------------------------- */
795 : /* We assume the user selects the .ers file. */
796 : /* -------------------------------------------------------------------- */
797 53931 : CPLString osHeader((const char *)poOpenInfo->pabyHeader,
798 107856 : poOpenInfo->nHeaderBytes);
799 :
800 53928 : if (osHeader.ifind("Algorithm Begin") != std::string::npos)
801 : {
802 0 : CPLError(CE_Failure, CPLE_OpenFailed,
803 : "%s appears to be an algorithm ERS file, which is not "
804 : "currently supported.",
805 : poOpenInfo->pszFilename);
806 0 : return FALSE;
807 : }
808 :
809 53924 : if (osHeader.ifind("DatasetHeader ") != std::string::npos)
810 89 : return TRUE;
811 :
812 53836 : return FALSE;
813 : }
814 :
815 : /************************************************************************/
816 : /* ERSProxyRasterBand */
817 : /************************************************************************/
818 :
819 : namespace
820 : {
821 :
822 63 : static int &GetRecLevel()
823 : {
824 : static thread_local int nRecLevel = 0;
825 63 : return nRecLevel;
826 : }
827 :
828 : class ERSProxyRasterBand final : public GDALProxyRasterBand
829 : {
830 : public:
831 1 : explicit ERSProxyRasterBand(GDALRasterBand *poUnderlyingBand)
832 1 : : m_poUnderlyingBand(poUnderlyingBand)
833 : {
834 1 : poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
835 1 : eDataType = poUnderlyingBand->GetRasterDataType();
836 1 : }
837 :
838 : int GetOverviewCount() override;
839 :
840 : protected:
841 1 : GDALRasterBand *RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
842 : {
843 1 : return m_poUnderlyingBand;
844 : }
845 :
846 : private:
847 : GDALRasterBand *m_poUnderlyingBand;
848 : };
849 :
850 0 : int ERSProxyRasterBand::GetOverviewCount()
851 : {
852 0 : int &nRecLevel = GetRecLevel();
853 0 : nRecLevel++;
854 0 : int nRet = GDALProxyRasterBand::GetOverviewCount();
855 0 : nRecLevel--;
856 0 : return nRet;
857 : }
858 :
859 : } // namespace
860 :
861 : /************************************************************************/
862 : /* Open() */
863 : /************************************************************************/
864 :
865 63 : GDALDataset *ERSDataset::Open(GDALOpenInfo *poOpenInfo)
866 :
867 : {
868 63 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
869 0 : return nullptr;
870 :
871 63 : int &nRecLevel = GetRecLevel();
872 : // cppcheck-suppress knownConditionTrueFalse
873 63 : if (nRecLevel)
874 : {
875 1 : CPLError(CE_Failure, CPLE_AppDefined,
876 : "Attempt at recursively opening ERS dataset");
877 1 : return nullptr;
878 : }
879 :
880 : /* -------------------------------------------------------------------- */
881 : /* Ingest the file as a tree of header nodes. */
882 : /* -------------------------------------------------------------------- */
883 62 : ERSHdrNode *poHeader = new ERSHdrNode();
884 :
885 62 : if (!poHeader->ParseHeader(poOpenInfo->fpL))
886 : {
887 0 : delete poHeader;
888 0 : VSIFCloseL(poOpenInfo->fpL);
889 0 : poOpenInfo->fpL = nullptr;
890 0 : return nullptr;
891 : }
892 :
893 62 : VSIFCloseL(poOpenInfo->fpL);
894 62 : poOpenInfo->fpL = nullptr;
895 :
896 : /* -------------------------------------------------------------------- */
897 : /* Do we have the minimum required information from this header? */
898 : /* -------------------------------------------------------------------- */
899 62 : if (poHeader->Find("RasterInfo.NrOfLines") == nullptr ||
900 124 : poHeader->Find("RasterInfo.NrOfCellsPerLine") == nullptr ||
901 62 : poHeader->Find("RasterInfo.NrOfBands") == nullptr)
902 : {
903 0 : if (poHeader->FindNode("Algorithm") != nullptr)
904 : {
905 0 : CPLError(CE_Failure, CPLE_OpenFailed,
906 : "%s appears to be an algorithm ERS file, which is not "
907 : "currently supported.",
908 : poOpenInfo->pszFilename);
909 : }
910 0 : delete poHeader;
911 0 : return nullptr;
912 : }
913 :
914 : /* -------------------------------------------------------------------- */
915 : /* Create a corresponding GDALDataset. */
916 : /* -------------------------------------------------------------------- */
917 124 : auto poDS = std::make_unique<ERSDataset>();
918 62 : poDS->poHeader = poHeader;
919 62 : poDS->eAccess = poOpenInfo->eAccess;
920 :
921 : /* -------------------------------------------------------------------- */
922 : /* Capture some information from the file that is of interest. */
923 : /* -------------------------------------------------------------------- */
924 62 : int nBands = atoi(poHeader->Find("RasterInfo.NrOfBands"));
925 62 : poDS->nRasterXSize = atoi(poHeader->Find("RasterInfo.NrOfCellsPerLine"));
926 62 : poDS->nRasterYSize = atoi(poHeader->Find("RasterInfo.NrOfLines"));
927 :
928 124 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
929 62 : !GDALCheckBandCount(nBands, FALSE))
930 : {
931 0 : return nullptr;
932 : }
933 :
934 : /* -------------------------------------------------------------------- */
935 : /* Get the HeaderOffset if it exists in the header */
936 : /* -------------------------------------------------------------------- */
937 62 : GIntBig nHeaderOffset = 0;
938 62 : const char *pszHeaderOffset = poHeader->Find("HeaderOffset");
939 62 : if (pszHeaderOffset != nullptr)
940 : {
941 2 : nHeaderOffset = CPLAtoGIntBig(pszHeaderOffset);
942 2 : if (nHeaderOffset < 0)
943 : {
944 0 : CPLError(CE_Failure, CPLE_AppDefined,
945 : "Illegal value for HeaderOffset: %s", pszHeaderOffset);
946 0 : return nullptr;
947 : }
948 : }
949 :
950 : /* -------------------------------------------------------------------- */
951 : /* Establish the data type. */
952 : /* -------------------------------------------------------------------- */
953 : CPLString osCellType =
954 124 : poHeader->Find("RasterInfo.CellType", "Unsigned8BitInteger");
955 : GDALDataType eType;
956 62 : if (EQUAL(osCellType, "Unsigned8BitInteger"))
957 29 : eType = GDT_Byte;
958 33 : else if (EQUAL(osCellType, "Signed8BitInteger"))
959 6 : eType = GDT_Int8;
960 27 : else if (EQUAL(osCellType, "Unsigned16BitInteger"))
961 3 : eType = GDT_UInt16;
962 24 : else if (EQUAL(osCellType, "Signed16BitInteger"))
963 6 : eType = GDT_Int16;
964 18 : else if (EQUAL(osCellType, "Unsigned32BitInteger"))
965 3 : eType = GDT_UInt32;
966 15 : else if (EQUAL(osCellType, "Signed32BitInteger"))
967 3 : eType = GDT_Int32;
968 12 : else if (EQUAL(osCellType, "IEEE4ByteReal"))
969 9 : eType = GDT_Float32;
970 3 : else if (EQUAL(osCellType, "IEEE8ByteReal"))
971 3 : eType = GDT_Float64;
972 : else
973 : {
974 0 : CPLDebug("ERS", "Unknown CellType '%s'", osCellType.c_str());
975 0 : eType = GDT_Byte;
976 : }
977 :
978 : /* -------------------------------------------------------------------- */
979 : /* Pick up the word order. */
980 : /* -------------------------------------------------------------------- */
981 : const int bNative =
982 : #ifdef CPL_LSB
983 62 : EQUAL(poHeader->Find("ByteOrder", "LSBFirst"), "LSBFirst")
984 : #else
985 : EQUAL(poHeader->Find("ByteOrder", "MSBFirst"), "MSBFirst")
986 : #endif
987 : ;
988 : /* -------------------------------------------------------------------- */
989 : /* Figure out the name of the target file. */
990 : /* -------------------------------------------------------------------- */
991 124 : CPLString osPath = CPLGetPath(poOpenInfo->pszFilename);
992 124 : CPLString osDataFile = poHeader->Find("DataFile", "");
993 :
994 62 : if (osDataFile.length() == 0) // just strip off extension.
995 : {
996 61 : osDataFile = CPLGetFilename(poOpenInfo->pszFilename);
997 61 : osDataFile = osDataFile.substr(0, osDataFile.find_last_of('.'));
998 : }
999 :
1000 124 : CPLString osDataFilePath = CPLFormFilename(osPath, osDataFile, nullptr);
1001 :
1002 : /* -------------------------------------------------------------------- */
1003 : /* DataSetType = Translated files are links to things like ecw */
1004 : /* files. */
1005 : /* -------------------------------------------------------------------- */
1006 62 : if (EQUAL(poHeader->Find("DataSetType", ""), "Translated"))
1007 : {
1008 2 : nRecLevel++;
1009 2 : poDS->poDepFile = GDALDataset::FromHandle(
1010 : GDALOpen(osDataFilePath, poOpenInfo->eAccess));
1011 2 : nRecLevel--;
1012 :
1013 2 : if (poDS->poDepFile != nullptr &&
1014 1 : poDS->poDepFile->GetRasterXSize() == poDS->GetRasterXSize() &&
1015 4 : poDS->poDepFile->GetRasterYSize() == poDS->GetRasterYSize() &&
1016 1 : poDS->poDepFile->GetRasterCount() >= nBands)
1017 : {
1018 2 : for (int iBand = 0; iBand < nBands; iBand++)
1019 : {
1020 : // Assume pixel interleaved.
1021 2 : poDS->SetBand(iBand + 1,
1022 : new ERSProxyRasterBand(
1023 1 : poDS->poDepFile->GetRasterBand(iBand + 1)));
1024 : }
1025 : }
1026 : else
1027 : {
1028 1 : delete poDS->poDepFile;
1029 1 : poDS->poDepFile = nullptr;
1030 : }
1031 : }
1032 :
1033 : /* ==================================================================== */
1034 : /* While ERStorage indicates a raw file. */
1035 : /* ==================================================================== */
1036 60 : else if (EQUAL(poHeader->Find("DataSetType", ""), "ERStorage"))
1037 : {
1038 : // Open data file.
1039 60 : if (poOpenInfo->eAccess == GA_Update)
1040 38 : poDS->fpImage = VSIFOpenL(osDataFilePath, "r+");
1041 : else
1042 22 : poDS->fpImage = VSIFOpenL(osDataFilePath, "r");
1043 :
1044 60 : poDS->osRawFilename = std::move(osDataFilePath);
1045 :
1046 60 : if (poDS->fpImage != nullptr && nBands > 0)
1047 : {
1048 60 : int iWordSize = GDALGetDataTypeSizeBytes(eType);
1049 :
1050 60 : const auto knIntMax = std::numeric_limits<int>::max();
1051 120 : if (nBands > knIntMax / iWordSize ||
1052 60 : poDS->nRasterXSize > knIntMax / (nBands * iWordSize))
1053 : {
1054 0 : CPLError(CE_Failure, CPLE_AppDefined,
1055 : "int overflow: too large nBands and/or nRasterXSize");
1056 0 : return nullptr;
1057 : }
1058 :
1059 60 : if (!RAWDatasetCheckMemoryUsage(
1060 60 : poDS->nRasterXSize, poDS->nRasterYSize, nBands, iWordSize,
1061 60 : iWordSize, iWordSize * nBands * poDS->nRasterXSize,
1062 : nHeaderOffset,
1063 60 : static_cast<vsi_l_offset>(iWordSize) * poDS->nRasterXSize,
1064 60 : poDS->fpImage))
1065 : {
1066 0 : return nullptr;
1067 : }
1068 60 : if (nHeaderOffset > std::numeric_limits<GIntBig>::max() -
1069 60 : static_cast<GIntBig>(nBands - 1) *
1070 60 : iWordSize * poDS->nRasterXSize)
1071 : {
1072 0 : CPLError(CE_Failure, CPLE_AppDefined,
1073 : "int overflow: too large nHeaderOffset");
1074 0 : return nullptr;
1075 : }
1076 :
1077 162 : for (int iBand = 0; iBand < nBands; iBand++)
1078 : {
1079 : // Assume pixel interleaved.
1080 : auto poBand = std::make_unique<ERSRasterBand>(
1081 102 : poDS.get(), iBand + 1, poDS->fpImage,
1082 0 : nHeaderOffset + static_cast<vsi_l_offset>(iWordSize) *
1083 102 : iBand * poDS->nRasterXSize,
1084 102 : iWordSize, iWordSize * nBands * poDS->nRasterXSize, eType,
1085 102 : bNative);
1086 102 : if (!poBand->IsValid())
1087 0 : return nullptr;
1088 102 : poDS->SetBand(iBand + 1, std::move(poBand));
1089 : }
1090 : }
1091 : }
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* Otherwise we have an error! */
1095 : /* -------------------------------------------------------------------- */
1096 62 : if (poDS->nBands == 0)
1097 : {
1098 1 : return nullptr;
1099 : }
1100 :
1101 : /* -------------------------------------------------------------------- */
1102 : /* Look for band descriptions. */
1103 : /* -------------------------------------------------------------------- */
1104 61 : ERSHdrNode *poRI = poHeader->FindNode("RasterInfo");
1105 :
1106 357 : for (int iChild = 0, iBand = 0;
1107 357 : poRI != nullptr && iChild < poRI->nItemCount && iBand < poDS->nBands;
1108 : iChild++)
1109 : {
1110 296 : if (poRI->papoItemChild[iChild] != nullptr &&
1111 33 : EQUAL(poRI->papszItemName[iChild], "BandId"))
1112 : {
1113 : const char *pszValue =
1114 12 : poRI->papoItemChild[iChild]->Find("Value", nullptr);
1115 :
1116 12 : iBand++;
1117 12 : if (pszValue)
1118 : {
1119 12 : CPLPushErrorHandler(CPLQuietErrorHandler);
1120 12 : poDS->GetRasterBand(iBand)->SetDescription(pszValue);
1121 12 : CPLPopErrorHandler();
1122 : }
1123 :
1124 12 : pszValue = poRI->papoItemChild[iChild]->Find("Units", nullptr);
1125 12 : if (pszValue)
1126 : {
1127 4 : CPLPushErrorHandler(CPLQuietErrorHandler);
1128 4 : poDS->GetRasterBand(iBand)->SetUnitType(pszValue);
1129 4 : CPLPopErrorHandler();
1130 : }
1131 : }
1132 : }
1133 :
1134 : /* -------------------------------------------------------------------- */
1135 : /* Look for projection. */
1136 : /* -------------------------------------------------------------------- */
1137 61 : poDS->osProj = poHeader->Find("CoordinateSpace.Projection", "");
1138 61 : poDS->osDatum = poHeader->Find("CoordinateSpace.Datum", "");
1139 61 : poDS->osUnits = poHeader->Find("CoordinateSpace.Units", "");
1140 :
1141 219 : poDS->m_oSRS.importFromERM(
1142 61 : !poDS->osProj.empty() ? poDS->osProj.c_str() : "RAW",
1143 61 : !poDS->osDatum.empty() ? poDS->osDatum.c_str() : "WGS84",
1144 61 : !poDS->osUnits.empty() ? poDS->osUnits.c_str() : "METERS");
1145 :
1146 : /* -------------------------------------------------------------------- */
1147 : /* Look for the geotransform. */
1148 : /* -------------------------------------------------------------------- */
1149 61 : if (poHeader->Find("RasterInfo.RegistrationCoord.Eastings", nullptr))
1150 : {
1151 6 : poDS->bGotTransform = TRUE;
1152 6 : poDS->adfGeoTransform[0] = CPLAtof(
1153 : poHeader->Find("RasterInfo.RegistrationCoord.Eastings", ""));
1154 12 : poDS->adfGeoTransform[1] =
1155 6 : CPLAtof(poHeader->Find("RasterInfo.CellInfo.Xdimension", "1.0"));
1156 6 : poDS->adfGeoTransform[2] = 0.0;
1157 6 : poDS->adfGeoTransform[3] = CPLAtof(
1158 : poHeader->Find("RasterInfo.RegistrationCoord.Northings", ""));
1159 6 : poDS->adfGeoTransform[4] = 0.0;
1160 6 : poDS->adfGeoTransform[5] =
1161 6 : -CPLAtof(poHeader->Find("RasterInfo.CellInfo.Ydimension", "1.0"));
1162 : }
1163 60 : else if (poHeader->Find("RasterInfo.RegistrationCoord.Latitude", nullptr) &&
1164 5 : poHeader->Find("RasterInfo.CellInfo.Xdimension", nullptr))
1165 : {
1166 5 : poDS->bGotTransform = TRUE;
1167 5 : poDS->adfGeoTransform[0] = ERSDMS2Dec(
1168 : poHeader->Find("RasterInfo.RegistrationCoord.Longitude", ""));
1169 10 : poDS->adfGeoTransform[1] =
1170 5 : CPLAtof(poHeader->Find("RasterInfo.CellInfo.Xdimension", ""));
1171 5 : poDS->adfGeoTransform[2] = 0.0;
1172 5 : poDS->adfGeoTransform[3] = ERSDMS2Dec(
1173 : poHeader->Find("RasterInfo.RegistrationCoord.Latitude", ""));
1174 5 : poDS->adfGeoTransform[4] = 0.0;
1175 5 : poDS->adfGeoTransform[5] =
1176 5 : -CPLAtof(poHeader->Find("RasterInfo.CellInfo.Ydimension", ""));
1177 : }
1178 :
1179 : /* -------------------------------------------------------------------- */
1180 : /* Adjust if we have a registration cell. */
1181 : /* -------------------------------------------------------------------- */
1182 :
1183 : /* http://geospatial.intergraph.com/Libraries/Tech_Docs/ERDAS_ER_Mapper_Customization_Guide.sflb.ashx
1184 : */
1185 : /* Page 27 : */
1186 : /* RegistrationCellX and RegistrationCellY : The image X and Y
1187 : coordinates of the cell which corresponds to the Registration
1188 : Coordinate. Note that the RegistrationCellX and
1189 : RegistrationCellY can be fractional values. If
1190 : RegistrationCellX and RegistrationCellY are not specified,
1191 : they are assumed to be (0,0), which is the top left corner of the
1192 : image.
1193 : */
1194 : double dfCellX =
1195 61 : CPLAtof(poHeader->Find("RasterInfo.RegistrationCellX", "0"));
1196 : double dfCellY =
1197 61 : CPLAtof(poHeader->Find("RasterInfo.RegistrationCellY", "0"));
1198 :
1199 61 : if (poDS->bGotTransform)
1200 : {
1201 11 : poDS->adfGeoTransform[0] -= dfCellX * poDS->adfGeoTransform[1] +
1202 11 : dfCellY * poDS->adfGeoTransform[2];
1203 22 : poDS->adfGeoTransform[3] -= dfCellX * poDS->adfGeoTransform[4] +
1204 11 : dfCellY * poDS->adfGeoTransform[5];
1205 : }
1206 :
1207 : /* -------------------------------------------------------------------- */
1208 : /* Check for null values. */
1209 : /* -------------------------------------------------------------------- */
1210 61 : if (poHeader->Find("RasterInfo.NullCellValue", nullptr))
1211 : {
1212 8 : poDS->bHasNoDataValue = TRUE;
1213 16 : poDS->dfNoDataValue =
1214 8 : CPLAtofM(poHeader->Find("RasterInfo.NullCellValue"));
1215 :
1216 8 : if (poDS->poDepFile != nullptr)
1217 : {
1218 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
1219 :
1220 0 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1221 0 : poDS->GetRasterBand(iBand)->SetNoDataValue(poDS->dfNoDataValue);
1222 :
1223 0 : CPLPopErrorHandler();
1224 : }
1225 : }
1226 :
1227 : /* -------------------------------------------------------------------- */
1228 : /* Do we have an "All" region? */
1229 : /* -------------------------------------------------------------------- */
1230 61 : ERSHdrNode *poAll = nullptr;
1231 :
1232 366 : for (int iChild = 0; poRI != nullptr && iChild < poRI->nItemCount; iChild++)
1233 : {
1234 305 : if (poRI->papoItemChild[iChild] != nullptr &&
1235 39 : EQUAL(poRI->papszItemName[iChild], "RegionInfo"))
1236 : {
1237 3 : if (EQUAL(poRI->papoItemChild[iChild]->Find("RegionName", ""),
1238 : "All"))
1239 3 : poAll = poRI->papoItemChild[iChild];
1240 : }
1241 : }
1242 :
1243 : /* -------------------------------------------------------------------- */
1244 : /* Do we have statistics? */
1245 : /* -------------------------------------------------------------------- */
1246 61 : if (poAll && poAll->FindNode("Stats"))
1247 : {
1248 3 : CPLPushErrorHandler(CPLQuietErrorHandler);
1249 :
1250 6 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1251 : {
1252 : const char *pszValue =
1253 3 : poAll->FindElem("Stats.MinimumValue", iBand - 1);
1254 :
1255 3 : if (pszValue)
1256 3 : poDS->GetRasterBand(iBand)->SetMetadataItem(
1257 3 : "STATISTICS_MINIMUM", pszValue);
1258 :
1259 3 : pszValue = poAll->FindElem("Stats.MaximumValue", iBand - 1);
1260 :
1261 3 : if (pszValue)
1262 3 : poDS->GetRasterBand(iBand)->SetMetadataItem(
1263 3 : "STATISTICS_MAXIMUM", pszValue);
1264 :
1265 3 : pszValue = poAll->FindElem("Stats.MeanValue", iBand - 1);
1266 :
1267 3 : if (pszValue)
1268 3 : poDS->GetRasterBand(iBand)->SetMetadataItem("STATISTICS_MEAN",
1269 3 : pszValue);
1270 :
1271 3 : pszValue = poAll->FindElem("Stats.MedianValue", iBand - 1);
1272 :
1273 3 : if (pszValue)
1274 3 : poDS->GetRasterBand(iBand)->SetMetadataItem("STATISTICS_MEDIAN",
1275 3 : pszValue);
1276 : }
1277 :
1278 3 : CPLPopErrorHandler();
1279 : }
1280 :
1281 : /* -------------------------------------------------------------------- */
1282 : /* Do we have GCPs. */
1283 : /* -------------------------------------------------------------------- */
1284 61 : if (poHeader->FindNode("RasterInfo.WarpControl"))
1285 2 : poDS->ReadGCPs();
1286 :
1287 : /* -------------------------------------------------------------------- */
1288 : /* Initialize any PAM information. */
1289 : /* -------------------------------------------------------------------- */
1290 61 : poDS->SetDescription(poOpenInfo->pszFilename);
1291 61 : poDS->TryLoadXML();
1292 :
1293 : // if no SR in xml, try aux
1294 61 : const OGRSpatialReference *poSRS = poDS->GDALPamDataset::GetSpatialRef();
1295 61 : if (poSRS == nullptr)
1296 : {
1297 : // try aux
1298 : auto poAuxDS = std::unique_ptr<GDALDataset>(GDALFindAssociatedAuxFile(
1299 122 : poOpenInfo->pszFilename, GA_ReadOnly, poDS.get()));
1300 61 : if (poAuxDS)
1301 : {
1302 0 : poSRS = poAuxDS->GetSpatialRef();
1303 0 : if (poSRS)
1304 : {
1305 0 : poDS->m_oSRS = *poSRS;
1306 : }
1307 : }
1308 : }
1309 : /* -------------------------------------------------------------------- */
1310 : /* Check for overviews. */
1311 : /* -------------------------------------------------------------------- */
1312 61 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
1313 :
1314 61 : return poDS.release();
1315 : }
1316 :
1317 : /************************************************************************/
1318 : /* Create() */
1319 : /************************************************************************/
1320 :
1321 67 : GDALDataset *ERSDataset::Create(const char *pszFilename, int nXSize, int nYSize,
1322 : int nBandsIn, GDALDataType eType,
1323 : char **papszOptions)
1324 :
1325 : {
1326 : /* -------------------------------------------------------------------- */
1327 : /* Verify settings. */
1328 : /* -------------------------------------------------------------------- */
1329 67 : if (nBandsIn <= 0)
1330 : {
1331 1 : CPLError(CE_Failure, CPLE_NotSupported,
1332 : "ERS driver does not support %d bands.\n", nBandsIn);
1333 1 : return nullptr;
1334 : }
1335 :
1336 66 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_Int16 &&
1337 29 : eType != GDT_UInt16 && eType != GDT_Int32 && eType != GDT_UInt32 &&
1338 19 : eType != GDT_Float32 && eType != GDT_Float64)
1339 : {
1340 16 : CPLError(
1341 : CE_Failure, CPLE_AppDefined,
1342 : "The ERS driver does not supporting creating files of types %s.",
1343 : GDALGetDataTypeName(eType));
1344 16 : return nullptr;
1345 : }
1346 :
1347 : /* -------------------------------------------------------------------- */
1348 : /* Work out the name we want to use for the .ers and binary */
1349 : /* data files. */
1350 : /* -------------------------------------------------------------------- */
1351 100 : CPLString osBinFile, osErsFile;
1352 :
1353 50 : if (EQUAL(CPLGetExtension(pszFilename), "ers"))
1354 : {
1355 7 : osErsFile = pszFilename;
1356 7 : osBinFile = osErsFile.substr(0, osErsFile.length() - 4);
1357 : }
1358 : else
1359 : {
1360 43 : osBinFile = pszFilename;
1361 43 : osErsFile = osBinFile + ".ers";
1362 : }
1363 :
1364 : /* -------------------------------------------------------------------- */
1365 : /* Work out some values we will write. */
1366 : /* -------------------------------------------------------------------- */
1367 50 : const char *pszCellType = "Unsigned8BitInteger";
1368 :
1369 50 : if (eType == GDT_Byte)
1370 28 : pszCellType = "Unsigned8BitInteger";
1371 22 : else if (eType == GDT_Int8)
1372 3 : pszCellType = "Signed8BitInteger";
1373 19 : else if (eType == GDT_Int16)
1374 3 : pszCellType = "Signed16BitInteger";
1375 16 : else if (eType == GDT_UInt16)
1376 3 : pszCellType = "Unsigned16BitInteger";
1377 13 : else if (eType == GDT_Int32)
1378 3 : pszCellType = "Signed32BitInteger";
1379 10 : else if (eType == GDT_UInt32)
1380 3 : pszCellType = "Unsigned32BitInteger";
1381 7 : else if (eType == GDT_Float32)
1382 4 : pszCellType = "IEEE4ByteReal";
1383 3 : else if (eType == GDT_Float64)
1384 3 : pszCellType = "IEEE8ByteReal";
1385 : else
1386 : {
1387 0 : CPLAssert(false);
1388 : }
1389 :
1390 : /* -------------------------------------------------------------------- */
1391 : /* Handling for signed eight bit data. */
1392 : /* -------------------------------------------------------------------- */
1393 50 : const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
1394 50 : if (pszPixelType && EQUAL(pszPixelType, "SIGNEDBYTE") && eType == GDT_Byte)
1395 0 : pszCellType = "Signed8BitInteger";
1396 :
1397 : /* -------------------------------------------------------------------- */
1398 : /* Write binary file. */
1399 : /* -------------------------------------------------------------------- */
1400 50 : VSILFILE *fpBin = VSIFOpenL(osBinFile, "w");
1401 :
1402 50 : if (fpBin == nullptr)
1403 : {
1404 3 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
1405 3 : osBinFile.c_str(), VSIStrerror(errno));
1406 3 : return nullptr;
1407 : }
1408 :
1409 : GUIntBig nSize =
1410 47 : nXSize * (GUIntBig)nYSize * nBandsIn * (GDALGetDataTypeSize(eType) / 8);
1411 47 : GByte byZero = 0;
1412 94 : if (VSIFSeekL(fpBin, nSize - 1, SEEK_SET) != 0 ||
1413 47 : VSIFWriteL(&byZero, 1, 1, fpBin) != 1)
1414 : {
1415 10 : CPLError(CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s",
1416 10 : osBinFile.c_str(), VSIStrerror(errno));
1417 10 : VSIFCloseL(fpBin);
1418 10 : return nullptr;
1419 : }
1420 37 : VSIFCloseL(fpBin);
1421 :
1422 : /* -------------------------------------------------------------------- */
1423 : /* Try writing header file. */
1424 : /* -------------------------------------------------------------------- */
1425 37 : VSILFILE *fpERS = VSIFOpenL(osErsFile, "w");
1426 :
1427 37 : if (fpERS == nullptr)
1428 : {
1429 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
1430 0 : osErsFile.c_str(), VSIStrerror(errno));
1431 0 : return nullptr;
1432 : }
1433 :
1434 37 : VSIFPrintfL(fpERS, "DatasetHeader Begin\n");
1435 37 : VSIFPrintfL(fpERS, "\tVersion\t\t = \"6.0\"\n");
1436 37 : VSIFPrintfL(fpERS, "\tName\t\t= \"%s\"\n", CPLGetFilename(osErsFile));
1437 :
1438 : // Last updated requires timezone info which we don't necessarily get
1439 : // get from VSICTime() so perhaps it is better to omit this.
1440 : // VSIFPrintfL( fpERS, "\tLastUpdated\t= %s",
1441 : // VSICTime( VSITime( NULL ) ) );
1442 :
1443 37 : VSIFPrintfL(fpERS, "\tDataSetType\t= ERStorage\n");
1444 37 : VSIFPrintfL(fpERS, "\tDataType\t= Raster\n");
1445 37 : VSIFPrintfL(fpERS, "\tByteOrder\t= LSBFirst\n");
1446 37 : VSIFPrintfL(fpERS, "\tRasterInfo Begin\n");
1447 37 : VSIFPrintfL(fpERS, "\t\tCellType\t= %s\n", pszCellType);
1448 37 : VSIFPrintfL(fpERS, "\t\tNrOfLines\t= %d\n", nYSize);
1449 37 : VSIFPrintfL(fpERS, "\t\tNrOfCellsPerLine\t= %d\n", nXSize);
1450 37 : VSIFPrintfL(fpERS, "\t\tNrOfBands\t= %d\n", nBandsIn);
1451 37 : VSIFPrintfL(fpERS, "\tRasterInfo End\n");
1452 37 : if (VSIFPrintfL(fpERS, "DatasetHeader End\n") < 17)
1453 : {
1454 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s",
1455 0 : osErsFile.c_str(), VSIStrerror(errno));
1456 0 : return nullptr;
1457 : }
1458 :
1459 37 : VSIFCloseL(fpERS);
1460 :
1461 : /* -------------------------------------------------------------------- */
1462 : /* Reopen. */
1463 : /* -------------------------------------------------------------------- */
1464 74 : GDALOpenInfo oOpenInfo(osErsFile, GA_Update);
1465 37 : ERSDataset *poDS = (ERSDataset *)Open(&oOpenInfo);
1466 37 : if (poDS == nullptr)
1467 0 : return nullptr;
1468 :
1469 : /* -------------------------------------------------------------------- */
1470 : /* Fetch DATUM, PROJ and UNITS creation option */
1471 : /* -------------------------------------------------------------------- */
1472 37 : const char *pszDatum = CSLFetchNameValue(papszOptions, "DATUM");
1473 37 : if (pszDatum)
1474 : {
1475 2 : poDS->osDatumForced = pszDatum;
1476 2 : poDS->osDatum = pszDatum;
1477 : }
1478 37 : const char *pszProj = CSLFetchNameValue(papszOptions, "PROJ");
1479 37 : if (pszProj)
1480 : {
1481 2 : poDS->osProjForced = pszProj;
1482 2 : poDS->osProj = pszProj;
1483 : }
1484 37 : const char *pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
1485 37 : if (pszUnits)
1486 : {
1487 2 : poDS->osUnitsForced = pszUnits;
1488 2 : poDS->osUnits = pszUnits;
1489 : }
1490 :
1491 37 : if (pszDatum || pszProj || pszUnits)
1492 : {
1493 2 : poDS->WriteProjectionInfo(pszProj ? pszProj : "RAW",
1494 : pszDatum ? pszDatum : "RAW",
1495 : pszUnits ? pszUnits : "METERS");
1496 : }
1497 :
1498 37 : return poDS;
1499 : }
1500 :
1501 : /************************************************************************/
1502 : /* GDALRegister_ERS() */
1503 : /************************************************************************/
1504 :
1505 1595 : void GDALRegister_ERS()
1506 :
1507 : {
1508 1595 : if (GDALGetDriverByName("ERS") != nullptr)
1509 302 : return;
1510 :
1511 1293 : GDALDriver *poDriver = new GDALDriver();
1512 :
1513 1293 : poDriver->SetDescription("ERS");
1514 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1515 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ERMapper .ers Labelled");
1516 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ers.html");
1517 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ers");
1518 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1519 : "Byte Int8 Int16 UInt16 Int32 UInt32 "
1520 1293 : "Float32 Float64");
1521 :
1522 1293 : poDriver->SetMetadataItem(
1523 : GDAL_DMD_CREATIONOPTIONLIST,
1524 : "<CreationOptionList>"
1525 : " <Option name='PIXELTYPE' type='string' description='(deprecated, "
1526 : "use Int8 datatype) By setting this to SIGNEDBYTE, a new Byte file can "
1527 : "be forced to be written as signed byte'/>"
1528 : " <Option name='PROJ' type='string' description='ERS Projection "
1529 : "Name'/>"
1530 : " <Option name='DATUM' type='string' description='ERS Datum Name' />"
1531 : " <Option name='UNITS' type='string-select' description='ERS "
1532 : "Projection Units'>"
1533 : " <Value>METERS</Value>"
1534 : " <Value>FEET</Value>"
1535 : " </Option>"
1536 1293 : "</CreationOptionList>");
1537 :
1538 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1539 :
1540 1293 : poDriver->pfnOpen = ERSDataset::Open;
1541 1293 : poDriver->pfnIdentify = ERSDataset::Identify;
1542 1293 : poDriver->pfnCreate = ERSDataset::Create;
1543 :
1544 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1545 : }
|