LCOV - code coverage report
Current view: top level - frmts/map - mapdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 18 182 9.9 %
Date: 2025-07-08 21:33:46 Functions: 2 17 11.8 %

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

Generated by: LCOV version 1.14