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