LCOV - code coverage report
Current view: top level - frmts/prf - phprfdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 279 311 89.7 %
Date: 2025-06-19 12:30:01 Functions: 17 17 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Purpose:  Racurs PHOTOMOD tiled format reader (http://www.racurs.ru)
       3             :  * Author:   Andrew Sudorgin (drons [a] list dot ru)
       4             :  ******************************************************************************
       5             :  * Copyright (c) 2016, Andrew Sudorgin
       6             :  *
       7             :  * SPDX-License-Identifier: MIT
       8             :  ****************************************************************************/
       9             : 
      10             : #include "cpl_minixml.h"
      11             : #include "gdal.h"
      12             : #include "gdal_priv.h"
      13             : #include "gdal_proxy.h"
      14             : #include "../vrt/vrtdataset.h"
      15             : 
      16             : enum ph_format
      17             : {
      18             :     ph_megatiff,
      19             :     ph_xdem
      20             : };
      21             : 
      22             : #define PH_PRF_DRIVER "PRF"
      23             : #define PH_PRF_EXT "prf"
      24             : #define PH_DEM_EXT "x-dem"
      25             : #define PH_GEOREF_SHIFT_Y (1.0)
      26             : 
      27             : class PhPrfBand final : public VRTSourcedRasterBand
      28             : {
      29             :     std::vector<GDALRasterBand *> osOverview{};
      30             : 
      31             :   public:
      32           7 :     PhPrfBand(GDALDataset *poDataset, int nBandCount, GDALDataType eType,
      33             :               int nXSize, int nYSize)
      34           7 :         : VRTSourcedRasterBand(poDataset, nBandCount, eType, nXSize, nYSize)
      35             :     {
      36           7 :     }
      37             : 
      38           3 :     void AddOverview(GDALRasterBand *ov)
      39             :     {
      40           3 :         osOverview.push_back(ov);
      41           3 :     }
      42             : 
      43             :     int GetOverviewCount() override;
      44             : 
      45           1 :     GDALRasterBand *GetOverview(int i) override
      46             :     {
      47           1 :         size_t n = static_cast<size_t>(i);
      48           1 :         if (n < osOverview.size())
      49             :         {
      50           1 :             return osOverview[n];
      51             :         }
      52             :         else
      53             :         {
      54           0 :             return VRTSourcedRasterBand::GetOverview(i);
      55             :         }
      56             :     }
      57             : };
      58             : 
      59           2 : int PhPrfBand::GetOverviewCount()
      60             : {
      61           2 :     if (!osOverview.empty())
      62             :     {
      63           1 :         return static_cast<int>(osOverview.size());
      64             :     }
      65             :     else
      66             :     {
      67           1 :         return VRTSourcedRasterBand::GetOverviewCount();
      68             :     }
      69             : }
      70             : 
      71             : class PhPrfDataset final : public VRTDataset
      72             : {
      73             :     std::vector<GDALDataset *> osSubTiles{};
      74             : 
      75             :   public:
      76             :     PhPrfDataset(GDALAccess eAccess, int nSizeX, int nSizeY, int nBandCount,
      77             :                  GDALDataType eType, const char *pszName);
      78             :     ~PhPrfDataset() override;
      79             :     bool AddTile(const char *pszPartName, GDALAccess eAccess, int nWidth,
      80             :                  int nHeight, int nOffsetX, int nOffsetY, int nScale);
      81             :     int CloseDependentDatasets() override;
      82             :     static int Identify(GDALOpenInfo *poOpenInfo);
      83             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      84             : };
      85             : 
      86           7 : PhPrfDataset::PhPrfDataset(GDALAccess _eAccess, int nSizeX, int nSizeY,
      87             :                            int nBandCount, GDALDataType eType,
      88           7 :                            const char *pszName)
      89           7 :     : VRTDataset(nSizeX, nSizeY)
      90             : {
      91           7 :     poDriver = GetGDALDriverManager()->GetDriverByName(PH_PRF_DRIVER);
      92           7 :     eAccess = _eAccess;
      93           7 :     SetWritable(FALSE);  // Avoid rewrite of *.prf file with 'vrt' file
      94           7 :     SetDescription(pszName);
      95             : 
      96          14 :     for (int i = 0; i != nBandCount; ++i)
      97             :     {
      98           7 :         PhPrfBand *poBand = new PhPrfBand(this, i + 1, eType, nSizeX, nSizeY);
      99           7 :         SetBand(i + 1, poBand);
     100             :     }
     101           7 : }
     102             : 
     103          14 : PhPrfDataset::~PhPrfDataset()
     104             : {
     105           7 :     PhPrfDataset::CloseDependentDatasets();
     106          14 : }
     107             : 
     108          39 : bool PhPrfDataset::AddTile(const char *pszPartName, GDALAccess eAccessType,
     109             :                            int nWidth, int nHeight, int nOffsetX, int nOffsetY,
     110             :                            int nScale)
     111             : {
     112             :     GDALProxyPoolDataset *poTileDataset;
     113          39 :     poTileDataset = new GDALProxyPoolDataset(pszPartName, nWidth, nHeight,
     114          39 :                                              eAccessType, FALSE);
     115             : 
     116          78 :     for (int nBand = 1; nBand != GetRasterCount() + 1; ++nBand)
     117             :     {
     118          39 :         PhPrfBand *poBand = dynamic_cast<PhPrfBand *>(GetRasterBand(nBand));
     119             : 
     120          39 :         if (poBand == nullptr)
     121             :         {
     122           0 :             delete poTileDataset;
     123           0 :             return false;
     124             :         }
     125             : 
     126             :         // Block sizes (nBlockXSize&nBlockYSize) passed as zeros.
     127             :         // They will be loaded when RefUnderlyingRasterBand
     128             :         // function is called on first open of tile's dataset 'poTileDataset'.
     129          39 :         poTileDataset->AddSrcBandDescription(poBand->GetRasterDataType(), 0, 0);
     130          39 :         GDALRasterBand *poTileBand = poTileDataset->GetRasterBand(nBand);
     131             : 
     132          39 :         if (0 == nScale)
     133             :         {
     134          36 :             poBand->AddSimpleSource(poTileBand, 0, 0, nWidth, nHeight, nOffsetX,
     135             :                                     nOffsetY, nWidth, nHeight);
     136             :         }
     137             :         else
     138             :         {
     139           3 :             poBand->AddOverview(poTileBand);
     140             :         }
     141             :     }
     142             : 
     143          39 :     osSubTiles.push_back(poTileDataset);
     144             : 
     145          39 :     return true;
     146             : }
     147             : 
     148           7 : int PhPrfDataset::CloseDependentDatasets()
     149             : {
     150           7 :     int bDroppedRef = VRTDataset::CloseDependentDatasets();
     151          46 :     for (std::vector<GDALDataset *>::iterator ii(osSubTiles.begin());
     152          46 :          ii != osSubTiles.end(); ++ii)
     153             :     {
     154          39 :         delete (*ii);
     155          39 :         bDroppedRef = TRUE;
     156             :     }
     157           7 :     osSubTiles.clear();
     158           7 :     return bDroppedRef;
     159             : }
     160             : 
     161       56273 : int PhPrfDataset::Identify(GDALOpenInfo *poOpenInfo)
     162             : {
     163       56273 :     if (poOpenInfo->pabyHeader == nullptr || poOpenInfo->nHeaderBytes < 20)
     164             :     {
     165       53257 :         return FALSE;
     166             :     }
     167             : 
     168        3016 :     if (strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader), "phini") ==
     169             :         nullptr)
     170             :     {
     171        3009 :         return FALSE;
     172             :     }
     173             : 
     174           7 :     if (poOpenInfo->IsExtensionEqualToCI(PH_PRF_EXT))
     175             :     {
     176           4 :         return TRUE;
     177             :     }
     178           3 :     else if (poOpenInfo->IsExtensionEqualToCI(PH_DEM_EXT))
     179             :     {
     180           3 :         return TRUE;
     181             :     }
     182             : 
     183           0 :     return FALSE;
     184             : }
     185             : 
     186         745 : static void GetXmlNameValuePair(const CPLXMLNode *psElt, CPLString &osName,
     187             :                                 CPLString &osValue)
     188             : {
     189        2079 :     for (const CPLXMLNode *psAttr = psElt->psChild; psAttr != nullptr;
     190        1334 :          psAttr = psAttr->psNext)
     191             :     {
     192        1334 :         if (psAttr->eType != CXT_Attribute || psAttr->pszValue == nullptr ||
     193         819 :             psAttr->psChild == nullptr || psAttr->psChild->pszValue == nullptr)
     194             :         {
     195         515 :             continue;
     196             :         }
     197         819 :         if (EQUAL(psAttr->pszValue, "n"))
     198             :         {
     199         438 :             osName = psAttr->psChild->pszValue;
     200             :         }
     201         381 :         else if (EQUAL(psAttr->pszValue, "v"))
     202             :         {
     203         381 :             osValue = psAttr->psChild->pszValue;
     204             :         }
     205             :     }
     206         745 : }
     207             : 
     208          39 : static CPLString GetXmlAttribute(const CPLXMLNode *psElt,
     209             :                                  const CPLString &osAttrName,
     210             :                                  const CPLString &osDef = CPLString())
     211             : {
     212          39 :     for (const CPLXMLNode *psAttr = psElt->psChild; psAttr != nullptr;
     213           0 :          psAttr = psAttr->psNext)
     214             :     {
     215          39 :         if (psAttr->eType != CXT_Attribute || psAttr->pszValue == nullptr ||
     216          39 :             psAttr->psChild == nullptr || psAttr->psChild->pszValue == nullptr)
     217             :         {
     218           0 :             continue;
     219             :         }
     220          39 :         if (EQUAL(psAttr->pszValue, osAttrName))
     221             :         {
     222          39 :             return psAttr->psChild->pszValue;
     223             :         }
     224             :     }
     225           0 :     return osDef;
     226             : }
     227             : 
     228           4 : static bool ParseGeoref(const CPLXMLNode *psGeorefElt, double *padfGeoTrans)
     229             : {
     230           4 :     bool abOk[6] = {false, false, false, false, false, false};
     231             :     static const char *const apszGeoKeys[6] = {"A_0", "A_1", "A_2",
     232             :                                                "B_0", "B_1", "B_2"};
     233          32 :     for (const CPLXMLNode *elt = psGeorefElt->psChild; elt != nullptr;
     234          28 :          elt = elt->psNext)
     235             :     {
     236          56 :         CPLString osName;
     237          56 :         CPLString osValue;
     238          28 :         GetXmlNameValuePair(elt, osName, osValue);
     239         196 :         for (int k = 0; k != 6; ++k)
     240             :         {
     241         168 :             if (EQUAL(osName, apszGeoKeys[k]))
     242             :             {
     243          24 :                 padfGeoTrans[k] = CPLAtof(osValue);
     244          24 :                 abOk[k] = true;
     245             :             }
     246             :         }
     247             :     }
     248             : 
     249          24 :     for (int k = 0; k != 6; ++k)
     250             :     {
     251          24 :         if (!abOk[k])
     252             :         {
     253           0 :             break;
     254             :         }
     255          24 :         if (k == 5)
     256             :         {
     257           4 :             padfGeoTrans[3] -= PH_GEOREF_SHIFT_Y * padfGeoTrans[4];
     258           4 :             padfGeoTrans[3] -= PH_GEOREF_SHIFT_Y * padfGeoTrans[5];
     259           4 :             return true;
     260             :         }
     261             :     }
     262           0 :     return false;
     263             : }
     264             : 
     265           3 : static bool ParseDemShift(const CPLXMLNode *psDemShiftElt, double *padfDemShift)
     266             : {
     267           3 :     bool abOk[6] = {false, false, false, false, false, false};
     268             :     static const char *const apszDemShiftKeys[6] = {"x", "y", "z", "", "", ""};
     269             : 
     270          15 :     for (const CPLXMLNode *elt = psDemShiftElt->psChild; elt != nullptr;
     271          12 :          elt = elt->psNext)
     272             :     {
     273          24 :         CPLString osName;
     274          24 :         CPLString osValue;
     275          12 :         GetXmlNameValuePair(elt, osName, osValue);
     276          48 :         for (int k = 0; k != 3; ++k)
     277             :         {
     278          36 :             if (EQUAL(osName, apszDemShiftKeys[k]))
     279             :             {
     280           9 :                 padfDemShift[k] = CPLAtof(osValue);
     281           9 :                 abOk[k] = true;
     282             :             }
     283             :         }
     284             :     }
     285           3 :     return abOk[0] && abOk[1] && abOk[2];
     286             : }
     287             : 
     288           7 : static GDALDataType ParseChannelsInfo(const CPLXMLNode *psElt)
     289             : {
     290          14 :     CPLString osType;
     291          14 :     CPLString osBytesPS;
     292          14 :     CPLString osChannels;
     293             : 
     294          35 :     for (const CPLXMLNode *psChild = psElt->psChild; psChild != nullptr;
     295          28 :          psChild = psChild->psNext)
     296             :     {
     297          28 :         if (psChild->eType != CXT_Element)
     298             :         {
     299           7 :             continue;
     300             :         }
     301             : 
     302          42 :         CPLString osName;
     303          42 :         CPLString osValue;
     304             : 
     305          21 :         GetXmlNameValuePair(psChild, osName, osValue);
     306             : 
     307          21 :         if (EQUAL(osName, "type"))
     308             :         {
     309           7 :             osType = std::move(osValue);
     310             :         }
     311          14 :         else if (EQUAL(osName, "bytes_ps"))
     312             :         {
     313           7 :             osBytesPS = std::move(osValue);
     314             :         }
     315           7 :         else if (EQUAL(osName, "channels"))
     316             :         {
     317           7 :             osChannels = std::move(osValue);
     318             :         }
     319             :     }
     320             : 
     321           7 :     const int nDataTypeSize = atoi(osBytesPS);
     322           7 :     if (osType == "U")
     323             :     {
     324           4 :         switch (nDataTypeSize)
     325             :         {
     326           0 :             case 1:
     327           0 :                 return GDT_Byte;
     328           4 :             case 2:
     329           4 :                 return GDT_UInt16;
     330           0 :             case 4:
     331           0 :                 return GDT_UInt32;
     332           0 :             default:
     333           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     334             :                          "Unsupported datatype size %d", nDataTypeSize);
     335           0 :                 return GDT_Unknown;
     336             :         }
     337             :     }
     338           3 :     else if (osType == "F")
     339             :     {
     340           3 :         switch (nDataTypeSize)
     341             :         {
     342           3 :             case 4:
     343           3 :                 return GDT_Float32;
     344           0 :             case 8:
     345           0 :                 return GDT_Float64;
     346           0 :             default:
     347           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     348             :                          "Unsupported datatype size %d", nDataTypeSize);
     349           0 :                 return GDT_Unknown;
     350             :         }
     351             :     }
     352             : 
     353           0 :     return GDT_Unknown;
     354             : }
     355             : 
     356           7 : GDALDataset *PhPrfDataset::Open(GDALOpenInfo *poOpenInfo)
     357             : {
     358             :     ph_format eFormat;
     359             : 
     360           7 :     if (poOpenInfo->IsExtensionEqualToCI(PH_PRF_EXT))
     361             :     {
     362           4 :         eFormat = ph_megatiff;
     363             :     }
     364           3 :     else if (poOpenInfo->IsExtensionEqualToCI(PH_DEM_EXT))
     365             :     {
     366           3 :         eFormat = ph_xdem;
     367             :     }
     368             :     else
     369             :     {
     370           0 :         return nullptr;
     371             :     }
     372             : 
     373          14 :     CPLXMLTreeCloser oDoc(CPLParseXMLFile(poOpenInfo->pszFilename));
     374             : 
     375           7 :     if (oDoc.get() == nullptr)
     376             :     {
     377           0 :         return nullptr;
     378             :     }
     379             : 
     380           7 :     const CPLXMLNode *psPhIni(CPLSearchXMLNode(oDoc.get(), "=phini"));
     381           7 :     if (psPhIni == nullptr)
     382             :     {
     383           0 :         return nullptr;
     384             :     }
     385             : 
     386           7 :     int nSizeX = 0;
     387           7 :     int nSizeY = 0;
     388           7 :     int nBandCount = 0;
     389           7 :     GDALDataType eResultDatatype = GDT_Unknown;
     390          14 :     CPLString osPartsBasePath(CPLGetPathSafe(poOpenInfo->pszFilename));
     391          14 :     CPLString osPartsPath(osPartsBasePath + "/" +
     392          21 :                           CPLGetBasenameSafe(poOpenInfo->pszFilename));
     393          14 :     CPLString osPartsExt;
     394           7 :     double adfGeoTrans[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
     395           7 :     bool bGeoTransOk = false;
     396             : 
     397           7 :     double adfDemShift[3] = {0.0, 0.0, 0.0};
     398           7 :     bool bDemShiftOk = false;
     399           7 :     const int nDemMDCount = 7;
     400           7 :     bool abDemMetadataOk[nDemMDCount] = {false, false, false, false,
     401             :                                          false, false, false};
     402           7 :     double adfDemMetadata[nDemMDCount] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
     403             :     static const char *const apszDemKeys[nDemMDCount] = {
     404             :         "XR_0", "XR_1", "YR_0", "YR_1", "ZR_0", "ZR_1", "BadZ"};
     405           7 :     if (eFormat == ph_megatiff)
     406             :     {
     407           4 :         osPartsExt = ".tif";
     408             :     }
     409           3 :     else if (eFormat == ph_xdem)
     410             :     {
     411           3 :         osPartsExt = ".demtif";
     412             :     }
     413             : 
     414         187 :     for (const CPLXMLNode *psElt = psPhIni->psChild; psElt != nullptr;
     415         180 :          psElt = psElt->psNext)
     416             :     {
     417         180 :         if (!EQUAL(psElt->pszValue, "s") || psElt->eType != CXT_Element)
     418             :         {
     419         176 :             continue;
     420             :         }
     421             : 
     422           8 :         CPLString osName;
     423           8 :         CPLString osValue;
     424             : 
     425           4 :         GetXmlNameValuePair(psElt, osName, osValue);
     426             : 
     427           4 :         if (EQUAL(osName, "parts_ext"))
     428             :         {
     429           4 :             osPartsExt = "." + osValue;
     430             :         }
     431             :     }
     432             : 
     433         187 :     for (const CPLXMLNode *psElt = psPhIni->psChild; psElt != nullptr;
     434         180 :          psElt = psElt->psNext)
     435             :     {
     436         360 :         CPLString osName;
     437         360 :         CPLString osValue;
     438             : 
     439         180 :         GetXmlNameValuePair(psElt, osName, osValue);
     440             : 
     441         180 :         if (EQUAL(osName, "ChannelsInfo"))
     442             :         {
     443           7 :             eResultDatatype = ParseChannelsInfo(psElt);
     444             :         }
     445         173 :         else if (EQUAL(osName, "Width"))
     446             :         {
     447           7 :             nSizeX = atoi(osValue);
     448             :         }
     449         166 :         else if (EQUAL(osName, "Height"))
     450             :         {
     451           7 :             nSizeY = atoi(osValue);
     452             :         }
     453         159 :         else if (EQUAL(osName, "QChans"))
     454             :         {
     455           7 :             nBandCount = atoi(osValue);
     456             :         }
     457         152 :         else if (EQUAL(osName, "GeoRef"))
     458             :         {
     459           4 :             bGeoTransOk = ParseGeoref(psElt, adfGeoTrans);
     460             :         }
     461         148 :         else if (EQUAL(osName, "DemShift"))
     462             :         {
     463           3 :             bDemShiftOk = ParseDemShift(psElt, adfDemShift);
     464             :         }
     465             :         else
     466             :         {
     467        1160 :             for (int n = 0; n != nDemMDCount; ++n)
     468             :             {
     469        1015 :                 if (EQUAL(osName, apszDemKeys[n]))
     470             :                 {
     471          21 :                     adfDemMetadata[n] = CPLAtof(osValue);
     472          21 :                     abDemMetadataOk[n] = true;
     473             :                 }
     474             :             }
     475             :         }
     476             :     }
     477             : 
     478           7 :     if (eResultDatatype == GDT_Unknown)
     479             :     {
     480           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     481             :                  "GDAL Dataset datatype not found");
     482           0 :         return nullptr;
     483             :     }
     484             : 
     485           7 :     if (nSizeX <= 0 || nSizeY <= 0 || nBandCount <= 0)
     486             :     {
     487           0 :         return nullptr;
     488             :     }
     489             : 
     490             :     PhPrfDataset *poDataset =
     491             :         new PhPrfDataset(GA_ReadOnly, nSizeX, nSizeY, nBandCount,
     492           7 :                          eResultDatatype, poOpenInfo->pszFilename);
     493             : 
     494           7 :     if (!GDALCheckDatasetDimensions(poDataset->GetRasterXSize(),
     495             :                                     poDataset->GetRasterYSize()))
     496             :     {
     497           0 :         delete poDataset;
     498           0 :         return nullptr;
     499             :     }
     500             : 
     501         187 :     for (const CPLXMLNode *psElt = psPhIni->psChild; psElt != nullptr;
     502         180 :          psElt = psElt->psNext)
     503             :     {
     504         180 :         int nWidth = 0;
     505         180 :         int nHeight = 0;
     506         180 :         int nOffsetX = 0;
     507         180 :         int nOffsetY = 0;
     508         180 :         int nScale = 0;
     509             : 
     510         680 :         for (const CPLXMLNode *psItem = psElt->psChild; psItem != nullptr;
     511         500 :              psItem = psItem->psNext)
     512             :         {
     513        1000 :             CPLString osName;
     514        1000 :             CPLString osValue;
     515             : 
     516         500 :             GetXmlNameValuePair(psItem, osName, osValue);
     517             : 
     518         500 :             if (EQUAL(osName, "Width"))
     519             :             {
     520          39 :                 nWidth = atoi(osValue);
     521             :             }
     522         461 :             else if (EQUAL(osName, "Height"))
     523             :             {
     524          39 :                 nHeight = atoi(osValue);
     525             :             }
     526         422 :             else if (EQUAL(osName, "DispX"))
     527             :             {
     528          36 :                 nOffsetX = atoi(osValue);
     529             :             }
     530         386 :             else if (EQUAL(osName, "DispY"))
     531             :             {
     532          36 :                 nOffsetY = atoi(osValue);
     533             :             }
     534         350 :             else if (EQUAL(osName, "Scale"))
     535             :             {
     536           3 :                 nScale = atoi(osValue);
     537             :             }
     538             :         }
     539             : 
     540         180 :         if (nWidth == 0 || nHeight == 0)
     541             :         {
     542         141 :             continue;
     543             :         }
     544             : 
     545          78 :         CPLString osPartName(osPartsPath + "/" + GetXmlAttribute(psElt, "n") +
     546          39 :                              osPartsExt);
     547             : 
     548          39 :         if (!poDataset->AddTile(osPartName, GA_ReadOnly, nWidth, nHeight,
     549             :                                 nOffsetX, nOffsetY, nScale))
     550             :         {
     551           0 :             delete poDataset;
     552           0 :             return nullptr;
     553             :         }
     554             :     }
     555             : 
     556           7 :     if (eFormat == ph_megatiff && bGeoTransOk)
     557             :     {
     558           4 :         poDataset->SetGeoTransform(adfGeoTrans);
     559             :     }
     560             : 
     561           7 :     if (eFormat == ph_xdem)
     562             :     {
     563           3 :         GDALRasterBand *poFirstBand = poDataset->GetRasterBand(1);
     564             : 
     565           3 :         if (poFirstBand != nullptr)
     566             :         {
     567           3 :             poFirstBand->SetUnitType("m");  // Always meters.
     568             :         }
     569             : 
     570           3 :         if (abDemMetadataOk[0] && abDemMetadataOk[1] && abDemMetadataOk[2] &&
     571           3 :             abDemMetadataOk[3] && nSizeX > 1 && nSizeY > 1)
     572             :         {
     573           3 :             adfGeoTrans[0] = adfDemMetadata[0];
     574           3 :             adfGeoTrans[1] =
     575           3 :                 (adfDemMetadata[1] - adfDemMetadata[0]) / (nSizeX - 1);
     576           3 :             adfGeoTrans[2] = 0;
     577           3 :             adfGeoTrans[3] = adfDemMetadata[3];
     578           3 :             adfGeoTrans[4] = 0;
     579           3 :             adfGeoTrans[5] =
     580           3 :                 (adfDemMetadata[2] - adfDemMetadata[3]) / (nSizeY - 1);
     581             : 
     582           3 :             adfGeoTrans[0] -= 0.5 * adfGeoTrans[1];
     583           3 :             adfGeoTrans[3] -= 0.5 * adfGeoTrans[5];
     584             : 
     585           3 :             if (bDemShiftOk)
     586             :             {
     587           3 :                 adfGeoTrans[0] += adfDemShift[0];
     588           3 :                 adfGeoTrans[3] += adfDemShift[1];
     589             :             }
     590             : 
     591           3 :             poDataset->SetGeoTransform(adfGeoTrans);
     592             :         }
     593             : 
     594           3 :         if (abDemMetadataOk[4] && abDemMetadataOk[5])
     595             :         {
     596           3 :             poFirstBand->SetMetadataItem("STATISTICS_MINIMUM",
     597           3 :                                          CPLSPrintf("%f", adfDemMetadata[4]));
     598           3 :             poFirstBand->SetMetadataItem("STATISTICS_MAXIMUM",
     599           3 :                                          CPLSPrintf("%f", adfDemMetadata[5]));
     600             :         }
     601             : 
     602           3 :         if (abDemMetadataOk[6])
     603             :         {
     604           3 :             poFirstBand->SetNoDataValue(adfDemMetadata[6]);
     605             :         }
     606             : 
     607           3 :         if (bDemShiftOk)
     608             :         {
     609           3 :             poFirstBand->SetOffset(adfDemShift[2]);
     610             :         }
     611             :     }
     612             : 
     613             :     const std::string osPrj =
     614           7 :         CPLResetExtensionSafe(poOpenInfo->pszFilename, "prj");
     615           7 :     VSILFILE *const fp = VSIFOpenL(osPrj.c_str(), "rt");
     616           7 :     if (fp != nullptr)
     617             :     {
     618           3 :         const size_t nBufMax = 100000;
     619           3 :         char *const pszWKT = static_cast<char *>(CPLMalloc(nBufMax));
     620           3 :         const size_t nBytes = VSIFReadL(pszWKT, 1, nBufMax - 1, fp);
     621           3 :         VSIFCloseL(fp);
     622           3 :         if (nBytes > 0 && nBytes < nBufMax - 1)
     623             :         {
     624           3 :             auto poSRS = new OGRSpatialReference();
     625           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     626           3 :             pszWKT[nBytes] = '\0';
     627           3 :             if (poSRS->importFromWkt(pszWKT) == OGRERR_NONE)
     628             :             {
     629           3 :                 poDataset->SetSpatialRef(poSRS);
     630             :             }
     631           3 :             delete poSRS;
     632             :         }
     633           3 :         CPLFree(pszWKT);
     634             :     }
     635             : 
     636           7 :     return poDataset;
     637             : }
     638             : 
     639        1911 : void GDALRegister_PRF()
     640             : {
     641        1911 :     if (GDALGetDriverByName(PH_PRF_DRIVER) != nullptr)
     642         282 :         return;
     643             : 
     644        1629 :     GDALDriver *poDriver = new GDALDriver;
     645             : 
     646        1629 :     poDriver->SetDescription(PH_PRF_DRIVER);
     647        1629 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Racurs PHOTOMOD PRF");
     648        1629 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     649        1629 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     650        1629 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "prf");
     651        1629 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/prf.html");
     652        1629 :     poDriver->pfnIdentify = PhPrfDataset::Identify;
     653        1629 :     poDriver->pfnOpen = PhPrfDataset::Open;
     654        1629 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     655             : }

Generated by: LCOV version 1.14