Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GView
4 : * Purpose: Implementation of Atlantis MFF Support
5 : * Author: Frank Warmerdam, warmerda@home.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam
9 : * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "atlsci_spheroid.h"
15 : #include "cpl_string.h"
16 : #include "gdal_frmts.h"
17 : #include "ogr_spatialref.h"
18 : #include "rawdataset.h"
19 :
20 : #include <cctype>
21 : #include <cmath>
22 : #include <algorithm>
23 :
24 : enum
25 : {
26 : MFFPRJ_NONE,
27 : MFFPRJ_LL,
28 : MFFPRJ_UTM,
29 : MFFPRJ_UNRECOGNIZED
30 : };
31 :
32 : /************************************************************************/
33 : /* ==================================================================== */
34 : /* MFFDataset */
35 : /* ==================================================================== */
36 : /************************************************************************/
37 :
38 : class MFFDataset final : public RawDataset
39 : {
40 : int nGCPCount;
41 : GDAL_GCP *pasGCPList;
42 :
43 : OGRSpatialReference m_oSRS{};
44 : OGRSpatialReference m_oGCPSRS{};
45 : double adfGeoTransform[6];
46 : char **m_papszFileList;
47 :
48 : void ScanForGCPs();
49 : void ScanForProjectionInfo();
50 :
51 : CPL_DISALLOW_COPY_ASSIGN(MFFDataset)
52 :
53 : CPLErr Close() override;
54 :
55 : public:
56 : MFFDataset();
57 : ~MFFDataset() override;
58 :
59 : char **papszHdrLines;
60 :
61 : VSILFILE **pafpBandFiles;
62 :
63 : char **GetFileList() override;
64 :
65 : int GetGCPCount() override;
66 :
67 0 : const OGRSpatialReference *GetGCPSpatialRef() const override
68 : {
69 0 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
70 : }
71 :
72 : const GDAL_GCP *GetGCPs() override;
73 :
74 0 : const OGRSpatialReference *GetSpatialRef() const override
75 : {
76 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
77 : }
78 :
79 : CPLErr GetGeoTransform(double *) override;
80 :
81 : static GDALDataset *Open(GDALOpenInfo *);
82 : };
83 :
84 : /************************************************************************/
85 : /* ==================================================================== */
86 : /* MFFTiledBand */
87 : /* ==================================================================== */
88 : /************************************************************************/
89 :
90 : class MFFTiledBand final : public GDALRasterBand
91 : {
92 : friend class MFFDataset;
93 :
94 : VSILFILE *fpRaw;
95 : RawRasterBand::ByteOrder eByteOrder;
96 :
97 : CPL_DISALLOW_COPY_ASSIGN(MFFTiledBand)
98 :
99 : public:
100 : MFFTiledBand(MFFDataset *, int, VSILFILE *, int, int, GDALDataType,
101 : RawRasterBand::ByteOrder);
102 : ~MFFTiledBand() override;
103 :
104 : CPLErr IReadBlock(int, int, void *) override;
105 : };
106 :
107 : /************************************************************************/
108 : /* MFFTiledBand() */
109 : /************************************************************************/
110 :
111 2 : MFFTiledBand::MFFTiledBand(MFFDataset *poDSIn, int nBandIn, VSILFILE *fp,
112 : int nTileXSize, int nTileYSize,
113 : GDALDataType eDataTypeIn,
114 2 : RawRasterBand::ByteOrder eByteOrderIn)
115 2 : : fpRaw(fp), eByteOrder(eByteOrderIn)
116 : {
117 2 : poDS = poDSIn;
118 2 : nBand = nBandIn;
119 :
120 2 : eDataType = eDataTypeIn;
121 :
122 2 : nBlockXSize = nTileXSize;
123 2 : nBlockYSize = nTileYSize;
124 2 : }
125 :
126 : /************************************************************************/
127 : /* ~MFFTiledBand() */
128 : /************************************************************************/
129 :
130 4 : MFFTiledBand::~MFFTiledBand()
131 :
132 : {
133 2 : if (VSIFCloseL(fpRaw) != 0)
134 : {
135 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
136 : }
137 4 : }
138 :
139 : /************************************************************************/
140 : /* IReadBlock() */
141 : /************************************************************************/
142 :
143 1 : CPLErr MFFTiledBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
144 :
145 : {
146 1 : const int nTilesPerRow = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
147 1 : const int nWordSize = GDALGetDataTypeSize(eDataType) / 8;
148 1 : const int nBlockSize = nWordSize * nBlockXSize * nBlockYSize;
149 :
150 1 : const vsi_l_offset nOffset =
151 1 : nBlockSize *
152 1 : (nBlockXOff + static_cast<vsi_l_offset>(nBlockYOff) * nTilesPerRow);
153 :
154 2 : if (VSIFSeekL(fpRaw, nOffset, SEEK_SET) == -1 ||
155 1 : VSIFReadL(pImage, 1, nBlockSize, fpRaw) < 1)
156 : {
157 0 : CPLError(CE_Failure, CPLE_FileIO,
158 : "Read of tile %d/%d failed with fseek or fread error.",
159 : nBlockXOff, nBlockYOff);
160 0 : return CE_Failure;
161 : }
162 :
163 1 : if (eByteOrder != RawRasterBand::NATIVE_BYTE_ORDER && nWordSize > 1)
164 : {
165 0 : if (GDALDataTypeIsComplex(eDataType))
166 : {
167 0 : GDALSwapWords(pImage, nWordSize / 2, nBlockXSize * nBlockYSize,
168 : nWordSize);
169 0 : GDALSwapWords(reinterpret_cast<GByte *>(pImage) + nWordSize / 2,
170 0 : nWordSize / 2, nBlockXSize * nBlockYSize, nWordSize);
171 : }
172 : else
173 0 : GDALSwapWords(pImage, nWordSize, nBlockXSize * nBlockYSize,
174 : nWordSize);
175 : }
176 :
177 1 : return CE_None;
178 : }
179 :
180 : /************************************************************************/
181 : /* MFF Spheroids */
182 : /************************************************************************/
183 :
184 : class MFFSpheroidList : public SpheroidList
185 : {
186 : public:
187 : MFFSpheroidList();
188 :
189 6 : ~MFFSpheroidList()
190 6 : {
191 6 : }
192 : };
193 :
194 6 : MFFSpheroidList ::MFFSpheroidList()
195 : {
196 6 : num_spheroids = 18;
197 :
198 6 : epsilonR = 0.1;
199 6 : epsilonI = 0.000001;
200 :
201 6 : spheroids[0].SetValuesByRadii("SPHERE", 6371007.0, 6371007.0);
202 6 : spheroids[1].SetValuesByRadii("EVEREST", 6377304.0, 6356103.0);
203 6 : spheroids[2].SetValuesByRadii("BESSEL", 6377397.0, 6356082.0);
204 6 : spheroids[3].SetValuesByRadii("AIRY", 6377563.0, 6356300.0);
205 6 : spheroids[4].SetValuesByRadii("CLARKE_1858", 6378294.0, 6356621.0);
206 6 : spheroids[5].SetValuesByRadii("CLARKE_1866", 6378206.4, 6356583.8);
207 6 : spheroids[6].SetValuesByRadii("CLARKE_1880", 6378249.0, 6356517.0);
208 6 : spheroids[7].SetValuesByRadii("HAYFORD", 6378388.0, 6356915.0);
209 6 : spheroids[8].SetValuesByRadii("KRASOVSKI", 6378245.0, 6356863.0);
210 6 : spheroids[9].SetValuesByRadii("HOUGH", 6378270.0, 6356794.0);
211 6 : spheroids[10].SetValuesByRadii("FISHER_60", 6378166.0, 6356784.0);
212 6 : spheroids[11].SetValuesByRadii("KAULA", 6378165.0, 6356345.0);
213 6 : spheroids[12].SetValuesByRadii("IUGG_67", 6378160.0, 6356775.0);
214 6 : spheroids[13].SetValuesByRadii("FISHER_68", 6378150.0, 6356330.0);
215 6 : spheroids[14].SetValuesByRadii("WGS_72", 6378135.0, 6356751.0);
216 6 : spheroids[15].SetValuesByRadii("IUGG_75", 6378140.0, 6356755.0);
217 6 : spheroids[16].SetValuesByRadii("WGS_84", 6378137.0, 6356752.0);
218 6 : spheroids[17].SetValuesByRadii("HUGHES", 6378273.0, 6356889.4);
219 6 : }
220 :
221 : /************************************************************************/
222 : /* ==================================================================== */
223 : /* MFFDataset */
224 : /* ==================================================================== */
225 : /************************************************************************/
226 :
227 : /************************************************************************/
228 : /* MFFDataset() */
229 : /************************************************************************/
230 :
231 7 : MFFDataset::MFFDataset()
232 : : nGCPCount(0), pasGCPList(nullptr), m_papszFileList(nullptr),
233 7 : papszHdrLines(nullptr), pafpBandFiles(nullptr)
234 : {
235 7 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
236 7 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
237 7 : adfGeoTransform[0] = 0.0;
238 7 : adfGeoTransform[1] = 1.0;
239 7 : adfGeoTransform[2] = 0.0;
240 7 : adfGeoTransform[3] = 0.0;
241 7 : adfGeoTransform[4] = 0.0;
242 7 : adfGeoTransform[5] = 1.0;
243 7 : }
244 :
245 : /************************************************************************/
246 : /* ~MFFDataset() */
247 : /************************************************************************/
248 :
249 14 : MFFDataset::~MFFDataset()
250 :
251 : {
252 7 : MFFDataset::Close();
253 14 : }
254 :
255 : /************************************************************************/
256 : /* Close() */
257 : /************************************************************************/
258 :
259 14 : CPLErr MFFDataset::Close()
260 : {
261 14 : CPLErr eErr = CE_None;
262 14 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
263 : {
264 7 : if (MFFDataset::FlushCache(true) != CE_None)
265 0 : eErr = CE_Failure;
266 :
267 7 : CSLDestroy(papszHdrLines);
268 7 : if (pafpBandFiles)
269 : {
270 0 : for (int i = 0; i < GetRasterCount(); i++)
271 : {
272 0 : if (pafpBandFiles[i])
273 : {
274 0 : if (VSIFCloseL(pafpBandFiles[i]) != 0)
275 : {
276 0 : eErr = CE_Failure;
277 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
278 : }
279 : }
280 : }
281 0 : CPLFree(pafpBandFiles);
282 : }
283 :
284 7 : if (nGCPCount > 0)
285 : {
286 6 : GDALDeinitGCPs(nGCPCount, pasGCPList);
287 : }
288 7 : CPLFree(pasGCPList);
289 7 : CSLDestroy(m_papszFileList);
290 :
291 7 : if (GDALPamDataset::Close() != CE_None)
292 0 : eErr = CE_Failure;
293 : }
294 14 : return eErr;
295 : }
296 :
297 : /************************************************************************/
298 : /* GetFileList() */
299 : /************************************************************************/
300 :
301 3 : char **MFFDataset::GetFileList()
302 : {
303 3 : char **papszFileList = RawDataset::GetFileList();
304 3 : papszFileList = CSLInsertStrings(papszFileList, -1, m_papszFileList);
305 3 : return papszFileList;
306 : }
307 :
308 : /************************************************************************/
309 : /* GetGCPCount() */
310 : /************************************************************************/
311 :
312 0 : int MFFDataset::GetGCPCount()
313 :
314 : {
315 0 : return nGCPCount;
316 : }
317 :
318 : /************************************************************************/
319 : /* GetGeoTransform() */
320 : /************************************************************************/
321 :
322 0 : CPLErr MFFDataset::GetGeoTransform(double *padfTransform)
323 :
324 : {
325 0 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
326 0 : return CE_None;
327 : }
328 :
329 : /************************************************************************/
330 : /* GetGCP() */
331 : /************************************************************************/
332 :
333 0 : const GDAL_GCP *MFFDataset::GetGCPs()
334 :
335 : {
336 0 : return pasGCPList;
337 : }
338 :
339 : /************************************************************************/
340 : /* ScanForGCPs() */
341 : /************************************************************************/
342 :
343 7 : void MFFDataset::ScanForGCPs()
344 :
345 : {
346 7 : int NUM_GCPS = 0;
347 :
348 7 : if (CSLFetchNameValue(papszHdrLines, "NUM_GCPS") != nullptr)
349 4 : NUM_GCPS = atoi(CSLFetchNameValue(papszHdrLines, "NUM_GCPS"));
350 7 : if (NUM_GCPS < 0)
351 0 : return;
352 :
353 7 : nGCPCount = 0;
354 7 : pasGCPList =
355 7 : static_cast<GDAL_GCP *>(VSICalloc(sizeof(GDAL_GCP), 5 + NUM_GCPS));
356 7 : if (pasGCPList == nullptr)
357 0 : return;
358 :
359 42 : for (int nCorner = 0; nCorner < 5; nCorner++)
360 : {
361 35 : const char *pszBase = nullptr;
362 35 : double dfRasterX = 0.0;
363 35 : double dfRasterY = 0.0;
364 :
365 35 : if (nCorner == 0)
366 : {
367 7 : dfRasterX = 0.5;
368 7 : dfRasterY = 0.5;
369 7 : pszBase = "TOP_LEFT_CORNER";
370 : }
371 28 : else if (nCorner == 1)
372 : {
373 7 : dfRasterX = GetRasterXSize() - 0.5;
374 7 : dfRasterY = 0.5;
375 7 : pszBase = "TOP_RIGHT_CORNER";
376 : }
377 21 : else if (nCorner == 2)
378 : {
379 7 : dfRasterX = GetRasterXSize() - 0.5;
380 7 : dfRasterY = GetRasterYSize() - 0.5;
381 7 : pszBase = "BOTTOM_RIGHT_CORNER";
382 : }
383 14 : else if (nCorner == 3)
384 : {
385 7 : dfRasterX = 0.5;
386 7 : dfRasterY = GetRasterYSize() - 0.5;
387 7 : pszBase = "BOTTOM_LEFT_CORNER";
388 : }
389 : else /* if( nCorner == 4 ) */
390 : {
391 7 : dfRasterX = GetRasterXSize() / 2.0;
392 7 : dfRasterY = GetRasterYSize() / 2.0;
393 7 : pszBase = "CENTRE";
394 : }
395 :
396 35 : char szLatName[40] = {'\0'};
397 35 : char szLongName[40] = {'\0'};
398 35 : snprintf(szLatName, sizeof(szLatName), "%s_LATITUDE", pszBase);
399 35 : snprintf(szLongName, sizeof(szLongName), "%s_LONGITUDE", pszBase);
400 :
401 45 : if (CSLFetchNameValue(papszHdrLines, szLatName) != nullptr &&
402 10 : CSLFetchNameValue(papszHdrLines, szLongName) != nullptr)
403 : {
404 10 : GDALInitGCPs(1, pasGCPList + nGCPCount);
405 :
406 10 : CPLFree(pasGCPList[nGCPCount].pszId);
407 :
408 10 : pasGCPList[nGCPCount].pszId = CPLStrdup(pszBase);
409 :
410 20 : pasGCPList[nGCPCount].dfGCPX =
411 10 : CPLAtof(CSLFetchNameValue(papszHdrLines, szLongName));
412 20 : pasGCPList[nGCPCount].dfGCPY =
413 10 : CPLAtof(CSLFetchNameValue(papszHdrLines, szLatName));
414 10 : pasGCPList[nGCPCount].dfGCPZ = 0.0;
415 :
416 10 : pasGCPList[nGCPCount].dfGCPPixel = dfRasterX;
417 10 : pasGCPList[nGCPCount].dfGCPLine = dfRasterY;
418 :
419 10 : nGCPCount++;
420 : }
421 : }
422 :
423 : /* -------------------------------------------------------------------- */
424 : /* Collect standalone GCPs. They look like: */
425 : /* */
426 : /* GCPn = row, col, lat, long */
427 : /* GCP1 = 1, 1, 45.0, -75.0 */
428 : /* -------------------------------------------------------------------- */
429 11 : for (int i = 0; i < NUM_GCPS; i++)
430 : {
431 4 : char szName[25] = {'\0'};
432 4 : snprintf(szName, sizeof(szName), "GCP%d", i + 1);
433 4 : if (CSLFetchNameValue(papszHdrLines, szName) == nullptr)
434 0 : continue;
435 :
436 4 : char **papszTokens = CSLTokenizeStringComplex(
437 4 : CSLFetchNameValue(papszHdrLines, szName), ",", FALSE, FALSE);
438 4 : if (CSLCount(papszTokens) == 4)
439 : {
440 4 : GDALInitGCPs(1, pasGCPList + nGCPCount);
441 :
442 4 : CPLFree(pasGCPList[nGCPCount].pszId);
443 4 : pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
444 :
445 4 : pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[3]);
446 4 : pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[2]);
447 4 : pasGCPList[nGCPCount].dfGCPZ = 0.0;
448 4 : pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[1]) + 0.5;
449 4 : pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[0]) + 0.5;
450 :
451 4 : nGCPCount++;
452 : }
453 :
454 4 : CSLDestroy(papszTokens);
455 : }
456 : }
457 :
458 : /************************************************************************/
459 : /* ScanForProjectionInfo */
460 : /************************************************************************/
461 :
462 7 : void MFFDataset::ScanForProjectionInfo()
463 : {
464 : const char *pszProjName =
465 7 : CSLFetchNameValue(papszHdrLines, "PROJECTION_NAME");
466 : const char *pszOriginLong =
467 7 : CSLFetchNameValue(papszHdrLines, "PROJECTION_ORIGIN_LONGITUDE");
468 : const char *pszSpheroidName =
469 7 : CSLFetchNameValue(papszHdrLines, "SPHEROID_NAME");
470 :
471 7 : if (pszProjName == nullptr)
472 : {
473 1 : m_oSRS.Clear();
474 1 : m_oGCPSRS.Clear();
475 1 : return;
476 : }
477 6 : else if ((!EQUAL(pszProjName, "utm")) && (!EQUAL(pszProjName, "ll")))
478 : {
479 0 : CPLError(CE_Warning, CPLE_AppDefined,
480 : "Only utm and lat/long projections are currently supported.");
481 0 : m_oSRS.Clear();
482 0 : m_oGCPSRS.Clear();
483 0 : return;
484 : }
485 6 : MFFSpheroidList *mffEllipsoids = new MFFSpheroidList;
486 :
487 12 : OGRSpatialReference oProj;
488 6 : oProj.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
489 6 : if (EQUAL(pszProjName, "utm"))
490 : {
491 : int nZone;
492 :
493 6 : if (pszOriginLong == nullptr)
494 : {
495 : // If origin not specified, assume 0.0.
496 4 : CPLError(
497 : CE_Warning, CPLE_AppDefined,
498 : "No projection origin longitude specified. Assuming 0.0.");
499 4 : nZone = 31;
500 : }
501 : else
502 2 : nZone = 31 + static_cast<int>(floor(CPLAtof(pszOriginLong) / 6.0));
503 :
504 6 : if (nGCPCount >= 5 && pasGCPList[4].dfGCPY < 0)
505 0 : oProj.SetUTM(nZone, 0);
506 : else
507 6 : oProj.SetUTM(nZone, 1);
508 :
509 6 : if (pszOriginLong != nullptr)
510 2 : oProj.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, CPLAtof(pszOriginLong));
511 : }
512 :
513 12 : OGRSpatialReference oLL;
514 6 : oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
515 6 : if (pszOriginLong != nullptr)
516 2 : oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
517 :
518 6 : if (pszSpheroidName == nullptr)
519 : {
520 4 : CPLError(CE_Warning, CPLE_AppDefined,
521 : "Unspecified ellipsoid. Using wgs-84 parameters.\n");
522 :
523 4 : oProj.SetWellKnownGeogCS("WGS84");
524 4 : oLL.SetWellKnownGeogCS("WGS84");
525 : }
526 : else
527 : {
528 2 : if (mffEllipsoids->SpheroidInList(pszSpheroidName))
529 : {
530 2 : oProj.SetGeogCS(
531 : "unknown", "unknown", pszSpheroidName,
532 : mffEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
533 : mffEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName));
534 2 : oLL.SetGeogCS(
535 : "unknown", "unknown", pszSpheroidName,
536 : mffEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
537 : mffEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName));
538 : }
539 0 : else if (EQUAL(pszSpheroidName, "USER_DEFINED"))
540 : {
541 : const char *pszSpheroidEqRadius =
542 0 : CSLFetchNameValue(papszHdrLines, "SPHEROID_EQUATORIAL_RADIUS");
543 : const char *pszSpheroidPolarRadius =
544 0 : CSLFetchNameValue(papszHdrLines, "SPHEROID_POLAR_RADIUS");
545 0 : if ((pszSpheroidEqRadius != nullptr) &&
546 : (pszSpheroidPolarRadius != nullptr))
547 : {
548 0 : const double eq_radius = CPLAtof(pszSpheroidEqRadius);
549 0 : const double polar_radius = CPLAtof(pszSpheroidPolarRadius);
550 0 : oProj.SetGeogCS("unknown", "unknown", "unknown", eq_radius,
551 0 : eq_radius / (eq_radius - polar_radius));
552 0 : oLL.SetGeogCS("unknown", "unknown", "unknown", eq_radius,
553 0 : eq_radius / (eq_radius - polar_radius));
554 : }
555 : else
556 : {
557 0 : CPLError(CE_Warning, CPLE_AppDefined,
558 : "Radii not specified for user-defined ellipsoid. "
559 : "Using wgs-84 parameters.");
560 0 : oProj.SetWellKnownGeogCS("WGS84");
561 0 : oLL.SetWellKnownGeogCS("WGS84");
562 : }
563 : }
564 : else
565 : {
566 0 : CPLError(CE_Warning, CPLE_AppDefined,
567 : "Unrecognized ellipsoid. Using wgs-84 parameters.");
568 0 : oProj.SetWellKnownGeogCS("WGS84");
569 0 : oLL.SetWellKnownGeogCS("WGS84");
570 : }
571 : }
572 :
573 : /* If a geotransform is sufficient to represent the GCP's (i.e. each */
574 : /* estimated gcp is within 0.25*pixel size of the actual value- this */
575 : /* is the test applied by GDALGCPsToGeoTransform), store the */
576 : /* geotransform. */
577 6 : bool transform_ok = false;
578 :
579 6 : if (EQUAL(pszProjName, "LL"))
580 : {
581 0 : transform_ok = CPL_TO_BOOL(
582 0 : GDALGCPsToGeoTransform(nGCPCount, pasGCPList, adfGeoTransform, 0));
583 : }
584 : else
585 : {
586 : OGRCoordinateTransformation *poTransform =
587 6 : OGRCreateCoordinateTransformation(&oLL, &oProj);
588 6 : bool bSuccess = true;
589 6 : if (poTransform == nullptr)
590 : {
591 0 : CPLErrorReset();
592 0 : bSuccess = FALSE;
593 : }
594 :
595 : double *dfPrjX =
596 6 : static_cast<double *>(CPLMalloc(nGCPCount * sizeof(double)));
597 : double *dfPrjY =
598 6 : static_cast<double *>(CPLMalloc(nGCPCount * sizeof(double)));
599 :
600 20 : for (int gcp_index = 0; gcp_index < nGCPCount; gcp_index++)
601 : {
602 14 : dfPrjX[gcp_index] = pasGCPList[gcp_index].dfGCPX;
603 14 : dfPrjY[gcp_index] = pasGCPList[gcp_index].dfGCPY;
604 :
605 28 : if (bSuccess && !poTransform->Transform(1, &(dfPrjX[gcp_index]),
606 14 : &(dfPrjY[gcp_index])))
607 0 : bSuccess = FALSE;
608 : }
609 :
610 6 : if (bSuccess)
611 : {
612 20 : for (int gcp_index = 0; gcp_index < nGCPCount; gcp_index++)
613 : {
614 14 : pasGCPList[gcp_index].dfGCPX = dfPrjX[gcp_index];
615 14 : pasGCPList[gcp_index].dfGCPY = dfPrjY[gcp_index];
616 : }
617 6 : transform_ok = CPL_TO_BOOL(GDALGCPsToGeoTransform(
618 6 : nGCPCount, pasGCPList, adfGeoTransform, 0));
619 : }
620 :
621 6 : if (poTransform)
622 6 : delete poTransform;
623 :
624 6 : CPLFree(dfPrjX);
625 6 : CPLFree(dfPrjY);
626 : }
627 :
628 6 : m_oSRS = oProj;
629 6 : m_oGCPSRS = std::move(oProj);
630 :
631 6 : if (!transform_ok)
632 : {
633 : /* transform is sufficient in some cases (slant range, standalone gcps)
634 : */
635 4 : adfGeoTransform[0] = 0.0;
636 4 : adfGeoTransform[1] = 1.0;
637 4 : adfGeoTransform[2] = 0.0;
638 4 : adfGeoTransform[3] = 0.0;
639 4 : adfGeoTransform[4] = 0.0;
640 4 : adfGeoTransform[5] = 1.0;
641 4 : m_oSRS.Clear();
642 : }
643 :
644 6 : delete mffEllipsoids;
645 : }
646 :
647 : /************************************************************************/
648 : /* Open() */
649 : /************************************************************************/
650 :
651 29536 : GDALDataset *MFFDataset::Open(GDALOpenInfo *poOpenInfo)
652 :
653 : {
654 : /* -------------------------------------------------------------------- */
655 : /* We assume the user is pointing to the header file. */
656 : /* -------------------------------------------------------------------- */
657 29536 : if (poOpenInfo->nHeaderBytes < 17 || poOpenInfo->fpL == nullptr)
658 26352 : return nullptr;
659 :
660 3184 : if (!poOpenInfo->IsExtensionEqualToCI("hdr"))
661 3167 : return nullptr;
662 :
663 : /* -------------------------------------------------------------------- */
664 : /* Load the .hdr file, and compress white space out around the */
665 : /* equal sign. */
666 : /* -------------------------------------------------------------------- */
667 17 : char **papszHdrLines = CSLLoad(poOpenInfo->pszFilename);
668 17 : if (papszHdrLines == nullptr)
669 0 : return nullptr;
670 :
671 : // Remove spaces. e.g.
672 : // SPHEROID_NAME = CLARKE_1866 -> SPHEROID_NAME=CLARKE_1866
673 567 : for (int i = 0; papszHdrLines[i] != nullptr; i++)
674 : {
675 550 : int iDst = 0;
676 550 : char *pszLine = papszHdrLines[i];
677 :
678 16139 : for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
679 : {
680 15589 : if (pszLine[iSrc] != ' ')
681 : {
682 13635 : pszLine[iDst++] = pszLine[iSrc];
683 : }
684 : }
685 550 : pszLine[iDst] = '\0';
686 : }
687 :
688 : /* -------------------------------------------------------------------- */
689 : /* Verify it is an MFF file. */
690 : /* -------------------------------------------------------------------- */
691 24 : if (CSLFetchNameValue(papszHdrLines, "IMAGE_FILE_FORMAT") != nullptr &&
692 7 : !EQUAL(CSLFetchNameValue(papszHdrLines, "IMAGE_FILE_FORMAT"), "MFF"))
693 : {
694 0 : CSLDestroy(papszHdrLines);
695 0 : return nullptr;
696 : }
697 :
698 17 : if ((CSLFetchNameValue(papszHdrLines, "IMAGE_LINES") == nullptr ||
699 27 : CSLFetchNameValue(papszHdrLines, "LINE_SAMPLES") == nullptr) &&
700 10 : (CSLFetchNameValue(papszHdrLines, "no_rows") == nullptr ||
701 0 : CSLFetchNameValue(papszHdrLines, "no_columns") == nullptr))
702 : {
703 10 : CSLDestroy(papszHdrLines);
704 10 : return nullptr;
705 : }
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* Create a corresponding GDALDataset. */
709 : /* -------------------------------------------------------------------- */
710 14 : auto poDS = std::make_unique<MFFDataset>();
711 :
712 7 : poDS->papszHdrLines = papszHdrLines;
713 :
714 7 : poDS->eAccess = poOpenInfo->eAccess;
715 :
716 : /* -------------------------------------------------------------------- */
717 : /* Set some dataset wide information. */
718 : /* -------------------------------------------------------------------- */
719 9 : if (CSLFetchNameValue(papszHdrLines, "no_rows") != nullptr &&
720 2 : CSLFetchNameValue(papszHdrLines, "no_columns") != nullptr)
721 : {
722 0 : poDS->nRasterXSize =
723 0 : atoi(CSLFetchNameValue(papszHdrLines, "no_columns"));
724 0 : poDS->nRasterYSize = atoi(CSLFetchNameValue(papszHdrLines, "no_rows"));
725 : }
726 : else
727 : {
728 14 : poDS->nRasterXSize =
729 7 : atoi(CSLFetchNameValue(papszHdrLines, "LINE_SAMPLES"));
730 7 : poDS->nRasterYSize =
731 7 : atoi(CSLFetchNameValue(papszHdrLines, "IMAGE_LINES"));
732 : }
733 :
734 7 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
735 : {
736 0 : return nullptr;
737 : }
738 :
739 7 : RawRasterBand::ByteOrder eByteOrder = RawRasterBand::NATIVE_BYTE_ORDER;
740 :
741 7 : const char *pszByteOrder = CSLFetchNameValue(papszHdrLines, "BYTE_ORDER");
742 7 : if (pszByteOrder)
743 : {
744 3 : eByteOrder = EQUAL(pszByteOrder, "LSB")
745 3 : ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
746 : : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
747 : }
748 :
749 : /* -------------------------------------------------------------------- */
750 : /* Get some information specific to APP tiled files. */
751 : /* -------------------------------------------------------------------- */
752 7 : int nTileXSize = 0;
753 7 : int nTileYSize = 0;
754 7 : const char *pszRefinedType = CSLFetchNameValue(papszHdrLines, "type");
755 7 : const bool bTiled = CSLFetchNameValue(papszHdrLines, "no_rows") != nullptr;
756 :
757 7 : if (bTiled)
758 : {
759 2 : if (CSLFetchNameValue(papszHdrLines, "tile_size_rows"))
760 2 : nTileYSize =
761 2 : atoi(CSLFetchNameValue(papszHdrLines, "tile_size_rows"));
762 2 : if (CSLFetchNameValue(papszHdrLines, "tile_size_columns"))
763 2 : nTileXSize =
764 2 : atoi(CSLFetchNameValue(papszHdrLines, "tile_size_columns"));
765 :
766 2 : if (nTileXSize <= 0 || nTileYSize <= 0 ||
767 6 : poDS->nRasterXSize - 1 > INT_MAX - nTileXSize ||
768 2 : poDS->nRasterYSize - 1 > INT_MAX - nTileYSize)
769 : {
770 0 : return nullptr;
771 : }
772 : }
773 :
774 : /* -------------------------------------------------------------------- */
775 : /* Read the directory to find matching band files. */
776 : /* -------------------------------------------------------------------- */
777 : char *const pszTargetPath =
778 7 : CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
779 : char *const pszTargetBase =
780 7 : CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
781 : char **papszDirFiles =
782 7 : VSIReadDir(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
783 7 : if (papszDirFiles == nullptr)
784 : {
785 0 : CPLFree(pszTargetPath);
786 0 : CPLFree(pszTargetBase);
787 0 : return nullptr;
788 : }
789 :
790 7 : int nSkipped = 0;
791 7 : for (int nRawBand = 0; true; nRawBand++)
792 : {
793 : /* Find the next raw band file. */
794 :
795 14 : int i = 0; // Used after for.
796 72 : for (; papszDirFiles[i] != nullptr; i++)
797 : {
798 65 : if (!EQUAL(CPLGetBasenameSafe(papszDirFiles[i]).c_str(),
799 : pszTargetBase))
800 41 : continue;
801 :
802 : const std::string osExtension =
803 24 : CPLGetExtensionSafe(papszDirFiles[i]);
804 24 : if (osExtension.size() >= 2 &&
805 24 : isdigit(static_cast<unsigned char>(osExtension[1])) &&
806 55 : atoi(osExtension.c_str() + 1) == nRawBand &&
807 7 : strchr("bBcCiIjJrRxXzZ", osExtension[0]) != nullptr)
808 7 : break;
809 : }
810 :
811 14 : if (papszDirFiles[i] == nullptr)
812 7 : break;
813 :
814 : /* open the file for required level of access */
815 : const std::string osRawFilename =
816 7 : CPLFormFilenameSafe(pszTargetPath, papszDirFiles[i], nullptr);
817 :
818 7 : VSILFILE *fpRaw = nullptr;
819 7 : if (poOpenInfo->eAccess == GA_Update)
820 0 : fpRaw = VSIFOpenL(osRawFilename.c_str(), "rb+");
821 : else
822 7 : fpRaw = VSIFOpenL(osRawFilename.c_str(), "rb");
823 :
824 7 : if (fpRaw == nullptr)
825 : {
826 0 : CPLError(CE_Warning, CPLE_OpenFailed,
827 : "Unable to open %s ... skipping.", osRawFilename.c_str());
828 0 : nSkipped++;
829 0 : continue;
830 : }
831 14 : poDS->m_papszFileList =
832 7 : CSLAddString(poDS->m_papszFileList, osRawFilename.c_str());
833 :
834 7 : GDALDataType eDataType = GDT_Unknown;
835 7 : const std::string osExt = CPLGetExtensionSafe(papszDirFiles[i]);
836 7 : if (pszRefinedType != nullptr)
837 : {
838 0 : if (EQUAL(pszRefinedType, "C*4"))
839 0 : eDataType = GDT_CFloat32;
840 0 : else if (EQUAL(pszRefinedType, "C*8"))
841 0 : eDataType = GDT_CFloat64;
842 0 : else if (EQUAL(pszRefinedType, "R*4"))
843 0 : eDataType = GDT_Float32;
844 0 : else if (EQUAL(pszRefinedType, "R*8"))
845 0 : eDataType = GDT_Float64;
846 0 : else if (EQUAL(pszRefinedType, "I*1"))
847 0 : eDataType = GDT_Byte;
848 0 : else if (EQUAL(pszRefinedType, "I*2"))
849 0 : eDataType = GDT_Int16;
850 0 : else if (EQUAL(pszRefinedType, "I*4"))
851 0 : eDataType = GDT_Int32;
852 0 : else if (EQUAL(pszRefinedType, "U*2"))
853 0 : eDataType = GDT_UInt16;
854 0 : else if (EQUAL(pszRefinedType, "U*4"))
855 0 : eDataType = GDT_UInt32;
856 0 : else if (EQUAL(pszRefinedType, "J*1"))
857 : {
858 0 : CPLError(
859 : CE_Warning, CPLE_OpenFailed,
860 : "Unable to open band %d because type J*1 is not handled. "
861 : "Skipping.",
862 : nRawBand + 1);
863 0 : nSkipped++;
864 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
865 0 : continue; // Does not support 1 byte complex.
866 : }
867 0 : else if (EQUAL(pszRefinedType, "J*2"))
868 0 : eDataType = GDT_CInt16;
869 0 : else if (EQUAL(pszRefinedType, "K*4"))
870 0 : eDataType = GDT_CInt32;
871 : else
872 : {
873 0 : CPLError(
874 : CE_Warning, CPLE_OpenFailed,
875 : "Unable to open band %d because type %s is not handled. "
876 : "Skipping.\n",
877 : nRawBand + 1, pszRefinedType);
878 0 : nSkipped++;
879 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
880 0 : continue;
881 : }
882 : }
883 7 : else if (STARTS_WITH_CI(osExt.c_str(), "b"))
884 : {
885 6 : eDataType = GDT_Byte;
886 : }
887 1 : else if (STARTS_WITH_CI(osExt.c_str(), "i"))
888 : {
889 0 : eDataType = GDT_UInt16;
890 : }
891 1 : else if (STARTS_WITH_CI(osExt.c_str(), "j"))
892 : {
893 0 : eDataType = GDT_CInt16;
894 : }
895 1 : else if (STARTS_WITH_CI(osExt.c_str(), "r"))
896 : {
897 1 : eDataType = GDT_Float32;
898 : }
899 0 : else if (STARTS_WITH_CI(osExt.c_str(), "x"))
900 : {
901 0 : eDataType = GDT_CFloat32;
902 : }
903 : else
904 : {
905 0 : CPLError(CE_Warning, CPLE_OpenFailed,
906 : "Unable to open band %d because extension %s is not "
907 : "handled. Skipping.",
908 : nRawBand + 1, osExt.c_str());
909 0 : nSkipped++;
910 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
911 0 : continue;
912 : }
913 :
914 7 : const int nBand = poDS->GetRasterCount() + 1;
915 :
916 7 : const int nPixelOffset = GDALGetDataTypeSizeBytes(eDataType);
917 0 : std::unique_ptr<GDALRasterBand> poBand;
918 :
919 7 : if (bTiled)
920 : {
921 4 : poBand = std::make_unique<MFFTiledBand>(poDS.get(), nBand, fpRaw,
922 : nTileXSize, nTileYSize,
923 2 : eDataType, eByteOrder);
924 : }
925 : else
926 : {
927 10 : if (nPixelOffset != 0 &&
928 5 : poDS->GetRasterXSize() > INT_MAX / nPixelOffset)
929 : {
930 0 : CPLError(CE_Warning, CPLE_AppDefined,
931 : "Int overflow occurred... skipping");
932 0 : nSkipped++;
933 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
934 0 : continue;
935 : }
936 :
937 10 : poBand = RawRasterBand::Create(
938 5 : poDS.get(), nBand, fpRaw, 0, nPixelOffset,
939 5 : nPixelOffset * poDS->GetRasterXSize(), eDataType, eByteOrder,
940 5 : RawRasterBand::OwnFP::YES);
941 : }
942 :
943 7 : poDS->SetBand(nBand, std::move(poBand));
944 7 : }
945 :
946 7 : CPLFree(pszTargetPath);
947 7 : CPLFree(pszTargetBase);
948 7 : CSLDestroy(papszDirFiles);
949 :
950 : /* -------------------------------------------------------------------- */
951 : /* Check if we have bands. */
952 : /* -------------------------------------------------------------------- */
953 7 : if (poDS->GetRasterCount() == 0)
954 : {
955 0 : if (nSkipped > 0 && poOpenInfo->eAccess)
956 : {
957 0 : CPLError(CE_Failure, CPLE_OpenFailed,
958 : "Failed to open %d files that were apparently bands. "
959 : "Perhaps this dataset is readonly?",
960 : nSkipped);
961 0 : return nullptr;
962 : }
963 : else
964 : {
965 0 : CPLError(CE_Failure, CPLE_OpenFailed,
966 : "MFF header file read successfully, but no bands "
967 : "were successfully found and opened.");
968 0 : return nullptr;
969 : }
970 : }
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Set all information from the .hdr that isn't well know to be */
974 : /* metadata. */
975 : /* -------------------------------------------------------------------- */
976 81 : for (int i = 0; papszHdrLines[i] != nullptr; i++)
977 : {
978 74 : char *pszName = nullptr;
979 :
980 74 : const char *pszValue = CPLParseNameValue(papszHdrLines[i], &pszName);
981 74 : if (pszName == nullptr || pszValue == nullptr)
982 3 : continue;
983 :
984 71 : if (!EQUAL(pszName, "END") && !EQUAL(pszName, "FILE_TYPE") &&
985 68 : !EQUAL(pszName, "BYTE_ORDER") && !EQUAL(pszName, "no_columns") &&
986 65 : !EQUAL(pszName, "no_rows") && !EQUAL(pszName, "type") &&
987 63 : !EQUAL(pszName, "tile_size_rows") &&
988 61 : !EQUAL(pszName, "tile_size_columns") &&
989 59 : !EQUAL(pszName, "IMAGE_FILE_FORMAT") &&
990 52 : !EQUAL(pszName, "IMAGE_LINES") && !EQUAL(pszName, "LINE_SAMPLES"))
991 : {
992 38 : poDS->SetMetadataItem(pszName, pszValue);
993 : }
994 :
995 71 : CPLFree(pszName);
996 : }
997 :
998 : /* -------------------------------------------------------------------- */
999 : /* Any GCPs in header file? */
1000 : /* -------------------------------------------------------------------- */
1001 7 : poDS->ScanForGCPs();
1002 7 : poDS->ScanForProjectionInfo();
1003 7 : if (poDS->nGCPCount == 0)
1004 1 : poDS->m_oGCPSRS.Clear();
1005 :
1006 : /* -------------------------------------------------------------------- */
1007 : /* Initialize any PAM information. */
1008 : /* -------------------------------------------------------------------- */
1009 7 : poDS->SetDescription(poOpenInfo->pszFilename);
1010 7 : poDS->TryLoadXML();
1011 :
1012 : /* -------------------------------------------------------------------- */
1013 : /* Check for overviews. */
1014 : /* -------------------------------------------------------------------- */
1015 7 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
1016 :
1017 7 : return poDS.release();
1018 : }
1019 :
1020 : /************************************************************************/
1021 : /* GDALRegister_MFF() */
1022 : /************************************************************************/
1023 :
1024 1667 : void GDALRegister_MFF()
1025 :
1026 : {
1027 1667 : if (GDALGetDriverByName("MFF") != nullptr)
1028 282 : return;
1029 :
1030 1385 : GDALDriver *poDriver = new GDALDriver();
1031 :
1032 1385 : poDriver->SetDescription("MFF");
1033 1385 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1034 1385 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Vexcel MFF Raster");
1035 1385 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/mff.html");
1036 1385 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdr");
1037 1385 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1038 :
1039 1385 : poDriver->pfnOpen = MFFDataset::Open;
1040 :
1041 1385 : GetGDALDriverManager()->RegisterDriver(poDriver);
1042 : }
|