LCOV - code coverage report
Current view: top level - frmts/miramon - miramon_dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 186 231 80.5 %
Date: 2025-09-10 17:48:50 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MiraMonRaster driver
       4             :  * Purpose:  Implements MMRDataset class: responsible for generating the
       5             :  *           main dataset or the subdatasets as needed.
       6             :  * Author:   Abel Pau
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2025, Xavier Pons
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "miramon_dataset.h"
      15             : #include "miramon_rasterband.h"
      16             : #include "miramon_band.h"  // Per a MMRBand
      17             : 
      18             : #include "../miramon_common/mm_gdal_functions.h"  // For MMCheck_REL_FILE()
      19             : 
      20             : /************************************************************************/
      21             : /*                GDALRegister_MiraMon()                                */
      22             : /************************************************************************/
      23        2024 : void GDALRegister_MiraMon()
      24             : 
      25             : {
      26        2024 :     if (GDALGetDriverByName("MiraMonRaster") != nullptr)
      27         283 :         return;
      28             : 
      29        1741 :     GDALDriver *poDriver = new GDALDriver();
      30             : 
      31        1741 :     poDriver->SetDescription("MiraMonRaster");
      32        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
      33        1741 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MiraMon Raster Images");
      34        1741 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
      35        1741 :                               "drivers/raster/miramon.html");
      36        1741 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "rel img");
      37             : 
      38        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
      39        1741 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
      40             : 
      41        1741 :     poDriver->pfnOpen = MMRDataset::Open;
      42        1741 :     poDriver->pfnIdentify = MMRDataset::Identify;
      43             : 
      44        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
      45             : }
      46             : 
      47             : /************************************************************************/
      48             : /*                            MMRDataset()                              */
      49             : /************************************************************************/
      50             : 
      51         242 : MMRDataset::MMRDataset(GDALOpenInfo *poOpenInfo)
      52             : {
      53         242 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      54             : 
      55             :     // Creating the class MMRRel.
      56         242 :     auto pMMfRel = std::make_unique<MMRRel>(poOpenInfo->pszFilename, true);
      57         242 :     if (!pMMfRel->IsValid())
      58             :     {
      59         172 :         if (pMMfRel->isAMiraMonFile())
      60             :         {
      61           8 :             CPLError(CE_Failure, CPLE_AppDefined,
      62             :                      "Unable to open %s, probably it's not a MiraMon file.",
      63             :                      poOpenInfo->pszFilename);
      64             :         }
      65         172 :         return;
      66             :     }
      67             : 
      68          70 :     if (pMMfRel->GetNBands() == 0)
      69             :     {
      70           1 :         if (pMMfRel->isAMiraMonFile())
      71             :         {
      72           1 :             CPLError(CE_Failure, CPLE_AppDefined,
      73             :                      "Unable to open %s, it has zero usable bands.",
      74             :                      poOpenInfo->pszFilename);
      75             :         }
      76           1 :         return;
      77             :     }
      78             : 
      79          69 :     m_pMMRRel = std::move(pMMfRel);
      80             : 
      81             :     // General Dataset information available
      82          69 :     nRasterXSize = m_pMMRRel->GetColumnsNumberFromREL();
      83          69 :     nRasterYSize = m_pMMRRel->GetRowsNumberFromREL();
      84          69 :     ReadProjection();
      85          69 :     nBands = 0;
      86             : 
      87             :     // Assign every band to a subdataset (if any)
      88             :     // If all bands should go to a one single Subdataset, then,
      89             :     // no subdataset will be created and all bands will go to this
      90             :     // dataset.
      91          69 :     AssignBandsToSubdataSets();
      92             : 
      93             :     // Create subdatasets or add bands, as needed
      94          69 :     if (m_nNSubdataSets)
      95             :     {
      96           6 :         CreateSubdatasetsFromBands();
      97             :         // Fills adfGeoTransform if documented
      98           6 :         UpdateGeoTransform();
      99             :     }
     100             :     else
     101             :     {
     102          63 :         if (!CreateRasterBands())
     103           0 :             return;
     104             : 
     105             :         // Fills adfGeoTransform if documented. If not, then gets one from last band.
     106          63 :         if (1 == UpdateGeoTransform())
     107             :         {
     108          10 :             MMRBand *poBand = m_pMMRRel->GetBand(m_pMMRRel->GetNBands() - 1);
     109          10 :             if (poBand)
     110          10 :                 m_gt = poBand->m_gt;
     111             :         }
     112             :     }
     113             : 
     114             :     // Make sure we don't try to do any pam stuff with this dataset.
     115          69 :     nPamFlags |= GPF_NOSAVE;
     116             : 
     117             :     // We have a valid DataSet.
     118          69 :     m_bIsValid = true;
     119             : }
     120             : 
     121             : /************************************************************************/
     122             : /*                           ~MMRDataset()                              */
     123             : /************************************************************************/
     124             : 
     125         484 : MMRDataset::~MMRDataset()
     126             : 
     127             : {
     128         484 : }
     129             : 
     130             : /************************************************************************/
     131             : /*                              Identify()                              */
     132             : /************************************************************************/
     133       55494 : int MMRDataset::Identify(GDALOpenInfo *poOpenInfo)
     134             : {
     135             :     // Checking for subdataset
     136             :     int nIdentifyResult =
     137       55494 :         MMRRel::IdentifySubdataSetFile(poOpenInfo->pszFilename);
     138       55492 :     if (nIdentifyResult != GDAL_IDENTIFY_FALSE)
     139          12 :         return nIdentifyResult;
     140             : 
     141             :     // Checking for MiraMon raster file
     142       55480 :     return MMRRel::IdentifyFile(poOpenInfo);
     143             : }
     144             : 
     145             : /************************************************************************/
     146             : /*                                Open()                                */
     147             : /************************************************************************/
     148         242 : GDALDataset *MMRDataset::Open(GDALOpenInfo *poOpenInfo)
     149             : 
     150             : {
     151             :     // Verify that this is a MMR file.
     152         242 :     if (!Identify(poOpenInfo))
     153           0 :         return nullptr;
     154             : 
     155             :     // Confirm the requested access is supported.
     156         242 :     if (poOpenInfo->eAccess == GA_Update)
     157             :     {
     158           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     159             :                  "The MiraMonRaster driver does not support update "
     160             :                  "access to existing datasets.");
     161           0 :         return nullptr;
     162             :     }
     163             : 
     164             :     // Create the Dataset (with bands or Subdatasets).
     165         484 :     auto poDS = std::make_unique<MMRDataset>(poOpenInfo);
     166         242 :     if (!poDS->IsValid())
     167         173 :         return nullptr;
     168             : 
     169             :     // Set description
     170          69 :     poDS->SetDescription(poOpenInfo->pszFilename);
     171             : 
     172          69 :     return poDS.release();
     173             : }
     174             : 
     175          63 : bool MMRDataset::CreateRasterBands()
     176             : {
     177             :     MMRBand *pBand;
     178             : 
     179         126 :     for (int nIBand = 0; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     180             :     {
     181             :         // Establish raster band info.
     182          63 :         pBand = m_pMMRRel->GetBand(nIBand);
     183          63 :         if (!pBand)
     184           0 :             return false;
     185          63 :         nRasterXSize = pBand->GetWidth();
     186          63 :         nRasterYSize = pBand->GetHeight();
     187          63 :         pBand->UpdateGeoTransform();  // Fills adfGeoTransform for this band
     188             : 
     189          63 :         auto poRasterBand = std::make_unique<MMRRasterBand>(this, nBands + 1);
     190          63 :         if (!poRasterBand->IsValid())
     191             :         {
     192           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     193             :                      "Failed to create a RasterBand from '%s'",
     194             :                      m_pMMRRel->GetRELNameChar());
     195             : 
     196           0 :             return false;
     197             :         }
     198             : 
     199          63 :         SetBand(nBands + 1, std::move(poRasterBand));
     200             : 
     201             :         MMRRasterBand *poBand =
     202          63 :             cpl::down_cast<MMRRasterBand *>(GetRasterBand(nIBand + 1));
     203             : 
     204          63 :         pBand = m_pMMRRel->GetBand(nIBand);
     205          63 :         if (!pBand)
     206           0 :             return false;
     207          63 :         if (!pBand->GetFriendlyDescription().empty())
     208             :         {
     209          53 :             poBand->SetMetadataItem("DESCRIPTION",
     210          53 :                                     pBand->GetFriendlyDescription());
     211             :         }
     212             :     }
     213             :     // Some metadata items must be preserved just in case to be restored
     214             :     // if they are preserved through translations.
     215          63 :     m_pMMRRel->RELToGDALMetadata(this);
     216             : 
     217          63 :     return true;
     218             : }
     219             : 
     220          69 : void MMRDataset::ReadProjection()
     221             : 
     222             : {
     223          69 :     if (!m_pMMRRel)
     224           0 :         return;
     225             : 
     226         138 :     CPLString osSRS;
     227             : 
     228         138 :     if (!m_pMMRRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
     229         207 :                                      "HorizontalSystemIdentifier", osSRS) ||
     230          69 :         osSRS.empty())
     231           0 :         return;
     232             : 
     233             :     char szResult[MM_MAX_ID_SNY + 10];
     234          69 :     int nResult = ReturnEPSGCodeSRSFromMMIDSRS(osSRS.c_str(), szResult);
     235          69 :     if (nResult == 1 || szResult[0] == '\0')
     236          10 :         return;
     237             : 
     238             :     int nEPSG;
     239          59 :     if (1 == sscanf(szResult, "%d", &nEPSG))
     240          59 :         m_oSRS.importFromEPSG(nEPSG);
     241             : 
     242          59 :     return;
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                           SUBDATASETS                                */
     247             : /************************************************************************/
     248             : // Assigns every band to a subdataset
     249          69 : void MMRDataset::AssignBandsToSubdataSets()
     250             : {
     251          69 :     m_nNSubdataSets = 0;
     252          69 :     if (!m_pMMRRel.get())
     253           0 :         return;
     254             : 
     255          69 :     m_nNSubdataSets = 1;
     256          69 :     int nIBand = 0;
     257          69 :     MMRBand *pBand = m_pMMRRel->GetBand(nIBand);
     258          69 :     if (!pBand)
     259           0 :         return;
     260             : 
     261          69 :     pBand->AssignSubDataSet(m_nNSubdataSets);
     262             :     MMRBand *pNextBand;
     263          81 :     for (; nIBand < m_pMMRRel->GetNBands() - 1; nIBand++)
     264             :     {
     265          12 :         if (IsNextBandInANewDataSet(nIBand))
     266             :         {
     267          12 :             m_nNSubdataSets++;
     268          12 :             pNextBand = m_pMMRRel->GetBand(nIBand + 1);
     269          12 :             if (!pNextBand)
     270           0 :                 return;
     271          12 :             pNextBand->AssignSubDataSet(m_nNSubdataSets);
     272             :         }
     273             :         else
     274             :         {
     275           0 :             pNextBand = m_pMMRRel->GetBand(nIBand + 1);
     276           0 :             if (!pNextBand)
     277           0 :                 return;
     278           0 :             pNextBand->AssignSubDataSet(m_nNSubdataSets);
     279             :         }
     280             :     }
     281             : 
     282             :     // If there is only one subdataset, it means that
     283             :     // we don't need subdatasets (all assigned to 0)
     284          69 :     if (m_nNSubdataSets == 1)
     285             :     {
     286          63 :         m_nNSubdataSets = 0;
     287         126 :         for (nIBand = 0; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     288             :         {
     289          63 :             pBand = m_pMMRRel->GetBand(nIBand);
     290          63 :             if (!pBand)
     291           0 :                 break;
     292          63 :             pBand->AssignSubDataSet(m_nNSubdataSets);
     293             :         }
     294             :     }
     295             : }
     296             : 
     297           6 : void MMRDataset::CreateSubdatasetsFromBands()
     298             : {
     299           6 :     CPLStringList oSubdatasetList;
     300           6 :     CPLString osDSName;
     301           6 :     CPLString osDSDesc;
     302             :     MMRBand *pBand;
     303             : 
     304          24 :     for (int iSubdataset = 1; iSubdataset <= m_nNSubdataSets; iSubdataset++)
     305             :     {
     306             :         int nIBand;
     307          36 :         for (nIBand = 0; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     308             :         {
     309          36 :             pBand = m_pMMRRel->GetBand(nIBand);
     310          36 :             if (!pBand)
     311           0 :                 return;
     312          36 :             if (pBand->GetAssignedSubDataSet() == iSubdataset)
     313          18 :                 break;
     314             :         }
     315             : 
     316          18 :         if (nIBand == m_pMMRRel->GetNBands())
     317           0 :             break;
     318             : 
     319          18 :         pBand = m_pMMRRel->GetBand(nIBand);
     320          18 :         if (!pBand)
     321           0 :             return;
     322             : 
     323             :         osDSName.Printf("MiraMonRaster:\"%s\",\"%s\"",
     324          36 :                         pBand->GetRELFileName().c_str(),
     325          36 :                         pBand->GetRawBandFileName().c_str());
     326             :         osDSDesc.Printf("Subdataset %d: \"%s\"", iSubdataset,
     327          18 :                         pBand->GetBandName().c_str());
     328          18 :         nIBand++;
     329             : 
     330          36 :         for (; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     331             :         {
     332          18 :             pBand = m_pMMRRel->GetBand(nIBand);
     333          18 :             if (!pBand)
     334           0 :                 return;
     335          18 :             if (pBand->GetAssignedSubDataSet() != iSubdataset)
     336          18 :                 continue;
     337             : 
     338             :             osDSName.append(
     339           0 :                 CPLSPrintf(",\"%s\"", pBand->GetRawBandFileName().c_str()));
     340             :             osDSDesc.append(
     341           0 :                 CPLSPrintf(",\"%s\"", pBand->GetBandName().c_str()));
     342             :         }
     343             : 
     344             :         oSubdatasetList.AddNameValue(
     345          18 :             CPLSPrintf("SUBDATASET_%d_NAME", iSubdataset), osDSName);
     346             :         oSubdatasetList.AddNameValue(
     347          18 :             CPLSPrintf("SUBDATASET_%d_DESC", iSubdataset), osDSDesc);
     348             :     }
     349             : 
     350           6 :     if (oSubdatasetList.Count() > 0)
     351             :     {
     352             :         // Add metadata to the main dataset
     353           6 :         SetMetadata(oSubdatasetList.List(), "SUBDATASETS");
     354           6 :         oSubdatasetList.Clear();
     355             :     }
     356             : }
     357             : 
     358          12 : bool MMRDataset::IsNextBandInANewDataSet(int nIBand) const
     359             : {
     360          12 :     if (nIBand < 0)
     361           0 :         return false;
     362             : 
     363          12 :     if (nIBand + 1 >= m_pMMRRel->GetNBands())
     364           0 :         return false;
     365             : 
     366          12 :     MMRBand *pThisBand = m_pMMRRel->GetBand(nIBand);
     367          12 :     MMRBand *pNextBand = m_pMMRRel->GetBand(nIBand + 1);
     368          12 :     if (!pThisBand || !pNextBand)
     369           0 :         return false;
     370             : 
     371             :     // Two images with different numbers of columns are assigned to different subdatasets
     372          12 :     if (pThisBand->GetWidth() != pNextBand->GetWidth())
     373           0 :         return true;
     374             : 
     375             :     // Two images with different numbers of rows are assigned to different subdatasets
     376          12 :     if (pThisBand->GetHeight() != pNextBand->GetHeight())
     377           0 :         return true;
     378             : 
     379             :     // Two images with different bounding box are assigned to different subdatasets
     380          12 :     if (pThisBand->GetBoundingBoxMinX() != pNextBand->GetBoundingBoxMinX())
     381           0 :         return true;
     382          12 :     if (pThisBand->GetBoundingBoxMaxX() != pNextBand->GetBoundingBoxMaxX())
     383           0 :         return true;
     384          12 :     if (pThisBand->GetBoundingBoxMinY() != pNextBand->GetBoundingBoxMinY())
     385           0 :         return true;
     386          12 :     if (pThisBand->GetBoundingBoxMaxY() != pNextBand->GetBoundingBoxMaxY())
     387           0 :         return true;
     388             : 
     389             :     // One image has NoData values and the other does not;
     390             :     // they are assigned to different subdatasets
     391          12 :     if (pThisBand->BandHasNoData() != pNextBand->BandHasNoData())
     392           6 :         return true;
     393             : 
     394             :     // Two images with different NoData values are assigned to different subdatasets
     395           6 :     if (pThisBand->GetNoDataValue() != pNextBand->GetNoDataValue())
     396           6 :         return true;
     397             : 
     398           0 :     return false;
     399             : }
     400             : 
     401             : /************************************************************************/
     402             : /*                          UpdateGeoTransform()                     */
     403             : /************************************************************************/
     404          69 : int MMRDataset::UpdateGeoTransform()
     405             : {
     406             :     // Bounding box of the band
     407             :     // Section [EXTENT] in rel file
     408             : 
     409          69 :     if (!m_pMMRRel)
     410           0 :         return 1;
     411             : 
     412         138 :     CPLString osMinX;
     413         128 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MinX", osMinX) ||
     414          59 :         osMinX.empty())
     415          10 :         return 1;
     416             : 
     417          59 :     if (1 != CPLsscanf(osMinX, "%lf", &(m_gt[0])))
     418           0 :         m_gt[0] = 0.0;
     419             : 
     420          59 :     int nNCols = m_pMMRRel->GetColumnsNumberFromREL();
     421          59 :     if (nNCols <= 0)
     422           0 :         return 1;
     423             : 
     424         118 :     CPLString osMaxX;
     425         118 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MaxX", osMaxX) ||
     426          59 :         osMaxX.empty())
     427           0 :         return 1;
     428             : 
     429             :     double dfMaxX;
     430          59 :     if (1 != CPLsscanf(osMaxX, "%lf", &dfMaxX))
     431           0 :         dfMaxX = 1.0;
     432             : 
     433          59 :     m_gt[1] = (dfMaxX - m_gt[0]) / nNCols;
     434          59 :     m_gt[2] = 0.0;  // No rotation in MiraMon rasters
     435             : 
     436         118 :     CPLString osMinY;
     437         118 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MinY", osMinY) ||
     438          59 :         osMinY.empty())
     439           0 :         return 1;
     440             : 
     441         118 :     CPLString osMaxY;
     442         118 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MaxY", osMaxY) ||
     443          59 :         osMaxY.empty())
     444           0 :         return 1;
     445             : 
     446          59 :     int nNRows = m_pMMRRel->GetRowsNumberFromREL();
     447          59 :     if (nNRows <= 0)
     448           0 :         return 1;
     449             : 
     450             :     double dfMaxY;
     451          59 :     if (1 != CPLsscanf(osMaxY, "%lf", &dfMaxY))
     452           0 :         dfMaxY = 1.0;
     453             : 
     454          59 :     m_gt[3] = dfMaxY;
     455          59 :     m_gt[4] = 0.0;
     456             : 
     457             :     double dfMinY;
     458          59 :     if (1 != CPLsscanf(osMinY, "%lf", &dfMinY))
     459           0 :         dfMinY = 0.0;
     460          59 :     m_gt[5] = (dfMinY - m_gt[3]) / nNRows;
     461             : 
     462          59 :     return 0;
     463             : }
     464             : 
     465          13 : const OGRSpatialReference *MMRDataset::GetSpatialRef() const
     466             : {
     467          13 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     468             : }
     469             : 
     470          35 : CPLErr MMRDataset::GetGeoTransform(GDALGeoTransform &gt) const
     471             : {
     472          38 :     if (m_gt[0] != 0.0 || m_gt[1] != 1.0 || m_gt[2] != 0.0 || m_gt[3] != 0.0 ||
     473          38 :         m_gt[4] != 0.0 || m_gt[5] != 1.0)
     474             :     {
     475          35 :         gt = m_gt;
     476          35 :         return CE_None;
     477             :     }
     478             : 
     479           0 :     return GDALDataset::GetGeoTransform(gt);
     480             : }

Generated by: LCOV version 1.14