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

Generated by: LCOV version 1.14