Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/raw/hkvdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GView
4
 * Purpose:  Implementation of Atlantis HKV labelled blob support
5
 * Author:   Frank Warmerdam, warmerdam@pobox.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 <ctype.h>
15
16
#include "atlsci_spheroid.h"
17
#include "cpl_string.h"
18
#include "gdal_frmts.h"
19
#include "gdal_priv.h"
20
#include "ogr_spatialref.h"
21
#include "rawdataset.h"
22
23
#include <cmath>
24
25
#include <algorithm>
26
27
/************************************************************************/
28
/* ==================================================================== */
29
/*                            HKVRasterBand                             */
30
/* ==================================================================== */
31
/************************************************************************/
32
33
class HKVDataset;
34
35
class HKVRasterBand final : public RawRasterBand
36
{
37
    friend class HKVDataset;
38
39
  public:
40
    HKVRasterBand(HKVDataset *poDS, int nBand, VSILFILE *fpRaw,
41
                  unsigned int nImgOffset, int nPixelOffset, int nLineOffset,
42
                  GDALDataType eDataType, int bNativeOrder);
43
44
    ~HKVRasterBand() override;
45
};
46
47
0
HKVRasterBand::~HKVRasterBand() = default;
48
49
/************************************************************************/
50
/*                            HKV Spheroids                             */
51
/************************************************************************/
52
53
class HKVSpheroidList final : public SpheroidList
54
{
55
  public:
56
    HKVSpheroidList();
57
};
58
59
HKVSpheroidList ::HKVSpheroidList()
60
0
{
61
0
    num_spheroids = 58;
62
0
    epsilonR = 0.1;
63
0
    epsilonI = 0.000001;
64
65
0
    spheroids[0].SetValuesByEqRadiusAndInvFlattening("airy-1830", 6377563.396,
66
0
                                                     299.3249646);
67
0
    spheroids[1].SetValuesByEqRadiusAndInvFlattening("modified-airy",
68
0
                                                     6377340.189, 299.3249646);
69
0
    spheroids[2].SetValuesByEqRadiusAndInvFlattening("australian-national",
70
0
                                                     6378160, 298.25);
71
0
    spheroids[3].SetValuesByEqRadiusAndInvFlattening("bessel-1841-namibia",
72
0
                                                     6377483.865, 299.1528128);
73
0
    spheroids[4].SetValuesByEqRadiusAndInvFlattening("bessel-1841", 6377397.155,
74
0
                                                     299.1528128);
75
0
    spheroids[5].SetValuesByEqRadiusAndInvFlattening("clarke-1858", 6378294.0,
76
0
                                                     294.297);
77
0
    spheroids[6].SetValuesByEqRadiusAndInvFlattening("clarke-1866", 6378206.4,
78
0
                                                     294.9786982);
79
0
    spheroids[7].SetValuesByEqRadiusAndInvFlattening("clarke-1880", 6378249.145,
80
0
                                                     293.465);
81
0
    spheroids[8].SetValuesByEqRadiusAndInvFlattening("everest-india-1830",
82
0
                                                     6377276.345, 300.8017);
83
0
    spheroids[9].SetValuesByEqRadiusAndInvFlattening("everest-sabah-sarawak",
84
0
                                                     6377298.556, 300.8017);
85
0
    spheroids[10].SetValuesByEqRadiusAndInvFlattening("everest-india-1956",
86
0
                                                      6377301.243, 300.8017);
87
0
    spheroids[11].SetValuesByEqRadiusAndInvFlattening("everest-malaysia-1969",
88
0
                                                      6377295.664, 300.8017);
89
0
    spheroids[12].SetValuesByEqRadiusAndInvFlattening("everest-malay-sing",
90
0
                                                      6377304.063, 300.8017);
91
0
    spheroids[13].SetValuesByEqRadiusAndInvFlattening("everest-pakistan",
92
0
                                                      6377309.613, 300.8017);
93
0
    spheroids[14].SetValuesByEqRadiusAndInvFlattening("modified-fisher-1960",
94
0
                                                      6378155, 298.3);
95
0
    spheroids[15].SetValuesByEqRadiusAndInvFlattening("helmert-1906", 6378200,
96
0
                                                      298.3);
97
0
    spheroids[16].SetValuesByEqRadiusAndInvFlattening("hough-1960", 6378270,
98
0
                                                      297);
99
0
    spheroids[17].SetValuesByEqRadiusAndInvFlattening("hughes", 6378273.0,
100
0
                                                      298.279);
101
0
    spheroids[18].SetValuesByEqRadiusAndInvFlattening("indonesian-1974",
102
0
                                                      6378160, 298.247);
103
0
    spheroids[19].SetValuesByEqRadiusAndInvFlattening("international-1924",
104
0
                                                      6378388, 297);
105
0
    spheroids[20].SetValuesByEqRadiusAndInvFlattening("iugc-67", 6378160.0,
106
0
                                                      298.254);
107
0
    spheroids[21].SetValuesByEqRadiusAndInvFlattening("iugc-75", 6378140.0,
108
0
                                                      298.25298);
109
0
    spheroids[22].SetValuesByEqRadiusAndInvFlattening("krassovsky-1940",
110
0
                                                      6378245, 298.3);
111
0
    spheroids[23].SetValuesByEqRadiusAndInvFlattening("kaula", 6378165.0,
112
0
                                                      292.308);
113
0
    spheroids[24].SetValuesByEqRadiusAndInvFlattening("grs-80", 6378137,
114
0
                                                      298.257222101);
115
0
    spheroids[25].SetValuesByEqRadiusAndInvFlattening("south-american-1969",
116
0
                                                      6378160, 298.25);
117
0
    spheroids[26].SetValuesByEqRadiusAndInvFlattening("wgs-72", 6378135,
118
0
                                                      298.26);
119
0
    spheroids[27].SetValuesByEqRadiusAndInvFlattening("wgs-84", 6378137,
120
0
                                                      298.257223563);
121
0
    spheroids[28].SetValuesByEqRadiusAndInvFlattening("ev-wgs-84", 6378137.0,
122
0
                                                      298.252841);
123
0
    spheroids[29].SetValuesByEqRadiusAndInvFlattening("ev-bessel", 6377397.0,
124
0
                                                      299.1976073);
125
126
0
    spheroids[30].SetValuesByEqRadiusAndInvFlattening("airy_1830", 6377563.396,
127
0
                                                      299.3249646);
128
0
    spheroids[31].SetValuesByEqRadiusAndInvFlattening("modified_airy",
129
0
                                                      6377340.189, 299.3249646);
130
0
    spheroids[32].SetValuesByEqRadiusAndInvFlattening("australian_national",
131
0
                                                      6378160, 298.25);
132
0
    spheroids[33].SetValuesByEqRadiusAndInvFlattening("bessel_1841_namibia",
133
0
                                                      6377483.865, 299.1528128);
134
0
    spheroids[34].SetValuesByEqRadiusAndInvFlattening("bessel_1841",
135
0
                                                      6377397.155, 299.1528128);
136
0
    spheroids[35].SetValuesByEqRadiusAndInvFlattening("clarke_1858", 6378294.0,
137
0
                                                      294.297);
138
0
    spheroids[36].SetValuesByEqRadiusAndInvFlattening("clarke_1866", 6378206.4,
139
0
                                                      294.9786982);
140
0
    spheroids[37].SetValuesByEqRadiusAndInvFlattening("clarke_1880",
141
0
                                                      6378249.145, 293.465);
142
0
    spheroids[38].SetValuesByEqRadiusAndInvFlattening("everest_india_1830",
143
0
                                                      6377276.345, 300.8017);
144
0
    spheroids[39].SetValuesByEqRadiusAndInvFlattening("everest_sabah_sarawak",
145
0
                                                      6377298.556, 300.8017);
146
0
    spheroids[40].SetValuesByEqRadiusAndInvFlattening("everest_india_1956",
147
0
                                                      6377301.243, 300.8017);
148
0
    spheroids[41].SetValuesByEqRadiusAndInvFlattening("everest_malaysia_1969",
149
0
                                                      6377295.664, 300.8017);
150
0
    spheroids[42].SetValuesByEqRadiusAndInvFlattening("everest_malay_sing",
151
0
                                                      6377304.063, 300.8017);
152
0
    spheroids[43].SetValuesByEqRadiusAndInvFlattening("everest_pakistan",
153
0
                                                      6377309.613, 300.8017);
154
0
    spheroids[44].SetValuesByEqRadiusAndInvFlattening("modified_fisher_1960",
155
0
                                                      6378155, 298.3);
156
0
    spheroids[45].SetValuesByEqRadiusAndInvFlattening("helmert_1906", 6378200,
157
0
                                                      298.3);
158
0
    spheroids[46].SetValuesByEqRadiusAndInvFlattening("hough_1960", 6378270,
159
0
                                                      297);
160
0
    spheroids[47].SetValuesByEqRadiusAndInvFlattening("indonesian_1974",
161
0
                                                      6378160, 298.247);
162
0
    spheroids[48].SetValuesByEqRadiusAndInvFlattening("international_1924",
163
0
                                                      6378388, 297);
164
0
    spheroids[49].SetValuesByEqRadiusAndInvFlattening("iugc_67", 6378160.0,
165
0
                                                      298.254);
166
0
    spheroids[50].SetValuesByEqRadiusAndInvFlattening("iugc_75", 6378140.0,
167
0
                                                      298.25298);
168
0
    spheroids[51].SetValuesByEqRadiusAndInvFlattening("krassovsky_1940",
169
0
                                                      6378245, 298.3);
170
0
    spheroids[52].SetValuesByEqRadiusAndInvFlattening("grs_80", 6378137,
171
0
                                                      298.257222101);
172
0
    spheroids[53].SetValuesByEqRadiusAndInvFlattening("south_american_1969",
173
0
                                                      6378160, 298.25);
174
0
    spheroids[54].SetValuesByEqRadiusAndInvFlattening("wgs_72", 6378135,
175
0
                                                      298.26);
176
0
    spheroids[55].SetValuesByEqRadiusAndInvFlattening("wgs_84", 6378137,
177
0
                                                      298.257223563);
178
0
    spheroids[56].SetValuesByEqRadiusAndInvFlattening("ev_wgs_84", 6378137.0,
179
0
                                                      298.252841);
180
0
    spheroids[57].SetValuesByEqRadiusAndInvFlattening("ev_bessel", 6377397.0,
181
0
                                                      299.1976073);
182
0
}
183
184
/************************************************************************/
185
/* ==================================================================== */
186
/*                              HKVDataset                              */
187
/* ==================================================================== */
188
/************************************************************************/
189
190
class HKVDataset final : public RawDataset
191
{
192
    friend class HKVRasterBand;
193
194
    char *pszPath;
195
    VSILFILE *fpBlob;
196
197
    int nGCPCount;
198
    GDAL_GCP *pasGCPList;
199
200
    void ProcessGeoref(const char *);
201
    void ProcessGeorefGCP(char **, const char *, double, double);
202
203
    void SetVersion(float version_number)
204
0
    {
205
        // Update stored info.
206
0
        MFF2version = version_number;
207
0
    }
208
209
    float MFF2version;
210
211
    GDALDataType eRasterType;
212
213
    void SetNoDataValue(double);
214
215
    OGRSpatialReference m_oSRS{};
216
    OGRSpatialReference m_oGCPSRS{};
217
    GDALGeoTransform m_gt{};
218
219
    char **papszAttrib;
220
221
    char **papszGeoref;
222
223
    // NOTE: The MFF2 format goes against GDAL's API in that nodata values are
224
    // set per-dataset rather than per-band.  To compromise, for writing out,
225
    // the dataset's nodata value will be set to the last value set on any of
226
    // the raster bands.
227
228
    bool bNoDataSet;
229
    double dfNoDataValue;
230
231
    CPL_DISALLOW_COPY_ASSIGN(HKVDataset)
232
233
    CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
234
235
  public:
236
    HKVDataset();
237
    ~HKVDataset() override;
238
239
    int GetGCPCount() override /* const */
240
0
    {
241
0
        return nGCPCount;
242
0
    }
243
244
    const OGRSpatialReference *GetGCPSpatialRef() const override
245
0
    {
246
0
        return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
247
0
    }
248
249
    const GDAL_GCP *GetGCPs() override;
250
251
    const OGRSpatialReference *GetSpatialRef() const override
252
0
    {
253
0
        return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
254
0
    }
255
256
    CPLErr GetGeoTransform(GDALGeoTransform &) const override;
257
258
    static GDALDataset *Open(GDALOpenInfo *);
259
};
260
261
/************************************************************************/
262
/* ==================================================================== */
263
/*                            HKVRasterBand                             */
264
/* ==================================================================== */
265
/************************************************************************/
266
267
/************************************************************************/
268
/*                           HKVRasterBand()                            */
269
/************************************************************************/
270
271
HKVRasterBand::HKVRasterBand(HKVDataset *poDSIn, int nBandIn, VSILFILE *fpRawIn,
272
                             unsigned int nImgOffsetIn, int nPixelOffsetIn,
273
                             int nLineOffsetIn, GDALDataType eDataTypeIn,
274
                             int bNativeOrderIn)
275
0
    : RawRasterBand(GDALDataset::FromHandle(poDSIn), nBandIn, fpRawIn,
276
0
                    nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn, eDataTypeIn,
277
0
                    bNativeOrderIn, RawRasterBand::OwnFP::NO)
278
279
0
{
280
0
    poDS = poDSIn;
281
0
    nBand = nBandIn;
282
283
0
    nBlockXSize = poDS->GetRasterXSize();
284
0
    nBlockYSize = 1;
285
0
}
286
287
/************************************************************************/
288
/* ==================================================================== */
289
/*                              HKVDataset                              */
290
/* ==================================================================== */
291
/************************************************************************/
292
293
/************************************************************************/
294
/*                             HKVDataset()                             */
295
/************************************************************************/
296
297
HKVDataset::HKVDataset()
298
0
    : pszPath(nullptr), fpBlob(nullptr), nGCPCount(0), pasGCPList(nullptr),
299
      // Initialize datasets to new version; change if necessary.
300
0
      MFF2version(1.1f), eRasterType(GDT_Unknown), papszAttrib(nullptr),
301
0
      papszGeoref(nullptr), bNoDataSet(false), dfNoDataValue(0.0)
302
0
{
303
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
304
0
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
305
0
}
306
307
/************************************************************************/
308
/*                            ~HKVDataset()                             */
309
/************************************************************************/
310
311
HKVDataset::~HKVDataset()
312
313
0
{
314
0
    HKVDataset::Close();
315
0
}
316
317
/************************************************************************/
318
/*                               Close()                                */
319
/************************************************************************/
320
321
CPLErr HKVDataset::Close(GDALProgressFunc, void *)
322
0
{
323
0
    CPLErr eErr = CE_None;
324
0
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
325
0
    {
326
0
        if (HKVDataset::FlushCache(true) != CE_None)
327
0
            eErr = CE_Failure;
328
329
0
        if (fpBlob)
330
0
        {
331
0
            if (VSIFCloseL(fpBlob) != 0)
332
0
            {
333
0
                eErr = CE_Failure;
334
0
                CPLError(CE_Failure, CPLE_FileIO, "I/O error");
335
0
            }
336
0
        }
337
338
0
        if (nGCPCount > 0)
339
0
        {
340
0
            GDALDeinitGCPs(nGCPCount, pasGCPList);
341
0
            CPLFree(pasGCPList);
342
0
        }
343
344
0
        CPLFree(pszPath);
345
0
        CSLDestroy(papszGeoref);
346
0
        CSLDestroy(papszAttrib);
347
348
0
        if (GDALPamDataset::Close() != CE_None)
349
0
            eErr = CE_Failure;
350
0
    }
351
0
    return eErr;
352
0
}
353
354
/************************************************************************/
355
/*                          GetGeoTransform()                           */
356
/************************************************************************/
357
358
CPLErr HKVDataset::GetGeoTransform(GDALGeoTransform &gt) const
359
360
0
{
361
0
    gt = m_gt;
362
0
    return CE_None;
363
0
}
364
365
/************************************************************************/
366
/*                               GetGCP()                               */
367
/************************************************************************/
368
369
const GDAL_GCP *HKVDataset::GetGCPs()
370
371
0
{
372
0
    return pasGCPList;
373
0
}
374
375
/************************************************************************/
376
/*                          ProcessGeorefGCP()                          */
377
/************************************************************************/
378
379
void HKVDataset::ProcessGeorefGCP(char **papszGeorefIn, const char *pszBase,
380
                                  double dfRasterX, double dfRasterY)
381
382
0
{
383
    /* -------------------------------------------------------------------- */
384
    /*      Fetch the GCP from the string list.                             */
385
    /* -------------------------------------------------------------------- */
386
0
    char szFieldName[128] = {'\0'};
387
0
    snprintf(szFieldName, sizeof(szFieldName), "%s.latitude", pszBase);
388
0
    double dfLat = 0.0;
389
0
    if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
390
0
        return;
391
0
    else
392
0
        dfLat = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
393
394
0
    snprintf(szFieldName, sizeof(szFieldName), "%s.longitude", pszBase);
395
0
    double dfLong = 0.0;
396
0
    if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
397
0
        return;
398
0
    else
399
0
        dfLong = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
400
401
    /* -------------------------------------------------------------------- */
402
    /*      Add the gcp to the internal list.                               */
403
    /* -------------------------------------------------------------------- */
404
0
    GDALInitGCPs(1, pasGCPList + nGCPCount);
405
406
0
    CPLFree(pasGCPList[nGCPCount].pszId);
407
408
0
    pasGCPList[nGCPCount].pszId = CPLStrdup(pszBase);
409
410
0
    pasGCPList[nGCPCount].dfGCPX = dfLong;
411
0
    pasGCPList[nGCPCount].dfGCPY = dfLat;
412
0
    pasGCPList[nGCPCount].dfGCPZ = 0.0;
413
414
0
    pasGCPList[nGCPCount].dfGCPPixel = dfRasterX;
415
0
    pasGCPList[nGCPCount].dfGCPLine = dfRasterY;
416
417
0
    nGCPCount++;
418
0
}
419
420
/************************************************************************/
421
/*                           ProcessGeoref()                            */
422
/************************************************************************/
423
424
void HKVDataset::ProcessGeoref(const char *pszFilename)
425
426
0
{
427
    /* -------------------------------------------------------------------- */
428
    /*      Load the georef file, and boil white space away from around     */
429
    /*      the equal sign.                                                 */
430
    /* -------------------------------------------------------------------- */
431
0
    CSLDestroy(papszGeoref);
432
0
    papszGeoref = CSLLoad(pszFilename);
433
0
    if (papszGeoref == nullptr)
434
0
        return;
435
436
0
    HKVSpheroidList *hkvEllipsoids = new HKVSpheroidList;
437
438
0
    for (int i = 0; papszGeoref[i] != nullptr; i++)
439
0
    {
440
0
        int iDst = 0;
441
0
        char *pszLine = papszGeoref[i];
442
443
0
        for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
444
0
        {
445
0
            if (pszLine[iSrc] != ' ')
446
0
            {
447
0
                pszLine[iDst++] = pszLine[iSrc];
448
0
            }
449
0
        }
450
0
        pszLine[iDst] = '\0';
451
0
    }
452
453
    /* -------------------------------------------------------------------- */
454
    /*      Try to get GCPs, in lat/longs                     .             */
455
    /* -------------------------------------------------------------------- */
456
0
    nGCPCount = 0;
457
0
    pasGCPList = static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 5));
458
459
0
    if (MFF2version > 1.0)
460
0
    {
461
0
        ProcessGeorefGCP(papszGeoref, "top_left", 0, 0);
462
0
        ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize(), 0);
463
0
        ProcessGeorefGCP(papszGeoref, "bottom_left", 0, GetRasterYSize());
464
0
        ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize(),
465
0
                         GetRasterYSize());
466
0
        ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
467
0
                         GetRasterYSize() / 2.0);
468
0
    }
469
0
    else
470
0
    {
471
0
        ProcessGeorefGCP(papszGeoref, "top_left", 0.5, 0.5);
472
0
        ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize() - 0.5, 0.5);
473
0
        ProcessGeorefGCP(papszGeoref, "bottom_left", 0.5,
474
0
                         GetRasterYSize() - 0.5);
475
0
        ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize() - 0.5,
476
0
                         GetRasterYSize() - 0.5);
477
0
        ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
478
0
                         GetRasterYSize() / 2.0);
479
0
    }
480
481
0
    if (nGCPCount == 0)
482
0
    {
483
0
        CPLFree(pasGCPList);
484
0
        pasGCPList = nullptr;
485
0
    }
486
487
    /* -------------------------------------------------------------------- */
488
    /*      Do we have a recognised projection?                             */
489
    /* -------------------------------------------------------------------- */
490
0
    const char *pszProjName = CSLFetchNameValue(papszGeoref, "projection.name");
491
0
    const char *pszOriginLong =
492
0
        CSLFetchNameValue(papszGeoref, "projection.origin_longitude");
493
0
    const char *pszSpheroidName =
494
0
        CSLFetchNameValue(papszGeoref, "spheroid.name");
495
496
0
    if (pszSpheroidName != nullptr &&
497
0
        hkvEllipsoids->SpheroidInList(pszSpheroidName))
498
0
    {
499
#if 0
500
      // TODO(schwehr): Enable in trunk after 2.1 branch and fix.
501
      // Breaks tests on some platforms.
502
      CPLError( CE_Failure, CPLE_AppDefined,
503
                "Unrecognized ellipsoid.  Not handled.  "
504
                "Spheroid name not in spheroid list: '%s'",
505
                pszSpheroidName );
506
#endif
507
        // Why were eq_radius and inv_flattening never used?
508
        // eq_radius = hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName);
509
        // inv_flattening =
510
        //     hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName);
511
0
    }
512
0
    else if (pszProjName != nullptr)
513
0
    {
514
0
        CPLError(CE_Warning, CPLE_AppDefined,
515
0
                 "Unrecognized ellipsoid.  Not handled.");
516
        // TODO(schwehr): This error is was never what was happening.
517
        // CPLError( CE_Warning, CPLE_AppDefined,
518
        //           "Unrecognized ellipsoid.  Using wgs-84 parameters.");
519
        // eq_radius=hkvEllipsoids->GetSpheroidEqRadius("wgs-84"); */
520
        // inv_flattening=hkvEllipsoids->GetSpheroidInverseFlattening("wgs-84");
521
0
    }
522
523
0
    if (pszProjName != nullptr && EQUAL(pszProjName, "utm") && nGCPCount == 5)
524
0
    {
525
        // int nZone = (int)((CPLAtof(pszOriginLong)+184.5) / 6.0);
526
0
        int nZone = 31;  // TODO(schwehr): Where does 31 come from?
527
528
0
        if (pszOriginLong == nullptr)
529
0
        {
530
            // If origin not specified, assume 0.0.
531
0
            CPLError(
532
0
                CE_Warning, CPLE_AppDefined,
533
0
                "No projection origin longitude specified.  Assuming 0.0.");
534
0
        }
535
0
        else
536
0
        {
537
0
            nZone = 31 + static_cast<int>(floor(CPLAtof(pszOriginLong) / 6.0));
538
0
        }
539
540
0
        OGRSpatialReference oUTM;
541
542
0
        if (pasGCPList[4].dfGCPY < 0)
543
0
            oUTM.SetUTM(nZone, 0);
544
0
        else
545
0
            oUTM.SetUTM(nZone, 1);
546
547
0
        OGRSpatialReference oLL;
548
0
        oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
549
0
        if (pszOriginLong != nullptr)
550
0
        {
551
0
            oUTM.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, CPLAtof(pszOriginLong));
552
0
            oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
553
0
        }
554
555
0
        if ((pszSpheroidName == nullptr) ||
556
0
            (EQUAL(pszSpheroidName, "wgs-84")) ||
557
0
            (EQUAL(pszSpheroidName, "wgs_84")))
558
0
        {
559
0
            oUTM.SetWellKnownGeogCS("WGS84");
560
0
            oLL.SetWellKnownGeogCS("WGS84");
561
0
        }
562
0
        else
563
0
        {
564
0
            if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
565
0
            {
566
0
                oUTM.SetGeogCS(
567
0
                    "unknown", "unknown", pszSpheroidName,
568
0
                    hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
569
0
                    hkvEllipsoids->GetSpheroidInverseFlattening(
570
0
                        pszSpheroidName));
571
0
                oLL.SetGeogCS(
572
0
                    "unknown", "unknown", pszSpheroidName,
573
0
                    hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
574
0
                    hkvEllipsoids->GetSpheroidInverseFlattening(
575
0
                        pszSpheroidName));
576
0
            }
577
0
            else
578
0
            {
579
0
                CPLError(CE_Warning, CPLE_AppDefined,
580
0
                         "Unrecognized ellipsoid.  Using wgs-84 parameters.");
581
0
                oUTM.SetWellKnownGeogCS("WGS84");
582
0
                oLL.SetWellKnownGeogCS("WGS84");
583
0
            }
584
0
        }
585
586
0
        OGRCoordinateTransformation *poTransform =
587
0
            OGRCreateCoordinateTransformation(&oLL, &oUTM);
588
589
0
        bool bSuccess = true;
590
0
        if (poTransform == nullptr)
591
0
        {
592
0
            CPLErrorReset();
593
0
            bSuccess = false;
594
0
        }
595
596
0
        double dfUtmX[5] = {0.0};
597
0
        double dfUtmY[5] = {0.0};
598
599
0
        if (poTransform != nullptr)
600
0
        {
601
0
            for (int gcp_index = 0; gcp_index < 5; gcp_index++)
602
0
            {
603
0
                dfUtmX[gcp_index] = pasGCPList[gcp_index].dfGCPX;
604
0
                dfUtmY[gcp_index] = pasGCPList[gcp_index].dfGCPY;
605
606
0
                if (bSuccess && !poTransform->Transform(1, &(dfUtmX[gcp_index]),
607
0
                                                        &(dfUtmY[gcp_index])))
608
0
                    bSuccess = false;
609
0
            }
610
0
        }
611
612
0
        if (bSuccess)
613
0
        {
614
            // Update GCPS to proper projection.
615
0
            for (int gcp_index = 0; gcp_index < 5; gcp_index++)
616
0
            {
617
0
                pasGCPList[gcp_index].dfGCPX = dfUtmX[gcp_index];
618
0
                pasGCPList[gcp_index].dfGCPY = dfUtmY[gcp_index];
619
0
            }
620
621
0
            m_oGCPSRS = oUTM;
622
623
0
            bool transform_ok = CPL_TO_BOOL(
624
0
                GDALGCPsToGeoTransform(5, pasGCPList, m_gt.data(), 0));
625
626
0
            if (!transform_ok)
627
0
            {
628
                // Transform may not be sufficient in all cases (slant range
629
                // projection).
630
0
                m_gt = GDALGeoTransform();
631
0
                m_oGCPSRS.Clear();
632
0
            }
633
0
            else
634
0
            {
635
0
                m_oSRS = std::move(oUTM);
636
0
            }
637
0
        }
638
639
0
        if (poTransform != nullptr)
640
0
            delete poTransform;
641
0
    }
642
0
    else if (pszProjName != nullptr && nGCPCount == 5)
643
0
    {
644
0
        OGRSpatialReference oLL;
645
0
        oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
646
647
0
        if (pszOriginLong != nullptr)
648
0
        {
649
0
            oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
650
0
        }
651
652
0
        if (pszSpheroidName == nullptr ||
653
0
            EQUAL(pszSpheroidName, "wgs-84") ||  // Dash.
654
0
            EQUAL(pszSpheroidName, "wgs_84"))    // Underscore.
655
0
        {
656
0
            oLL.SetWellKnownGeogCS("WGS84");
657
0
        }
658
0
        else
659
0
        {
660
0
            if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
661
0
            {
662
0
                oLL.SetGeogCS(
663
0
                    "", "", pszSpheroidName,
664
0
                    hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
665
0
                    hkvEllipsoids->GetSpheroidInverseFlattening(
666
0
                        pszSpheroidName));
667
0
            }
668
0
            else
669
0
            {
670
0
                CPLError(CE_Warning, CPLE_AppDefined,
671
0
                         "Unrecognized ellipsoid.  "
672
0
                         "Using wgs-84 parameters.");
673
0
                oLL.SetWellKnownGeogCS("WGS84");
674
0
            }
675
0
        }
676
677
0
        const bool transform_ok =
678
0
            CPL_TO_BOOL(GDALGCPsToGeoTransform(5, pasGCPList, m_gt.data(), 0));
679
680
0
        m_oSRS.Clear();
681
682
0
        if (!transform_ok)
683
0
        {
684
0
            m_gt = GDALGeoTransform();
685
0
        }
686
0
        else
687
0
        {
688
0
            m_oSRS = oLL;
689
0
        }
690
691
0
        m_oGCPSRS = std::move(oLL);
692
0
    }
693
694
0
    delete hkvEllipsoids;
695
0
}
696
697
/************************************************************************/
698
/*                                Open()                                */
699
/************************************************************************/
700
701
GDALDataset *HKVDataset::Open(GDALOpenInfo *poOpenInfo)
702
703
467k
{
704
    /* -------------------------------------------------------------------- */
705
    /*      We assume the dataset is passed as a directory.  Check for      */
706
    /*      an attrib and blob file as a minimum.                           */
707
    /* -------------------------------------------------------------------- */
708
467k
    if (!poOpenInfo->bIsDirectory)
709
464k
        return nullptr;
710
711
3.62k
    std::string osFilename =
712
3.62k
        CPLFormFilenameSafe(poOpenInfo->pszFilename, "image_data", nullptr);
713
3.62k
    VSIStatBuf sStat;
714
3.62k
    if (VSIStat(osFilename.c_str(), &sStat) != 0)
715
3.62k
        osFilename =
716
3.62k
            CPLFormFilenameSafe(poOpenInfo->pszFilename, "blob", nullptr);
717
3.62k
    if (VSIStat(osFilename.c_str(), &sStat) != 0)
718
3.62k
        return nullptr;
719
720
0
    osFilename =
721
0
        CPLFormFilenameSafe(poOpenInfo->pszFilename, "attrib", nullptr);
722
0
    if (VSIStat(osFilename.c_str(), &sStat) != 0)
723
0
        return nullptr;
724
725
    /* -------------------------------------------------------------------- */
726
    /*      Load the attrib file, and boil white space away from around     */
727
    /*      the equal sign.                                                 */
728
    /* -------------------------------------------------------------------- */
729
0
    char **papszAttrib = CSLLoad(osFilename.c_str());
730
0
    if (papszAttrib == nullptr)
731
0
        return nullptr;
732
733
0
    for (int i = 0; papszAttrib[i] != nullptr; i++)
734
0
    {
735
0
        int iDst = 0;
736
0
        char *pszLine = papszAttrib[i];
737
738
0
        for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
739
0
        {
740
0
            if (pszLine[iSrc] != ' ')
741
0
            {
742
0
                pszLine[iDst++] = pszLine[iSrc];
743
0
            }
744
0
        }
745
0
        pszLine[iDst] = '\0';
746
0
    }
747
748
    /* -------------------------------------------------------------------- */
749
    /*      Create a corresponding GDALDataset.                             */
750
    /* -------------------------------------------------------------------- */
751
0
    auto poDS = std::make_unique<HKVDataset>();
752
753
0
    poDS->pszPath = CPLStrdup(poOpenInfo->pszFilename);
754
0
    poDS->papszAttrib = papszAttrib;
755
756
0
    poDS->eAccess = poOpenInfo->eAccess;
757
758
    /* -------------------------------------------------------------------- */
759
    /*      Set some dataset wide information.                              */
760
    /* -------------------------------------------------------------------- */
761
0
    bool bNative = false;
762
0
    bool bComplex = false;
763
0
    int nRawBands = 0;
764
765
0
    if (CSLFetchNameValue(papszAttrib, "extent.cols") == nullptr ||
766
0
        CSLFetchNameValue(papszAttrib, "extent.rows") == nullptr)
767
0
    {
768
0
        return nullptr;
769
0
    }
770
771
0
    poDS->nRasterXSize = atoi(CSLFetchNameValue(papszAttrib, "extent.cols"));
772
0
    poDS->nRasterYSize = atoi(CSLFetchNameValue(papszAttrib, "extent.rows"));
773
774
0
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
775
0
    {
776
0
        return nullptr;
777
0
    }
778
779
0
    const char *pszValue = CSLFetchNameValue(papszAttrib, "pixel.order");
780
0
    if (pszValue == nullptr)
781
0
        bNative = true;
782
0
    else
783
0
    {
784
#ifdef CPL_MSB
785
        bNative = strstr(pszValue, "*msbf") != NULL;
786
#else
787
0
        bNative = strstr(pszValue, "*lsbf") != nullptr;
788
0
#endif
789
0
    }
790
791
0
    bool bNoDataSet = false;
792
0
    double dfNoDataValue = 0.0;
793
0
    pszValue = CSLFetchNameValue(papszAttrib, "pixel.no_data");
794
0
    if (pszValue != nullptr)
795
0
    {
796
0
        bNoDataSet = true;
797
0
        dfNoDataValue = CPLAtof(pszValue);
798
0
    }
799
800
0
    pszValue = CSLFetchNameValue(papszAttrib, "channel.enumeration");
801
0
    if (pszValue != nullptr)
802
0
        nRawBands = atoi(pszValue);
803
0
    else
804
0
        nRawBands = 1;
805
806
0
    if (!GDALCheckBandCount(nRawBands, TRUE))
807
0
    {
808
0
        return nullptr;
809
0
    }
810
811
0
    pszValue = CSLFetchNameValue(papszAttrib, "pixel.field");
812
0
    if (pszValue != nullptr && strstr(pszValue, "*complex") != nullptr)
813
0
        bComplex = true;
814
0
    else
815
0
        bComplex = false;
816
817
    /* Get the version number, if present (if not, assume old version. */
818
    /* Versions differ in their interpretation of corner coordinates.  */
819
820
0
    if (CSLFetchNameValue(papszAttrib, "version") != nullptr)
821
0
        poDS->SetVersion(static_cast<float>(
822
0
            CPLAtof(CSLFetchNameValue(papszAttrib, "version"))));
823
0
    else
824
0
        poDS->SetVersion(1.0);
825
826
    /* -------------------------------------------------------------------- */
827
    /*      Figure out the datatype                                         */
828
    /* -------------------------------------------------------------------- */
829
0
    const char *pszEncoding = CSLFetchNameValue(papszAttrib, "pixel.encoding");
830
0
    if (pszEncoding == nullptr)
831
0
        pszEncoding = "{ *unsigned }";
832
833
0
    int nSize = 1;
834
0
    if (CSLFetchNameValue(papszAttrib, "pixel.size") != nullptr)
835
0
        nSize = atoi(CSLFetchNameValue(papszAttrib, "pixel.size")) / 8;
836
#if 0
837
    int nPseudoBands;
838
    if( bComplex )
839
        nPseudoBands = 2;
840
    else
841
        nPseudoBands = 1;
842
#endif
843
844
0
    GDALDataType eType;
845
0
    if (nSize == 1)
846
0
        eType = GDT_UInt8;
847
0
    else if (nSize == 2 && strstr(pszEncoding, "*unsigned") != nullptr)
848
0
        eType = GDT_UInt16;
849
0
    else if (nSize == 4 && bComplex)
850
0
        eType = GDT_CInt16;
851
0
    else if (nSize == 2)
852
0
        eType = GDT_Int16;
853
0
    else if (nSize == 4 && strstr(pszEncoding, "*unsigned") != nullptr)
854
0
        eType = GDT_UInt32;
855
0
    else if (nSize == 8 && strstr(pszEncoding, "*two") != nullptr && bComplex)
856
0
        eType = GDT_CInt32;
857
0
    else if (nSize == 4 && strstr(pszEncoding, "*two") != nullptr)
858
0
        eType = GDT_Int32;
859
0
    else if (nSize == 8 && bComplex)
860
0
        eType = GDT_CFloat32;
861
0
    else if (nSize == 4)
862
0
        eType = GDT_Float32;
863
0
    else if (nSize == 16 && bComplex)
864
0
        eType = GDT_CFloat64;
865
0
    else if (nSize == 8)
866
0
        eType = GDT_Float64;
867
0
    else
868
0
    {
869
0
        CPLError(CE_Failure, CPLE_AppDefined,
870
0
                 "Unsupported pixel data type in %s.\n"
871
0
                 "pixel.size=%d pixel.encoding=%s",
872
0
                 poDS->pszPath, nSize, pszEncoding);
873
0
        return nullptr;
874
0
    }
875
876
    /* -------------------------------------------------------------------- */
877
    /*      Open the blob file.                                             */
878
    /* -------------------------------------------------------------------- */
879
0
    osFilename = CPLFormFilenameSafe(poDS->pszPath, "image_data", nullptr);
880
0
    if (VSIStat(osFilename.c_str(), &sStat) != 0)
881
0
        osFilename = CPLFormFilenameSafe(poDS->pszPath, "blob", nullptr);
882
0
    if (poOpenInfo->eAccess == GA_ReadOnly)
883
0
    {
884
0
        poDS->fpBlob = VSIFOpenL(osFilename.c_str(), "rb");
885
0
        if (poDS->fpBlob == nullptr)
886
0
        {
887
0
            CPLError(CE_Failure, CPLE_OpenFailed,
888
0
                     "Unable to open file %s for read access.",
889
0
                     osFilename.c_str());
890
0
            return nullptr;
891
0
        }
892
0
    }
893
0
    else
894
0
    {
895
0
        poDS->fpBlob = VSIFOpenL(osFilename.c_str(), "rb+");
896
0
        if (poDS->fpBlob == nullptr)
897
0
        {
898
0
            CPLError(CE_Failure, CPLE_OpenFailed,
899
0
                     "Unable to open file %s for update access.",
900
0
                     osFilename.c_str());
901
0
            return nullptr;
902
0
        }
903
0
    }
904
905
    /* -------------------------------------------------------------------- */
906
    /*      Build the overview filename, as blob file = "_ovr".             */
907
    /* -------------------------------------------------------------------- */
908
0
    std::string osOvrFilename(osFilename);
909
0
    osOvrFilename += "_ovr";
910
911
    /* -------------------------------------------------------------------- */
912
    /*      Define the bands.                                               */
913
    /* -------------------------------------------------------------------- */
914
0
    const int nPixelOffset = nRawBands * nSize;
915
0
    const int nLineOffset = nPixelOffset * poDS->GetRasterXSize();
916
0
    int nOffset = 0;
917
918
0
    for (int iRawBand = 0; iRawBand < nRawBands; iRawBand++)
919
0
    {
920
0
        auto poBand = std::make_unique<HKVRasterBand>(
921
0
            poDS.get(), poDS->GetRasterCount() + 1, poDS->fpBlob, nOffset,
922
0
            nPixelOffset, nLineOffset, eType, bNative);
923
0
        if (!poBand->IsValid())
924
0
            return nullptr;
925
926
0
        if (bNoDataSet)
927
0
            poBand->SetNoDataValue(dfNoDataValue);
928
0
        poDS->SetBand(poDS->GetRasterCount() + 1, std::move(poBand));
929
0
        nOffset += GDALGetDataTypeSizeBytes(eType);
930
0
    }
931
932
0
    poDS->eRasterType = eType;
933
934
    /* -------------------------------------------------------------------- */
935
    /*      Process the georef file if there is one.                        */
936
    /* -------------------------------------------------------------------- */
937
0
    osFilename = CPLFormFilenameSafe(poDS->pszPath, "georef", nullptr);
938
0
    if (VSIStat(osFilename.c_str(), &sStat) == 0)
939
0
        poDS->ProcessGeoref(osFilename.c_str());
940
941
    /* -------------------------------------------------------------------- */
942
    /*      Initialize any PAM information.                                 */
943
    /* -------------------------------------------------------------------- */
944
0
    poDS->SetDescription(osOvrFilename.c_str());
945
0
    poDS->TryLoadXML();
946
947
    /* -------------------------------------------------------------------- */
948
    /*      Handle overviews.                                               */
949
    /* -------------------------------------------------------------------- */
950
0
    poDS->oOvManager.Initialize(poDS.get(), osOvrFilename.c_str(), nullptr,
951
0
                                TRUE);
952
953
0
    return poDS.release();
954
0
}
955
956
/************************************************************************/
957
/*                          GDALRegister_HKV()                          */
958
/************************************************************************/
959
960
void GDALRegister_HKV()
961
962
22
{
963
22
    if (GDALGetDriverByName("MFF2") != nullptr)
964
0
        return;
965
966
22
    GDALDriver *poDriver = new GDALDriver();
967
968
22
    poDriver->SetDescription("MFF2");
969
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
970
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Vexcel MFF2 (HKV) Raster");
971
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/mff2.html");
972
973
22
    poDriver->pfnOpen = HKVDataset::Open;
974
975
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
976
22
}