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

Generated by: LCOV version 1.14