LCOV - code coverage report
Current view: top level - frmts/map - mapdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 16 165 9.7 %
Date: 2025-09-10 17:48:50 Functions: 2 15 13.3 %

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

Generated by: LCOV version 1.14