Coverage Report

Created: 2025-06-09 07:07

/src/gdal/frmts/map/mapdataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OziExplorer .MAP Driver
4
 * Purpose:  GDALDataset driver for OziExplorer .MAP files
5
 * Author:   Jean-Claude Repetto, <jrepetto at @free dot fr>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2012, Jean-Claude Repetto
9
 * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "gdal_frmts.h"
15
#include "gdal_pam.h"
16
#include "gdal_proxy.h"
17
#include "ogr_geometry.h"
18
#include "ogr_spatialref.h"
19
20
/************************************************************************/
21
/* ==================================================================== */
22
/*                                MAPDataset                            */
23
/* ==================================================================== */
24
/************************************************************************/
25
26
class MAPDataset final : public GDALDataset
27
{
28
    GDALDataset *poImageDS;
29
30
    OGRSpatialReference m_oSRS{};
31
    int bGeoTransformValid;
32
    double adfGeoTransform[6];
33
    int nGCPCount;
34
    GDAL_GCP *pasGCPList;
35
    OGRPolygon *poNeatLine;
36
    CPLString osImgFilename;
37
38
  public:
39
    MAPDataset();
40
    virtual ~MAPDataset();
41
42
    const OGRSpatialReference *GetSpatialRef() const override;
43
    virtual CPLErr GetGeoTransform(double *) override;
44
    virtual int GetGCPCount() override;
45
    const OGRSpatialReference *GetGCPSpatialRef() const override;
46
    virtual const GDAL_GCP *GetGCPs() override;
47
    virtual char **GetFileList() override;
48
49
    virtual int CloseDependentDatasets() override;
50
51
    static GDALDataset *Open(GDALOpenInfo *);
52
    static int Identify(GDALOpenInfo *poOpenInfo);
53
};
54
55
/************************************************************************/
56
/* ==================================================================== */
57
/*                         MAPWrapperRasterBand                         */
58
/* ==================================================================== */
59
/************************************************************************/
60
class MAPWrapperRasterBand final : public GDALProxyRasterBand
61
{
62
    GDALRasterBand *poBaseBand;
63
64
  protected:
65
    virtual GDALRasterBand *
66
    RefUnderlyingRasterBand(bool /*bForceOpen*/) const override;
67
68
  public:
69
    explicit MAPWrapperRasterBand(GDALRasterBand *poBaseBandIn)
70
14
    {
71
14
        this->poBaseBand = poBaseBandIn;
72
14
        eDataType = poBaseBand->GetRasterDataType();
73
14
        poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
74
14
    }
75
76
    ~MAPWrapperRasterBand()
77
0
    {
78
0
    }
79
};
80
81
GDALRasterBand *
82
MAPWrapperRasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
83
0
{
84
0
    return poBaseBand;
85
0
}
86
87
/************************************************************************/
88
/* ==================================================================== */
89
/*                             MAPDataset                               */
90
/* ==================================================================== */
91
/************************************************************************/
92
93
MAPDataset::MAPDataset()
94
419
    : poImageDS(nullptr), bGeoTransformValid(false), nGCPCount(0),
95
419
      pasGCPList(nullptr), poNeatLine(nullptr)
96
419
{
97
419
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
98
419
    adfGeoTransform[0] = 0.0;
99
419
    adfGeoTransform[1] = 1.0;
100
419
    adfGeoTransform[2] = 0.0;
101
419
    adfGeoTransform[3] = 0.0;
102
419
    adfGeoTransform[4] = 0.0;
103
419
    adfGeoTransform[5] = 1.0;
104
419
}
105
106
/************************************************************************/
107
/*                            ~MAPDataset()                             */
108
/************************************************************************/
109
110
MAPDataset::~MAPDataset()
111
112
419
{
113
419
    if (poImageDS != nullptr)
114
14
    {
115
14
        GDALClose(poImageDS);
116
14
        poImageDS = nullptr;
117
14
    }
118
119
419
    if (nGCPCount)
120
0
    {
121
0
        GDALDeinitGCPs(nGCPCount, pasGCPList);
122
0
        CPLFree(pasGCPList);
123
0
    }
124
125
419
    if (poNeatLine != nullptr)
126
3
    {
127
3
        delete poNeatLine;
128
3
        poNeatLine = nullptr;
129
3
    }
130
419
}
131
132
/************************************************************************/
133
/*                       CloseDependentDatasets()                       */
134
/************************************************************************/
135
136
int MAPDataset::CloseDependentDatasets()
137
0
{
138
0
    int bRet = GDALDataset::CloseDependentDatasets();
139
0
    if (poImageDS != nullptr)
140
0
    {
141
0
        GDALClose(poImageDS);
142
0
        poImageDS = nullptr;
143
0
        bRet = TRUE;
144
0
    }
145
0
    return bRet;
146
0
}
147
148
/************************************************************************/
149
/*                              Identify()                              */
150
/************************************************************************/
151
152
int MAPDataset::Identify(GDALOpenInfo *poOpenInfo)
153
154
16.2k
{
155
16.2k
    if (poOpenInfo->nHeaderBytes < 200 ||
156
16.2k
        !poOpenInfo->IsExtensionEqualToCI("MAP"))
157
14.6k
        return FALSE;
158
159
1.61k
    if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
160
1.61k
               "OziExplorer Map Data File") == nullptr)
161
776
        return FALSE;
162
163
838
    return TRUE;
164
1.61k
}
165
166
/************************************************************************/
167
/*                                Open()                                */
168
/************************************************************************/
169
170
GDALDataset *MAPDataset::Open(GDALOpenInfo *poOpenInfo)
171
419
{
172
419
    if (!Identify(poOpenInfo))
173
0
        return nullptr;
174
175
    /* -------------------------------------------------------------------- */
176
    /*      Confirm the requested access is supported.                      */
177
    /* -------------------------------------------------------------------- */
178
419
    if (poOpenInfo->eAccess == GA_Update)
179
0
    {
180
0
        ReportUpdateNotSupportedByDriver("MAP");
181
0
        return nullptr;
182
0
    }
183
184
    /* -------------------------------------------------------------------- */
185
    /*      Create a corresponding GDALDataset.                             */
186
    /* -------------------------------------------------------------------- */
187
188
419
    MAPDataset *poDS = new MAPDataset();
189
190
    /* -------------------------------------------------------------------- */
191
    /*      Try to load and parse the .MAP file.                            */
192
    /* -------------------------------------------------------------------- */
193
194
419
    char *pszWKT = nullptr;
195
419
    bool bOziFileOK = CPL_TO_BOOL(
196
419
        GDALLoadOziMapFile(poOpenInfo->pszFilename, poDS->adfGeoTransform,
197
419
                           &pszWKT, &poDS->nGCPCount, &poDS->pasGCPList));
198
419
    if (pszWKT)
199
0
    {
200
0
        poDS->m_oSRS.importFromWkt(pszWKT);
201
0
        CPLFree(pszWKT);
202
0
    }
203
204
419
    if (bOziFileOK && poDS->nGCPCount == 0)
205
0
        poDS->bGeoTransformValid = TRUE;
206
207
    /* We need to read again the .map file because the GDALLoadOziMapFile
208
       function does not returns all required data . An API change is necessary
209
       : maybe in GDAL 2.0 ? */
210
211
419
    char **papszLines = CSLLoad2(poOpenInfo->pszFilename, 200, 200, nullptr);
212
213
419
    if (!papszLines)
214
6
    {
215
6
        delete poDS;
216
6
        return nullptr;
217
6
    }
218
219
413
    const int nLines = CSLCount(papszLines);
220
413
    if (nLines < 3)
221
4
    {
222
4
        delete poDS;
223
4
        CSLDestroy(papszLines);
224
4
        return nullptr;
225
4
    }
226
227
    /* -------------------------------------------------------------------- */
228
    /*      We need to open the image in order to establish                 */
229
    /*      details like the band count and types.                          */
230
    /* -------------------------------------------------------------------- */
231
409
    poDS->osImgFilename = papszLines[2];
232
233
409
    const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
234
409
    if (CPLIsFilenameRelative(poDS->osImgFilename))
235
216
    {
236
216
        poDS->osImgFilename =
237
216
            CPLFormCIFilenameSafe(osPath, poDS->osImgFilename, nullptr);
238
216
    }
239
193
    else
240
193
    {
241
193
        VSIStatBufL sStat;
242
193
        if (VSIStatL(poDS->osImgFilename, &sStat) != 0)
243
106
        {
244
106
            poDS->osImgFilename = CPLGetFilename(poDS->osImgFilename);
245
106
            poDS->osImgFilename =
246
106
                CPLFormCIFilenameSafe(osPath, poDS->osImgFilename, nullptr);
247
106
        }
248
193
    }
249
250
    /* -------------------------------------------------------------------- */
251
    /*      Try and open the file.                                          */
252
    /* -------------------------------------------------------------------- */
253
409
    poDS->poImageDS =
254
409
        GDALDataset::FromHandle(GDALOpen(poDS->osImgFilename, GA_ReadOnly));
255
409
    if (poDS->poImageDS == nullptr || poDS->poImageDS->GetRasterCount() == 0)
256
395
    {
257
395
        CSLDestroy(papszLines);
258
395
        delete poDS;
259
395
        return nullptr;
260
395
    }
261
262
    /* -------------------------------------------------------------------- */
263
    /*      Attach the bands.                                               */
264
    /* -------------------------------------------------------------------- */
265
14
    poDS->nRasterXSize = poDS->poImageDS->GetRasterXSize();
266
14
    poDS->nRasterYSize = poDS->poImageDS->GetRasterYSize();
267
14
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
268
0
    {
269
0
        CSLDestroy(papszLines);
270
0
        GDALClose(poDS->poImageDS);
271
0
        delete poDS;
272
0
        return nullptr;
273
0
    }
274
275
28
    for (int iBand = 1; iBand <= poDS->poImageDS->GetRasterCount(); iBand++)
276
14
        poDS->SetBand(iBand, new MAPWrapperRasterBand(
277
14
                                 poDS->poImageDS->GetRasterBand(iBand)));
278
279
    /* -------------------------------------------------------------------- */
280
    /*      Add the neatline/cutline, if required                           */
281
    /* -------------------------------------------------------------------- */
282
283
    /* First, we need to check if it is necessary to define a neatline */
284
14
    bool bNeatLine = false;
285
961
    for (int iLine = 10; iLine < nLines; iLine++)
286
950
    {
287
950
        if (STARTS_WITH_CI(papszLines[iLine], "MMPXY,"))
288
24
        {
289
24
            char **papszTok =
290
24
                CSLTokenizeString2(papszLines[iLine], ",",
291
24
                                   CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
292
293
24
            if (CSLCount(papszTok) != 4)
294
21
            {
295
21
                CSLDestroy(papszTok);
296
21
                continue;
297
21
            }
298
299
3
            const int x = atoi(papszTok[2]);
300
3
            const int y = atoi(papszTok[3]);
301
3
            if ((x != 0 && x != poDS->nRasterXSize) ||
302
3
                (y != 0 && y != poDS->nRasterYSize))
303
3
            {
304
3
                bNeatLine = true;
305
3
                CSLDestroy(papszTok);
306
3
                break;
307
3
            }
308
0
            CSLDestroy(papszTok);
309
0
        }
310
950
    }
311
312
    /* Create and fill the neatline polygon */
313
14
    if (bNeatLine)
314
3
    {
315
3
        poDS->poNeatLine =
316
3
            new OGRPolygon(); /* Create a polygon to store the neatline */
317
3
        OGRLinearRing *poRing = new OGRLinearRing();
318
319
3
        if (poDS->bGeoTransformValid) /* Compute the projected coordinates of
320
                                         the corners */
321
0
        {
322
0
            for (int iLine = 10; iLine < nLines; iLine++)
323
0
            {
324
0
                if (STARTS_WITH_CI(papszLines[iLine], "MMPXY,"))
325
0
                {
326
0
                    char **papszTok = CSLTokenizeString2(
327
0
                        papszLines[iLine], ",",
328
0
                        CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
329
330
0
                    if (CSLCount(papszTok) != 4)
331
0
                    {
332
0
                        CSLDestroy(papszTok);
333
0
                        continue;
334
0
                    }
335
336
0
                    const double x = CPLAtofM(papszTok[2]);
337
0
                    const double y = CPLAtofM(papszTok[3]);
338
0
                    const double X = poDS->adfGeoTransform[0] +
339
0
                                     x * poDS->adfGeoTransform[1] +
340
0
                                     y * poDS->adfGeoTransform[2];
341
0
                    const double Y = poDS->adfGeoTransform[3] +
342
0
                                     x * poDS->adfGeoTransform[4] +
343
0
                                     y * poDS->adfGeoTransform[5];
344
0
                    poRing->addPoint(X, Y);
345
0
                    CPLDebug("CORNER MMPXY", "%f, %f, %f, %f", x, y, X, Y);
346
0
                    CSLDestroy(papszTok);
347
0
                }
348
0
            }
349
0
        }
350
3
        else /* Convert the geographic coordinates to projected coordinates */
351
3
        {
352
3
            OGRCoordinateTransformation *poTransform = nullptr;
353
3
            if (!poDS->m_oSRS.IsEmpty())
354
0
            {
355
0
                OGRSpatialReference *poLongLat = poDS->m_oSRS.CloneGeogCS();
356
0
                if (poLongLat)
357
0
                {
358
0
                    poLongLat->SetAxisMappingStrategy(
359
0
                        OAMS_TRADITIONAL_GIS_ORDER);
360
0
                    poTransform = OGRCreateCoordinateTransformation(
361
0
                        poLongLat, &poDS->m_oSRS);
362
0
                    delete poLongLat;
363
0
                }
364
0
            }
365
366
58
            for (int iLine = 10; iLine < nLines; iLine++)
367
55
            {
368
55
                if (STARTS_WITH_CI(papszLines[iLine], "MMPLL,"))
369
0
                {
370
0
                    CPLDebug("MMPLL", "%s", papszLines[iLine]);
371
372
0
                    char **papszTok = CSLTokenizeString2(
373
0
                        papszLines[iLine], ",",
374
0
                        CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
375
376
0
                    if (CSLCount(papszTok) != 4)
377
0
                    {
378
0
                        CSLDestroy(papszTok);
379
0
                        continue;
380
0
                    }
381
382
0
                    double dfLon = CPLAtofM(papszTok[2]);
383
0
                    double dfLat = CPLAtofM(papszTok[3]);
384
385
0
                    if (poTransform)
386
0
                        poTransform->Transform(1, &dfLon, &dfLat);
387
0
                    poRing->addPoint(dfLon, dfLat);
388
0
                    CPLDebug("CORNER MMPLL", "%f, %f", dfLon, dfLat);
389
0
                    CSLDestroy(papszTok);
390
0
                }
391
55
            }
392
3
            if (poTransform)
393
0
                delete poTransform;
394
3
        }
395
396
3
        poRing->closeRings();
397
3
        poDS->poNeatLine->addRingDirectly(poRing);
398
399
3
        char *pszNeatLineWkt = nullptr;
400
3
        poDS->poNeatLine->exportToWkt(&pszNeatLineWkt);
401
3
        CPLDebug("NEATLINE", "%s", pszNeatLineWkt);
402
3
        poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
403
3
        CPLFree(pszNeatLineWkt);
404
3
    }
405
406
14
    CSLDestroy(papszLines);
407
408
14
    return poDS;
409
14
}
410
411
/************************************************************************/
412
/*                          GetSpatialRef()                             */
413
/************************************************************************/
414
415
const OGRSpatialReference *MAPDataset::GetSpatialRef() const
416
0
{
417
0
    return (!m_oSRS.IsEmpty() && nGCPCount == 0) ? &m_oSRS : nullptr;
418
0
}
419
420
/************************************************************************/
421
/*                          GetGeoTransform()                           */
422
/************************************************************************/
423
424
CPLErr MAPDataset::GetGeoTransform(double *padfTransform)
425
426
0
{
427
0
    memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
428
429
0
    return (nGCPCount == 0) ? CE_None : CE_Failure;
430
0
}
431
432
/************************************************************************/
433
/*                           GetGCPCount()                              */
434
/************************************************************************/
435
436
int MAPDataset::GetGCPCount()
437
0
{
438
0
    return nGCPCount;
439
0
}
440
441
/************************************************************************/
442
/*                          GetGCPSpatialRef()                          */
443
/************************************************************************/
444
445
const OGRSpatialReference *MAPDataset::GetGCPSpatialRef() const
446
0
{
447
0
    return (!m_oSRS.IsEmpty() && nGCPCount != 0) ? &m_oSRS : nullptr;
448
0
}
449
450
/************************************************************************/
451
/*                               GetGCPs()                              */
452
/************************************************************************/
453
454
const GDAL_GCP *MAPDataset::GetGCPs()
455
0
{
456
0
    return pasGCPList;
457
0
}
458
459
/************************************************************************/
460
/*                            GetFileList()                             */
461
/************************************************************************/
462
463
char **MAPDataset::GetFileList()
464
0
{
465
0
    char **papszFileList = GDALDataset::GetFileList();
466
467
0
    papszFileList = CSLAddString(papszFileList, osImgFilename);
468
469
0
    return papszFileList;
470
0
}
471
472
/************************************************************************/
473
/*                          GDALRegister_MAP()                          */
474
/************************************************************************/
475
476
void GDALRegister_MAP()
477
478
2
{
479
2
    if (GDALGetDriverByName("MAP") != nullptr)
480
0
        return;
481
482
2
    GDALDriver *poDriver = new GDALDriver();
483
484
2
    poDriver->SetDescription("MAP");
485
2
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
486
2
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "OziExplorer .MAP");
487
2
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/map.html");
488
489
2
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
490
491
2
    poDriver->pfnOpen = MAPDataset::Open;
492
2
    poDriver->pfnIdentify = MAPDataset::Identify;
493
494
2
    GetGDALDriverManager()->RegisterDriver(poDriver);
495
2
}