LCOV - code coverage report
Current view: top level - frmts/miramon - miramon_dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 225 277 81.2 %
Date: 2026-02-12 23:49:34 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 <algorithm>
      15             : 
      16             : #include "miramon_dataset.h"
      17             : #include "miramon_rasterband.h"
      18             : #include "miramon_band.h"  // Per a MMRBand
      19             : 
      20             : #include "gdal_frmts.h"
      21             : 
      22             : #include "../miramon_common/mm_gdal_functions.h"  // For MMCheck_REL_FILE()
      23             : 
      24             : /************************************************************************/
      25             : /*                        GDALRegister_MiraMon()                        */
      26             : /************************************************************************/
      27        2059 : void GDALRegister_MiraMon()
      28             : 
      29             : {
      30        2059 :     if (GDALGetDriverByName("MiraMonRaster") != nullptr)
      31         283 :         return;
      32             : 
      33        1776 :     GDALDriver *poDriver = new GDALDriver();
      34             : 
      35        1776 :     poDriver->SetDescription("MiraMonRaster");
      36        1776 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
      37        1776 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MiraMon Raster Images");
      38        1776 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
      39        1776 :                               "drivers/raster/miramon.html");
      40        1776 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "rel img");
      41             : 
      42        1776 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
      43        1776 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
      44             : 
      45        1776 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
      46             : 
      47        1776 :     poDriver->SetMetadataItem(
      48             :         GDAL_DMD_OPENOPTIONLIST,
      49             :         "<OpenOptionList>\n"
      50             :         "   <Option name='RAT_OR_CT' type='string-select' "
      51             :         "description='Controls whether the Raster Attribute Table (RAT) "
      52             :         "and/or the Color Table (CT) are exposed.' default='ALL'>\n"
      53             :         "       <Value>ALL</Value>\n"
      54             :         "       <Value>RAT</Value>\n"
      55             :         "       <Value>CT</Value>\n"
      56             :         "   </Option>\n"
      57        1776 :         "</OpenOptionList>\n");
      58             : 
      59        1776 :     poDriver->pfnOpen = MMRDataset::Open;
      60        1776 :     poDriver->pfnIdentify = MMRDataset::Identify;
      61             : 
      62        1776 :     GetGDALDriverManager()->RegisterDriver(poDriver);
      63             : }
      64             : 
      65             : /************************************************************************/
      66             : /*                             MMRDataset()                             */
      67             : /************************************************************************/
      68             : 
      69         243 : MMRDataset::MMRDataset(GDALOpenInfo *poOpenInfo)
      70             : {
      71         243 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      72             : 
      73             :     // Creating the class MMRRel.
      74         243 :     auto pMMfRel = std::make_unique<MMRRel>(poOpenInfo->pszFilename, true);
      75         243 :     if (!pMMfRel->IsValid())
      76             :     {
      77         174 :         if (pMMfRel->isAMiraMonFile())
      78             :         {
      79           8 :             CPLError(CE_Failure, CPLE_AppDefined,
      80             :                      "Unable to open %s, probably it's not a MiraMon file.",
      81             :                      poOpenInfo->pszFilename);
      82             :         }
      83         174 :         return;
      84             :     }
      85             : 
      86          69 :     if (pMMfRel->GetNBands() == 0)
      87             :     {
      88           1 :         if (pMMfRel->isAMiraMonFile())
      89             :         {
      90           1 :             CPLError(CE_Failure, CPLE_AppDefined,
      91             :                      "Unable to open %s, it has zero usable bands.",
      92             :                      poOpenInfo->pszFilename);
      93             :         }
      94           1 :         return;
      95             :     }
      96             : 
      97          68 :     m_pMMRRel = std::move(pMMfRel);
      98             : 
      99             :     // General Dataset information available
     100          68 :     nRasterXSize = m_pMMRRel->GetColumnsNumberFromREL();
     101          68 :     nRasterYSize = m_pMMRRel->GetRowsNumberFromREL();
     102          68 :     ReadProjection();
     103          68 :     nBands = 0;
     104             : 
     105             :     // Getting the open option that determines how to expose subdatasets.
     106             :     // To avoid recusivity subdatasets are exposed as they are.
     107             :     const char *pszDataType =
     108          68 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RAT_OR_CT");
     109          68 :     if (pszDataType != nullptr)
     110             :     {
     111           0 :         if (EQUAL(pszDataType, "RAT"))
     112           0 :             nRatOrCT = RAT_OR_CT::RAT;
     113           0 :         else if (EQUAL(pszDataType, "ALL"))
     114           0 :             nRatOrCT = RAT_OR_CT::ALL;
     115           0 :         else if (EQUAL(pszDataType, "CT"))
     116           0 :             nRatOrCT = RAT_OR_CT::CT;
     117             :     }
     118             : 
     119          68 :     AssignBandsToSubdataSets();
     120             : 
     121             :     // Create subdatasets or add bands, as needed
     122          68 :     if (m_nNSubdataSets)
     123             :     {
     124           7 :         CreateSubdatasetsFromBands();
     125             :         // Fills adfGeoTransform if documented
     126           7 :         UpdateGeoTransform();
     127             :     }
     128             :     else
     129             :     {
     130          61 :         if (!CreateRasterBands())
     131           0 :             return;
     132             : 
     133             :         // GeoTransform of a subdataset is always the same than the first band
     134          61 :         if (m_pMMRRel->GetNBands() >= 1)
     135             :         {
     136          61 :             MMRBand *poBand = m_pMMRRel->GetBand(m_pMMRRel->GetNBands() - 1);
     137          61 :             if (poBand)
     138          61 :                 m_gt = poBand->m_gt;
     139             :         }
     140             :     }
     141             : 
     142             :     // Make sure we don't try to do any pam stuff with this dataset.
     143          68 :     nPamFlags |= GPF_NOSAVE;
     144             : 
     145             :     // We have a valid DataSet.
     146          68 :     m_bIsValid = true;
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                            ~MMRDataset()                             */
     151             : /************************************************************************/
     152             : 
     153         486 : MMRDataset::~MMRDataset()
     154             : 
     155             : {
     156         486 : }
     157             : 
     158             : /************************************************************************/
     159             : /*                              Identify()                              */
     160             : /************************************************************************/
     161       57983 : int MMRDataset::Identify(GDALOpenInfo *poOpenInfo)
     162             : {
     163             :     // Checking for subdataset
     164             :     int nIdentifyResult =
     165       57983 :         MMRRel::IdentifySubdataSetFile(poOpenInfo->pszFilename);
     166       57983 :     if (nIdentifyResult != GDAL_IDENTIFY_FALSE)
     167          12 :         return nIdentifyResult;
     168             : 
     169             :     // Checking for MiraMon raster file
     170       57971 :     return MMRRel::IdentifyFile(poOpenInfo);
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*                                Open()                                */
     175             : /************************************************************************/
     176         243 : GDALDataset *MMRDataset::Open(GDALOpenInfo *poOpenInfo)
     177             : 
     178             : {
     179             :     // Verify that this is a MMR file.
     180         243 :     if (!Identify(poOpenInfo))
     181           0 :         return nullptr;
     182             : 
     183             :     // Confirm the requested access is supported.
     184         243 :     if (poOpenInfo->eAccess == GA_Update)
     185             :     {
     186           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     187             :                  "The MiraMonRaster driver does not support update "
     188             :                  "access to existing datasets.");
     189           0 :         return nullptr;
     190             :     }
     191             : 
     192             :     // Create the Dataset (with bands or Subdatasets).
     193         486 :     auto poDS = std::make_unique<MMRDataset>(poOpenInfo);
     194         243 :     if (!poDS->IsValid())
     195         175 :         return nullptr;
     196             : 
     197             :     // Set description
     198          68 :     poDS->SetDescription(poOpenInfo->pszFilename);
     199             : 
     200          68 :     return poDS.release();
     201             : }
     202             : 
     203          61 : bool MMRDataset::CreateRasterBands()
     204             : {
     205             :     MMRBand *pBand;
     206             : 
     207         122 :     for (int nIBand = 0; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     208             :     {
     209             :         // Establish raster band info.
     210          61 :         pBand = m_pMMRRel->GetBand(nIBand);
     211          61 :         if (!pBand)
     212           0 :             return false;
     213          61 :         nRasterXSize = pBand->GetWidth();
     214          61 :         nRasterYSize = pBand->GetHeight();
     215          61 :         pBand->UpdateGeoTransform();  // Fills adfGeoTransform for this band
     216             : 
     217          61 :         auto poRasterBand = std::make_unique<MMRRasterBand>(this, nBands + 1);
     218          61 :         if (!poRasterBand->IsValid())
     219             :         {
     220           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     221             :                      "Failed to create a RasterBand from '%s'",
     222             :                      m_pMMRRel->GetRELNameChar());
     223             : 
     224           0 :             return false;
     225             :         }
     226             : 
     227          61 :         SetBand(nBands + 1, std::move(poRasterBand));
     228             : 
     229             :         MMRRasterBand *poBand =
     230          61 :             cpl::down_cast<MMRRasterBand *>(GetRasterBand(nIBand + 1));
     231             : 
     232          61 :         pBand = m_pMMRRel->GetBand(nIBand);
     233          61 :         if (!pBand)
     234           0 :             return false;
     235          61 :         if (!pBand->GetFriendlyDescription().empty())
     236             :         {
     237          51 :             poBand->SetMetadataItem("DESCRIPTION",
     238          51 :                                     pBand->GetFriendlyDescription());
     239             :         }
     240             :     }
     241             :     // Some metadata items must be preserved just in case to be restored
     242             :     // if they are preserved through translations.
     243          61 :     m_pMMRRel->RELToGDALMetadata(this);
     244             : 
     245          61 :     return true;
     246             : }
     247             : 
     248          68 : void MMRDataset::ReadProjection()
     249             : 
     250             : {
     251          68 :     if (!m_pMMRRel)
     252           0 :         return;
     253             : 
     254         136 :     CPLString osSRS;
     255             : 
     256         136 :     if (!m_pMMRRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
     257         204 :                                      "HorizontalSystemIdentifier", osSRS) ||
     258          68 :         osSRS.empty())
     259           0 :         return;
     260             : 
     261             :     char szResult[MM_MAX_ID_SNY + 10];
     262          68 :     int nResult = ReturnEPSGCodeSRSFromMMIDSRS(osSRS.c_str(), szResult);
     263          68 :     if (nResult == 1 || szResult[0] == '\0')
     264          10 :         return;
     265             : 
     266             :     int nEPSG;
     267          58 :     if (1 == sscanf(szResult, "%d", &nEPSG))
     268          58 :         m_oSRS.importFromEPSG(nEPSG);
     269             : 
     270          58 :     return;
     271             : }
     272             : 
     273             : /************************************************************************/
     274             : /*                             SUBDATASETS                              */
     275             : /************************************************************************/
     276             : // Assigns every band to a subdataset
     277          68 : void MMRDataset::AssignBandsToSubdataSets()
     278             : {
     279          68 :     m_nNSubdataSets = 0;
     280          68 :     if (!m_pMMRRel.get())
     281           0 :         return;
     282             : 
     283          68 :     int nIBand = 0;
     284          68 :     int nIBand2 = 0;
     285             : 
     286             :     MMRBand *pBand;
     287             :     MMRBand *pOtherBand;
     288         170 :     for (; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     289             :     {
     290         102 :         pBand = m_pMMRRel->GetBand(nIBand);
     291         102 :         if (!pBand)
     292           0 :             continue;
     293             : 
     294         102 :         if (pBand->GetAssignedSubDataSet() != 0)
     295           1 :             continue;
     296             : 
     297         101 :         m_nNSubdataSets++;
     298         101 :         pBand->AssignSubDataSet(m_nNSubdataSets);
     299             : 
     300             :         // Let's put all suitable bands in the same subdataset
     301         207 :         for (nIBand2 = nIBand + 1; nIBand2 < m_pMMRRel->GetNBands(); nIBand2++)
     302             :         {
     303         106 :             pOtherBand = m_pMMRRel->GetBand(nIBand2);
     304         106 :             if (!pOtherBand)
     305           0 :                 continue;
     306             : 
     307         106 :             if (pOtherBand->GetAssignedSubDataSet() != 0)
     308           0 :                 continue;
     309             : 
     310         106 :             if (BandInTheSameDataset(nIBand, nIBand2))
     311           1 :                 pOtherBand->AssignSubDataSet(m_nNSubdataSets);
     312             :         }
     313             :     }
     314             : 
     315             :     // If there is only one subdataset, it means that
     316             :     // we don't need subdatasets (all assigned to 0)
     317          68 :     if (m_nNSubdataSets == 1)
     318             :     {
     319          61 :         m_nNSubdataSets = 0;
     320         122 :         for (nIBand = 0; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     321             :         {
     322          61 :             pBand = m_pMMRRel->GetBand(nIBand);
     323          61 :             if (!pBand)
     324           0 :                 break;
     325          61 :             pBand->AssignSubDataSet(m_nNSubdataSets);
     326             :         }
     327             :     }
     328             : }
     329             : 
     330           7 : void MMRDataset::CreateSubdatasetsFromBands()
     331             : {
     332           7 :     CPLStringList oSubdatasetList;
     333           7 :     CPLString osDSName;
     334           7 :     CPLString osDSDesc;
     335             :     MMRBand *pBand;
     336             : 
     337          47 :     for (int iSubdataset = 1; iSubdataset <= m_nNSubdataSets; iSubdataset++)
     338             :     {
     339             :         int nIBand;
     340         154 :         for (nIBand = 0; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     341             :         {
     342         154 :             pBand = m_pMMRRel->GetBand(nIBand);
     343         154 :             if (!pBand)
     344           0 :                 return;
     345         154 :             if (pBand->GetAssignedSubDataSet() == iSubdataset)
     346          40 :                 break;
     347             :         }
     348             : 
     349          40 :         if (nIBand == m_pMMRRel->GetNBands())
     350           0 :             break;
     351             : 
     352          40 :         pBand = m_pMMRRel->GetBand(nIBand);
     353          40 :         if (!pBand)
     354           0 :             return;
     355             : 
     356             :         osDSName.Printf("MiraMonRaster:\"%s\",\"%s\"",
     357          80 :                         pBand->GetRELFileName().c_str(),
     358          80 :                         pBand->GetRawBandFileName().c_str());
     359             :         osDSDesc.Printf("Subdataset %d: \"%s\"", iSubdataset,
     360          40 :                         pBand->GetBandName().c_str());
     361          40 :         nIBand++;
     362             : 
     363         146 :         for (; nIBand < m_pMMRRel->GetNBands(); nIBand++)
     364             :         {
     365         106 :             pBand = m_pMMRRel->GetBand(nIBand);
     366         106 :             if (!pBand)
     367           0 :                 return;
     368         106 :             if (pBand->GetAssignedSubDataSet() != iSubdataset)
     369         105 :                 continue;
     370             : 
     371             :             osDSName.append(
     372           1 :                 CPLSPrintf(",\"%s\"", pBand->GetRawBandFileName().c_str()));
     373             :             osDSDesc.append(
     374           1 :                 CPLSPrintf(",\"%s\"", pBand->GetBandName().c_str()));
     375             :         }
     376             : 
     377             :         oSubdatasetList.AddNameValue(
     378          40 :             CPLSPrintf("SUBDATASET_%d_NAME", iSubdataset), osDSName);
     379             :         oSubdatasetList.AddNameValue(
     380          40 :             CPLSPrintf("SUBDATASET_%d_DESC", iSubdataset), osDSDesc);
     381             :     }
     382             : 
     383           7 :     if (oSubdatasetList.Count() > 0)
     384             :     {
     385             :         // Add metadata to the main dataset
     386           7 :         SetMetadata(oSubdatasetList.List(), "SUBDATASETS");
     387           7 :         oSubdatasetList.Clear();
     388             :     }
     389             : }
     390             : 
     391         106 : bool MMRDataset::BandInTheSameDataset(int nIBand1, int nIBand2) const
     392             : {
     393         106 :     if (nIBand1 < 0 || nIBand2 < 0)
     394           0 :         return true;
     395             : 
     396         106 :     if (nIBand1 >= m_pMMRRel->GetNBands() || nIBand2 >= m_pMMRRel->GetNBands())
     397           0 :         return true;
     398             : 
     399         106 :     MMRBand *pThisBand = m_pMMRRel->GetBand(nIBand1);
     400         106 :     MMRBand *pOtherBand = m_pMMRRel->GetBand(nIBand2);
     401         106 :     if (!pThisBand || !pOtherBand)
     402           0 :         return true;
     403             : 
     404             :     // Two images with different numbers of columns are assigned to different subdatasets
     405         106 :     if (pThisBand->GetWidth() != pOtherBand->GetWidth())
     406           9 :         return false;
     407             : 
     408             :     // Two images with different numbers of rows are assigned to different subdatasets
     409          97 :     if (pThisBand->GetHeight() != pOtherBand->GetHeight())
     410           0 :         return false;
     411             : 
     412             :     // Two images with different data type are assigned to different subdatasets
     413          97 :     if (pThisBand->GeteMMNCDataType() != pOtherBand->GeteMMNCDataType())
     414          32 :         return false;
     415             : 
     416             :     // Two images with different bounding box are assigned to different subdatasets
     417          65 :     if (pThisBand->GetBoundingBoxMinX() != pOtherBand->GetBoundingBoxMinX())
     418           7 :         return false;
     419          58 :     if (pThisBand->GetBoundingBoxMaxX() != pOtherBand->GetBoundingBoxMaxX())
     420           6 :         return false;
     421          52 :     if (pThisBand->GetBoundingBoxMinY() != pOtherBand->GetBoundingBoxMinY())
     422           0 :         return false;
     423          52 :     if (pThisBand->GetBoundingBoxMaxY() != pOtherBand->GetBoundingBoxMaxY())
     424           0 :         return false;
     425             : 
     426             :     // Two images with different simbolization are assigned to different subdatasets
     427          52 :     if (!EQUAL(pThisBand->GetColor_Const(), pOtherBand->GetColor_Const()))
     428           0 :         return false;
     429          52 :     if (pThisBand->GetConstantColorRGB().c1 !=
     430          52 :         pOtherBand->GetConstantColorRGB().c1)
     431           0 :         return false;
     432          52 :     if (pThisBand->GetConstantColorRGB().c2 !=
     433          52 :         pOtherBand->GetConstantColorRGB().c2)
     434           0 :         return false;
     435          52 :     if (pThisBand->GetConstantColorRGB().c3 !=
     436          52 :         pOtherBand->GetConstantColorRGB().c3)
     437           0 :         return false;
     438          52 :     if (!EQUAL(pThisBand->GetColor_Paleta(), pOtherBand->GetColor_Paleta()))
     439          26 :         return false;
     440          26 :     if (!EQUAL(pThisBand->GetColor_TractamentVariable(),
     441             :                pOtherBand->GetColor_TractamentVariable()))
     442           0 :         return false;
     443          26 :     if (!EQUAL(pThisBand->GetTractamentVariable(),
     444             :                pOtherBand->GetTractamentVariable()))
     445           3 :         return false;
     446          23 :     if (!EQUAL(pThisBand->GetColor_EscalatColor(),
     447             :                pOtherBand->GetColor_EscalatColor()))
     448           0 :         return false;
     449          23 :     if (!EQUAL(pThisBand->GetColor_N_SimbolsALaTaula(),
     450             :                pOtherBand->GetColor_N_SimbolsALaTaula()))
     451           0 :         return false;
     452          23 :     if (pThisBand->IsCategorical() != pOtherBand->IsCategorical())
     453           0 :         return false;
     454          23 :     if (pThisBand->IsCategorical())
     455             :     {
     456          19 :         if (pThisBand->GetMaxSet() != pOtherBand->GetMaxSet())
     457           0 :             return false;
     458          19 :         if (pThisBand->GetMaxSet())
     459             :         {
     460          19 :             if (pThisBand->GetMax() != pOtherBand->GetMax())
     461          12 :                 return false;
     462             :         }
     463             :     }
     464             : 
     465             :     // Two images with different RATs are assigned to different subdatasets
     466          26 :     if (!EQUAL(pThisBand->GetShortRATName(), pOtherBand->GetShortRATName()) ||
     467          15 :         !EQUAL(pThisBand->GetAssociateREL(), pOtherBand->GetAssociateREL()))
     468           7 :         return false;
     469             : 
     470             :     // One image has NoData values and the other does not;
     471             :     // they are assigned to different subdatasets
     472           4 :     if (pThisBand->BandHasNoData() != pOtherBand->BandHasNoData())
     473           2 :         return false;
     474             : 
     475             :     // Two images with different NoData values are assigned to different subdatasets
     476           2 :     if (pThisBand->GetNoDataValue() != pOtherBand->GetNoDataValue())
     477           1 :         return false;
     478             : 
     479           1 :     return true;
     480             : }
     481             : 
     482             : /************************************************************************/
     483             : /*                         UpdateGeoTransform()                         */
     484             : /************************************************************************/
     485           7 : int MMRDataset::UpdateGeoTransform()
     486             : {
     487             :     // Bounding box of the band
     488             :     // Section [EXTENT] in rel file
     489             : 
     490           7 :     if (!m_pMMRRel)
     491           0 :         return 1;
     492             : 
     493          14 :     CPLString osMinX;
     494          14 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MinX", osMinX) ||
     495           7 :         osMinX.empty())
     496           0 :         return 1;
     497             : 
     498           7 :     if (1 != CPLsscanf(osMinX, "%lf", &(m_gt.xorig)))
     499           0 :         m_gt.xorig = 0.0;
     500             : 
     501           7 :     int nNCols = m_pMMRRel->GetColumnsNumberFromREL();
     502           7 :     if (nNCols <= 0)
     503           0 :         return 1;
     504             : 
     505          14 :     CPLString osMaxX;
     506          14 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MaxX", osMaxX) ||
     507           7 :         osMaxX.empty())
     508           0 :         return 1;
     509             : 
     510             :     double dfMaxX;
     511           7 :     if (1 != CPLsscanf(osMaxX, "%lf", &dfMaxX))
     512           0 :         dfMaxX = 1.0;
     513             : 
     514           7 :     m_gt.xscale = (dfMaxX - m_gt.xorig) / nNCols;
     515           7 :     m_gt.xrot = 0.0;  // No rotation in MiraMon rasters
     516             : 
     517          14 :     CPLString osMinY;
     518          14 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MinY", osMinY) ||
     519           7 :         osMinY.empty())
     520           0 :         return 1;
     521             : 
     522          14 :     CPLString osMaxY;
     523          14 :     if (!m_pMMRRel->GetMetadataValue(SECTION_EXTENT, "MaxY", osMaxY) ||
     524           7 :         osMaxY.empty())
     525           0 :         return 1;
     526             : 
     527           7 :     int nNRows = m_pMMRRel->GetRowsNumberFromREL();
     528           7 :     if (nNRows <= 0)
     529           0 :         return 1;
     530             : 
     531             :     double dfMaxY;
     532           7 :     if (1 != CPLsscanf(osMaxY, "%lf", &dfMaxY))
     533           0 :         dfMaxY = 1.0;
     534             : 
     535           7 :     m_gt.yorig = dfMaxY;
     536           7 :     m_gt.yrot = 0.0;
     537             : 
     538             :     double dfMinY;
     539           7 :     if (1 != CPLsscanf(osMinY, "%lf", &dfMinY))
     540           0 :         dfMinY = 0.0;
     541           7 :     m_gt.yscale = (dfMinY - m_gt.yorig) / nNRows;
     542             : 
     543           7 :     return 0;
     544             : }
     545             : 
     546          14 : const OGRSpatialReference *MMRDataset::GetSpatialRef() const
     547             : {
     548          14 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     549             : }
     550             : 
     551          35 : CPLErr MMRDataset::GetGeoTransform(GDALGeoTransform &gt) const
     552             : {
     553          35 :     if (m_gt.xorig != 0.0 || m_gt.xscale != 1.0 || m_gt.xrot != 0.0 ||
     554           3 :         m_gt.yorig != 0.0 || m_gt.yrot != 0.0 || m_gt.yscale != 1.0)
     555             :     {
     556          35 :         gt = m_gt;
     557          35 :         return CE_None;
     558             :     }
     559             : 
     560           0 :     return GDALDataset::GetGeoTransform(gt);
     561             : }

Generated by: LCOV version 1.14