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

Generated by: LCOV version 1.14