LCOV - code coverage report
Current view: top level - frmts/pcidsk - pcidskdataset2.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 599 883 67.8 %
Date: 2025-01-18 12:42:00 Functions: 43 45 95.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PCIDSK Database File
       4             :  * Purpose:  Read/write PCIDSK Database File used by the PCI software, using
       5             :  *           the external PCIDSK library.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "gdal_frmts.h"
      16             : #include "pcidskdataset2.h"
      17             : #include "pcidskdrivercore.h"
      18             : 
      19             : #include <algorithm>
      20             : 
      21             : const PCIDSK::PCIDSKInterfaces *PCIDSK2GetInterfaces(void);
      22             : 
      23             : /************************************************************************/
      24             : /* ==================================================================== */
      25             : /*                            PCIDSK2Band                               */
      26             : /* ==================================================================== */
      27             : /************************************************************************/
      28             : 
      29             : /************************************************************************/
      30             : /*                            PCIDSK2Band()                             */
      31             : /*                                                                      */
      32             : /*      This constructor is used for main file channels.                */
      33             : /************************************************************************/
      34             : 
      35         231 : PCIDSK2Band::PCIDSK2Band(PCIDSKFile *poFileIn, PCIDSKChannel *poChannelIn)
      36             : 
      37             : {
      38         231 :     Initialize();
      39             : 
      40         231 :     poFile = poFileIn;
      41         231 :     poChannel = poChannelIn;
      42             : 
      43         231 :     nBlockXSize = static_cast<int>(poChannel->GetBlockWidth());
      44         231 :     nBlockYSize = static_cast<int>(poChannel->GetBlockHeight());
      45             : 
      46         231 :     eDataType = PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType());
      47             : 
      48         231 :     if (!STARTS_WITH_CI(poChannel->GetDescription().c_str(),
      49             :                         "Contents Not Specified"))
      50          10 :         GDALMajorObject::SetDescription(poChannel->GetDescription().c_str());
      51             : 
      52             :     /* -------------------------------------------------------------------- */
      53             :     /*      Do we have overviews?                                           */
      54             :     /* -------------------------------------------------------------------- */
      55         231 :     RefreshOverviewList();
      56         231 : }
      57             : 
      58             : /************************************************************************/
      59             : /*                            PCIDSK2Band()                             */
      60             : /*                                                                      */
      61             : /*      This constructor is used for overviews and bitmap segments      */
      62             : /*      as bands.                                                       */
      63             : /************************************************************************/
      64             : 
      65          11 : PCIDSK2Band::PCIDSK2Band(PCIDSKChannel *poChannelIn)
      66             : 
      67             : {
      68          11 :     Initialize();
      69             : 
      70          11 :     this->poChannel = poChannelIn;
      71             : 
      72          11 :     nBand = 1;
      73             : 
      74          11 :     nBlockXSize = static_cast<int>(poChannel->GetBlockWidth());
      75          11 :     nBlockYSize = static_cast<int>(poChannel->GetBlockHeight());
      76             : 
      77          11 :     nRasterXSize = static_cast<int>(poChannel->GetWidth());
      78          11 :     nRasterYSize = static_cast<int>(poChannel->GetHeight());
      79             : 
      80          11 :     eDataType = PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType());
      81             : 
      82          11 :     if (poChannel->GetType() == CHN_BIT)
      83             :     {
      84           0 :         PCIDSK2Band::SetMetadataItem("NBITS", "1", "IMAGE_STRUCTURE");
      85             : 
      86           0 :         if (!STARTS_WITH_CI(poChannel->GetDescription().c_str(),
      87             :                             "Contents Not Specified"))
      88           0 :             GDALMajorObject::SetDescription(
      89           0 :                 poChannel->GetDescription().c_str());
      90             :     }
      91          11 : }
      92             : 
      93             : /************************************************************************/
      94             : /*                             Initialize()                             */
      95             : /************************************************************************/
      96             : 
      97         242 : void PCIDSK2Band::Initialize()
      98             : 
      99             : {
     100         242 :     papszLastMDListValue = nullptr;
     101             : 
     102         242 :     poChannel = nullptr;
     103         242 :     poFile = nullptr;
     104         242 :     poDS = nullptr;
     105             : 
     106         242 :     bCheckedForColorTable = false;
     107         242 :     poColorTable = nullptr;
     108         242 :     nPCTSegNumber = -1;
     109             : 
     110         242 :     papszCategoryNames = nullptr;
     111         242 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                            ~PCIDSK2Band()                            */
     115             : /************************************************************************/
     116             : 
     117         726 : PCIDSK2Band::~PCIDSK2Band()
     118             : 
     119             : {
     120         253 :     while (!apoOverviews.empty())
     121             :     {
     122          11 :         delete apoOverviews.back();
     123          11 :         apoOverviews.pop_back();
     124             :     }
     125         242 :     CSLDestroy(papszLastMDListValue);
     126         242 :     CSLDestroy(papszCategoryNames);
     127             : 
     128         242 :     delete poColorTable;
     129         484 : }
     130             : 
     131             : /************************************************************************/
     132             : /*                           SetDescription()                           */
     133             : /************************************************************************/
     134             : 
     135           3 : void PCIDSK2Band::SetDescription(const char *pszDescription)
     136             : 
     137             : {
     138           3 :     if (GetAccess() == GA_ReadOnly)
     139             :     {
     140           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     141             :                  "Unable to set description on read-only file.");
     142           0 :         return;
     143             :     }
     144             : 
     145             :     try
     146             :     {
     147           3 :         poChannel->SetDescription(pszDescription);
     148             : 
     149           3 :         if (!STARTS_WITH_CI(poChannel->GetDescription().c_str(),
     150             :                             "Contents Not Specified"))
     151           3 :             GDALMajorObject::SetDescription(
     152           6 :                 poChannel->GetDescription().c_str());
     153             :     }
     154           0 :     catch (const PCIDSKException &ex)
     155             :     {
     156           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     157             :     }
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                          GetCategoryNames()                          */
     162             : /*                                                                      */
     163             : /*      Offer category names from Class_*_ metadata.                    */
     164             : /************************************************************************/
     165             : 
     166           4 : char **PCIDSK2Band::GetCategoryNames()
     167             : 
     168             : {
     169             :     // already scanned?
     170           4 :     if (papszCategoryNames != nullptr)
     171           0 :         return papszCategoryNames;
     172             : 
     173             :     try
     174             :     {
     175           8 :         std::vector<std::string> aosMDKeys = poChannel->GetMetadataKeys();
     176           4 :         int nClassCount = 0;
     177           4 :         constexpr int nMaxClasses = 10000;
     178           4 :         papszCategoryNames = reinterpret_cast<char **>(
     179           4 :             CPLCalloc(nMaxClasses + 1, sizeof(char *)));
     180             : 
     181           7 :         for (size_t i = 0; i < aosMDKeys.size(); i++)
     182             :         {
     183           3 :             CPLString osKey = aosMDKeys[i];
     184             : 
     185             :             // is this a "Class_n_name" keyword?
     186           3 :             if (!STARTS_WITH_CI(osKey, "Class_"))
     187           3 :                 continue;
     188             : 
     189           0 :             if (!EQUAL(osKey.c_str() + osKey.size() - 5, "_name"))
     190           0 :                 continue;
     191             : 
     192             :             // Ignore unreasonable class values.
     193           0 :             int iClass = atoi(osKey.c_str() + 6);
     194             : 
     195           0 :             if (iClass < 0 || iClass > 10000)
     196           0 :                 continue;
     197             : 
     198             :             // Fetch the name.
     199           0 :             CPLString osName = poChannel->GetMetadataValue(osKey);
     200             : 
     201             :             // do we need to put in place dummy class names for missing values?
     202           0 :             if (iClass >= nClassCount)
     203             :             {
     204           0 :                 while (iClass >= nClassCount)
     205             :                 {
     206           0 :                     papszCategoryNames[nClassCount++] = CPLStrdup("");
     207           0 :                     papszCategoryNames[nClassCount] = nullptr;
     208             :                 }
     209             :             }
     210             : 
     211             :             // Replace target category name.
     212           0 :             CPLFree(papszCategoryNames[iClass]);
     213           0 :             papszCategoryNames[iClass] = nullptr;
     214             : 
     215           0 :             papszCategoryNames[iClass] = CPLStrdup(osName);
     216             :         }
     217             : 
     218           4 :         if (nClassCount == 0)
     219           4 :             return GDALPamRasterBand::GetCategoryNames();
     220             : 
     221           0 :         return papszCategoryNames;
     222             :     }
     223           0 :     catch (const PCIDSKException &ex)
     224             :     {
     225           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     226           0 :         return nullptr;
     227             :     }
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                         CheckForColorTable()                         */
     232             : /************************************************************************/
     233             : 
     234          26 : bool PCIDSK2Band::CheckForColorTable()
     235             : 
     236             : {
     237          26 :     if (bCheckedForColorTable || poFile == nullptr)
     238          14 :         return true;
     239             : 
     240          12 :     bCheckedForColorTable = true;
     241             : 
     242             :     try
     243             :     {
     244             :         /* --------------------------------------------------------------------
     245             :          */
     246             :         /*      Try to find an appropriate PCT segment to use. */
     247             :         /* --------------------------------------------------------------------
     248             :          */
     249             :         std::string osDefaultPCT =
     250          36 :             poChannel->GetMetadataValue("DEFAULT_PCT_REF");
     251          12 :         PCIDSKSegment *poPCTSeg = nullptr;
     252             : 
     253             :         // If there is no metadata, assume a single PCT in a file with only
     254             :         // one raster band must be intended for it.
     255          23 :         if (osDefaultPCT.empty() && poDS != nullptr &&
     256          11 :             poDS->GetRasterCount() == 1)
     257             :         {
     258          11 :             poPCTSeg = poFile->GetSegment(SEG_PCT, "");
     259          11 :             if (poPCTSeg != nullptr &&
     260          11 :                 poFile->GetSegment(SEG_PCT, "", poPCTSeg->GetSegmentNumber()) !=
     261             :                     nullptr)
     262           0 :                 poPCTSeg = nullptr;
     263             :         }
     264             :         // Parse default PCT ref assuming an in file reference.
     265           2 :         else if (!osDefaultPCT.empty() &&
     266           1 :                  strstr(osDefaultPCT.c_str(), "PCT:") != nullptr)
     267             :         {
     268           1 :             poPCTSeg = poFile->GetSegment(
     269           1 :                 atoi(strstr(osDefaultPCT.c_str(), "PCT:") + 4));
     270             :         }
     271             : 
     272          12 :         if (poPCTSeg != nullptr)
     273             :         {
     274           1 :             poColorTable = new GDALColorTable();
     275             :             unsigned char abyPCT[768];
     276             : 
     277           1 :             PCIDSK_PCT *poPCT = dynamic_cast<PCIDSK_PCT *>(poPCTSeg);
     278           1 :             if (poPCT)
     279             :             {
     280           1 :                 nPCTSegNumber = poPCTSeg->GetSegmentNumber();
     281             : 
     282           1 :                 poPCT->ReadPCT(abyPCT);
     283             : 
     284         257 :                 for (int i = 0; i < 256; i++)
     285             :                 {
     286             :                     GDALColorEntry sEntry;
     287             : 
     288         256 :                     sEntry.c1 = abyPCT[256 * 0 + i];
     289         256 :                     sEntry.c2 = abyPCT[256 * 1 + i];
     290         256 :                     sEntry.c3 = abyPCT[256 * 2 + i];
     291         256 :                     sEntry.c4 = 255;
     292         256 :                     poColorTable->SetColorEntry(i, &sEntry);
     293             :                 }
     294             :             }
     295             :         }
     296             : 
     297             :         /* --------------------------------------------------------------------
     298             :          */
     299             :         /*      If we did not find an appropriate PCT segment, check for */
     300             :         /*      Class_n color data from which to construct a color table. */
     301             :         /* --------------------------------------------------------------------
     302             :          */
     303          24 :         std::vector<std::string> aosMDKeys = poChannel->GetMetadataKeys();
     304             : 
     305          22 :         for (size_t i = 0; i < aosMDKeys.size(); i++)
     306             :         {
     307          10 :             CPLString osKey = aosMDKeys[i];
     308             : 
     309             :             // is this a "Class_n_name" keyword?
     310             : 
     311          10 :             if (!STARTS_WITH_CI(osKey, "Class_"))
     312          10 :                 continue;
     313             : 
     314           0 :             if (!EQUAL(osKey.c_str() + osKey.size() - 6, "_Color"))
     315           0 :                 continue;
     316             : 
     317             :             // Ignore unreasonable class values.
     318           0 :             const int iClass = atoi(osKey.c_str() + 6);
     319             : 
     320           0 :             if (iClass < 0 || iClass > 10000)
     321           0 :                 continue;
     322             : 
     323             :             // Fetch and parse the RGB value "(RGB:red green blue)"
     324           0 :             CPLString osRGB = poChannel->GetMetadataValue(osKey);
     325             : 
     326           0 :             if (!STARTS_WITH_CI(osRGB, "(RGB:"))
     327           0 :                 continue;
     328             : 
     329             :             int nRed, nGreen, nBlue;
     330           0 :             if (sscanf(osRGB.c_str() + 5, "%d %d %d", &nRed, &nGreen, &nBlue) !=
     331             :                 3)
     332           0 :                 continue;
     333             : 
     334             :             // we have an entry - apply to the color table.
     335             :             GDALColorEntry sEntry;
     336             : 
     337           0 :             sEntry.c1 = (short)nRed;
     338           0 :             sEntry.c2 = (short)nGreen;
     339           0 :             sEntry.c3 = (short)nBlue;
     340           0 :             sEntry.c4 = 255;
     341             : 
     342           0 :             if (poColorTable == nullptr)
     343             :             {
     344           0 :                 CPLDebug("PCIDSK",
     345             :                          "Using Class_n_Color metadata for color table.");
     346           0 :                 poColorTable = new GDALColorTable();
     347             :             }
     348             : 
     349           0 :             poColorTable->SetColorEntry(iClass, &sEntry);
     350             :         }
     351             :     }
     352           0 :     catch (const PCIDSKException &ex)
     353             :     {
     354           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     355           0 :         return false;
     356             :     }
     357             : 
     358          12 :     return true;
     359             : }
     360             : 
     361             : /************************************************************************/
     362             : /*                           GetColorTable()                            */
     363             : /************************************************************************/
     364             : 
     365          10 : GDALColorTable *PCIDSK2Band::GetColorTable()
     366             : 
     367             : {
     368          10 :     CheckForColorTable();
     369             : 
     370          10 :     if (poColorTable)
     371           2 :         return poColorTable;
     372             : 
     373           8 :     return GDALPamRasterBand::GetColorTable();
     374             : }
     375             : 
     376             : /************************************************************************/
     377             : /*                           SetColorTable()                            */
     378             : /************************************************************************/
     379             : 
     380           2 : CPLErr PCIDSK2Band::SetColorTable(GDALColorTable *poCT)
     381             : 
     382             : {
     383           2 :     if (!CheckForColorTable())
     384           0 :         return CE_Failure;
     385             : 
     386             :     // no color tables on overviews.
     387           2 :     if (poFile == nullptr)
     388           0 :         return CE_Failure;
     389             : 
     390           2 :     if (GetAccess() == GA_ReadOnly)
     391             :     {
     392           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     393             :                  "Unable to set color table on read-only file.");
     394           0 :         return CE_Failure;
     395             :     }
     396             : 
     397             :     try
     398             :     {
     399             :         /* --------------------------------------------------------------------
     400             :          */
     401             :         /*      Are we trying to delete the color table? */
     402             :         /* --------------------------------------------------------------------
     403             :          */
     404           2 :         if (poCT == nullptr)
     405             :         {
     406           1 :             delete poColorTable;
     407           1 :             poColorTable = nullptr;
     408             : 
     409           1 :             if (nPCTSegNumber != -1)
     410           1 :                 poFile->DeleteSegment(nPCTSegNumber);
     411           1 :             poChannel->SetMetadataValue("DEFAULT_PCT_REF", "");
     412           1 :             nPCTSegNumber = -1;
     413             : 
     414           1 :             return CE_None;
     415             :         }
     416             : 
     417             :         /* --------------------------------------------------------------------
     418             :          */
     419             :         /*      Do we need to create the segment?  If so, also set the */
     420             :         /*      default pct metadata. */
     421             :         /* --------------------------------------------------------------------
     422             :          */
     423           1 :         if (nPCTSegNumber == -1)
     424             :         {
     425           1 :             nPCTSegNumber = poFile->CreateSegment(
     426           1 :                 "PCTTable", "Default Pseudo-Color Table", SEG_PCT, 0);
     427             : 
     428           1 :             CPLString osRef;
     429           1 :             osRef.Printf("gdb:/{PCT:%d}", nPCTSegNumber);
     430           1 :             poChannel->SetMetadataValue("DEFAULT_PCT_REF", osRef);
     431             :         }
     432             : 
     433             :         /* --------------------------------------------------------------------
     434             :          */
     435             :         /*      Write out the PCT. */
     436             :         /* --------------------------------------------------------------------
     437             :          */
     438           1 :         const int nColorCount = std::min(256, poCT->GetColorEntryCount());
     439             : 
     440             :         unsigned char abyPCT[768];
     441           1 :         memset(abyPCT, 0, 768);
     442             : 
     443           4 :         for (int i = 0; i < nColorCount; i++)
     444             :         {
     445             :             GDALColorEntry sEntry;
     446             : 
     447           3 :             poCT->GetColorEntryAsRGB(i, &sEntry);
     448           3 :             abyPCT[256 * 0 + i] = (unsigned char)sEntry.c1;
     449           3 :             abyPCT[256 * 1 + i] = (unsigned char)sEntry.c2;
     450           3 :             abyPCT[256 * 2 + i] = (unsigned char)sEntry.c3;
     451             :         }
     452             : 
     453             :         PCIDSK_PCT *poPCT =
     454           1 :             dynamic_cast<PCIDSK_PCT *>(poFile->GetSegment(nPCTSegNumber));
     455           1 :         if (poPCT)
     456           1 :             poPCT->WritePCT(abyPCT);
     457             : 
     458           1 :         delete poColorTable;
     459           1 :         poColorTable = poCT->Clone();
     460             :     }
     461             : 
     462             :     /* -------------------------------------------------------------------- */
     463             :     /*      Trap exceptions.                                                */
     464             :     /* -------------------------------------------------------------------- */
     465           0 :     catch (const PCIDSKException &ex)
     466             :     {
     467           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     468           0 :         return CE_Failure;
     469             :     }
     470             : 
     471           1 :     return CE_None;
     472             : }
     473             : 
     474             : /************************************************************************/
     475             : /*                       GetColorInterpretation()                       */
     476             : /************************************************************************/
     477             : 
     478          14 : GDALColorInterp PCIDSK2Band::GetColorInterpretation()
     479             : 
     480             : {
     481          14 :     CheckForColorTable();
     482             : 
     483          14 :     if (poColorTable != nullptr)
     484           1 :         return GCI_PaletteIndex;
     485             : 
     486          13 :     return GDALPamRasterBand::GetColorInterpretation();
     487             : }
     488             : 
     489             : /************************************************************************/
     490             : /*                        RefreshOverviewList()                         */
     491             : /************************************************************************/
     492             : 
     493         232 : void PCIDSK2Band::RefreshOverviewList()
     494             : 
     495             : {
     496             :     /* -------------------------------------------------------------------- */
     497             :     /*      Clear existing overviews.                                       */
     498             :     /* -------------------------------------------------------------------- */
     499         232 :     while (!apoOverviews.empty())
     500             :     {
     501           0 :         delete apoOverviews.back();
     502           0 :         apoOverviews.pop_back();
     503             :     }
     504             : 
     505             :     /* -------------------------------------------------------------------- */
     506             :     /*      Fetch overviews.                                                */
     507             :     /* -------------------------------------------------------------------- */
     508         243 :     for (int iOver = 0; iOver < poChannel->GetOverviewCount(); iOver++)
     509             :     {
     510          11 :         auto poOvrBand = new PCIDSK2Band(poChannel->GetOverview(iOver));
     511          11 :         poOvrBand->eAccess = eAccess;
     512          11 :         apoOverviews.push_back(poOvrBand);
     513             :     }
     514         232 : }
     515             : 
     516             : /************************************************************************/
     517             : /*                             IReadBlock()                             */
     518             : /************************************************************************/
     519             : 
     520         261 : CPLErr PCIDSK2Band::IReadBlock(int iBlockX, int iBlockY, void *pData)
     521             : 
     522             : {
     523             :     try
     524             :     {
     525         261 :         poChannel->ReadBlock(iBlockX + iBlockY * nBlocksPerRow, pData);
     526             : 
     527             :         // Do we need to upsample 1bit to 8bit?
     528         261 :         if (poChannel->GetType() == CHN_BIT)
     529             :         {
     530           0 :             GByte *pabyData = reinterpret_cast<GByte *>(pData);
     531             : 
     532           0 :             for (int ii = nBlockXSize * nBlockYSize - 1; ii >= 0; ii--)
     533             :             {
     534           0 :                 if ((pabyData[ii >> 3] & (0x80 >> (ii & 0x7))))
     535           0 :                     pabyData[ii] = 1;
     536             :                 else
     537           0 :                     pabyData[ii] = 0;
     538             :             }
     539             :         }
     540             : 
     541         261 :         return CE_None;
     542             :     }
     543           0 :     catch (const PCIDSKException &ex)
     544             :     {
     545           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     546           0 :         return CE_Failure;
     547             :     }
     548             : }
     549             : 
     550             : /************************************************************************/
     551             : /*                             IWriteBlock()                            */
     552             : /************************************************************************/
     553             : 
     554         516 : CPLErr PCIDSK2Band::IWriteBlock(int iBlockX, int iBlockY, void *pData)
     555             : 
     556             : {
     557             :     try
     558             :     {
     559         516 :         poChannel->WriteBlock(iBlockX + iBlockY * nBlocksPerRow, pData);
     560             :     }
     561           0 :     catch (const PCIDSKException &ex)
     562             :     {
     563           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     564           0 :         return CE_Failure;
     565             :     }
     566             : 
     567         516 :     return CE_None;
     568             : }
     569             : 
     570             : /************************************************************************/
     571             : /*                          GetOverviewCount()                          */
     572             : /************************************************************************/
     573             : 
     574          16 : int PCIDSK2Band::GetOverviewCount()
     575             : 
     576             : {
     577          16 :     if (!apoOverviews.empty())
     578           5 :         return static_cast<int>(apoOverviews.size());
     579             : 
     580          11 :     return GDALPamRasterBand::GetOverviewCount();
     581             : }
     582             : 
     583             : /************************************************************************/
     584             : /*                            GetOverview()                             */
     585             : /************************************************************************/
     586             : 
     587           6 : GDALRasterBand *PCIDSK2Band::GetOverview(int iOverview)
     588             : 
     589             : {
     590           6 :     if (iOverview < 0 || iOverview >= static_cast<int>(apoOverviews.size()))
     591           1 :         return GDALPamRasterBand::GetOverview(iOverview);
     592             : 
     593           5 :     return apoOverviews[iOverview];
     594             : }
     595             : 
     596             : /************************************************************************/
     597             : /*                            SetMetadata()                             */
     598             : /************************************************************************/
     599             : 
     600           1 : CPLErr PCIDSK2Band::SetMetadata(char **papszMD, const char *pszDomain)
     601             : 
     602             : {
     603             :     /* -------------------------------------------------------------------- */
     604             :     /*      PCIDSK only supports metadata in the default domain.            */
     605             :     /* -------------------------------------------------------------------- */
     606           1 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
     607           0 :         return GDALPamRasterBand::SetMetadata(papszMD, pszDomain);
     608             : 
     609             :     /* -------------------------------------------------------------------- */
     610             :     /*      Set each item individually.                                     */
     611             :     /* -------------------------------------------------------------------- */
     612           1 :     CSLDestroy(papszLastMDListValue);
     613           1 :     papszLastMDListValue = nullptr;
     614           1 :     m_oCacheMetadataItem.clear();
     615             : 
     616           1 :     if (GetAccess() == GA_ReadOnly)
     617             :     {
     618           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     619             :                  "Unable to set metadata on read-only file.");
     620           0 :         return CE_Failure;
     621             :     }
     622             : 
     623             :     try
     624             :     {
     625           3 :         for (int iItem = 0; papszMD && papszMD[iItem]; iItem++)
     626             :         {
     627           2 :             char *pszItemName = nullptr;
     628             : 
     629             :             const char *pszItemValue =
     630           2 :                 CPLParseNameValue(papszMD[iItem], &pszItemName);
     631           2 :             if (pszItemName != nullptr)
     632             :             {
     633           2 :                 poChannel->SetMetadataValue(pszItemName, pszItemValue);
     634           2 :                 CPLFree(pszItemName);
     635             :             }
     636             :         }
     637             :     }
     638           0 :     catch (const PCIDSKException &ex)
     639             :     {
     640           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     641           0 :         return CE_Failure;
     642             :     }
     643             : 
     644           1 :     return CE_None;
     645             : }
     646             : 
     647             : /************************************************************************/
     648             : /*                          SetMetadataItem()                           */
     649             : /************************************************************************/
     650             : 
     651           5 : CPLErr PCIDSK2Band::SetMetadataItem(const char *pszName, const char *pszValue,
     652             :                                     const char *pszDomain)
     653             : 
     654             : {
     655             :     /* -------------------------------------------------------------------- */
     656             :     /*      PCIDSK only supports metadata in the default domain.            */
     657             :     /* -------------------------------------------------------------------- */
     658           5 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
     659           1 :         return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
     660             : 
     661             :     /* -------------------------------------------------------------------- */
     662             :     /*      Set on the file.                                                */
     663             :     /* -------------------------------------------------------------------- */
     664           4 :     CSLDestroy(papszLastMDListValue);
     665           4 :     papszLastMDListValue = nullptr;
     666           4 :     m_oCacheMetadataItem.clear();
     667             : 
     668           4 :     if (GetAccess() == GA_ReadOnly)
     669             :     {
     670           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     671             :                  "Unable to set metadata on read-only file.");
     672           0 :         return CE_Failure;
     673             :     }
     674             : 
     675             :     try
     676             :     {
     677           4 :         if (!pszValue)
     678           0 :             pszValue = "";
     679           4 :         poChannel->SetMetadataValue(pszName, pszValue);
     680             :     }
     681           0 :     catch (const PCIDSKException &ex)
     682             :     {
     683           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     684           0 :         return CE_Failure;
     685             :     }
     686             : 
     687           4 :     return CE_None;
     688             : }
     689             : 
     690             : /************************************************************************/
     691             : /*                      GetMetadataDomainList()                         */
     692             : /************************************************************************/
     693             : 
     694           0 : char **PCIDSK2Band::GetMetadataDomainList()
     695             : {
     696           0 :     return BuildMetadataDomainList(GDALPamRasterBand::GetMetadataDomainList(),
     697           0 :                                    TRUE, "", nullptr);
     698             : }
     699             : 
     700             : /************************************************************************/
     701             : /*                          GetMetadataItem()                           */
     702             : /************************************************************************/
     703             : 
     704          34 : const char *PCIDSK2Band::GetMetadataItem(const char *pszName,
     705             :                                          const char *pszDomain)
     706             : 
     707             : {
     708             :     /* -------------------------------------------------------------------- */
     709             :     /*      PCIDSK only supports metadata in the default domain.            */
     710             :     /* -------------------------------------------------------------------- */
     711          34 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
     712          23 :         return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
     713             : 
     714             :     /* -------------------------------------------------------------------- */
     715             :     /*      Try and fetch (use cached value if available)                   */
     716             :     /* -------------------------------------------------------------------- */
     717          11 :     auto oIter = m_oCacheMetadataItem.find(pszName);
     718          11 :     if (oIter != m_oCacheMetadataItem.end())
     719             :     {
     720           5 :         return oIter->second.empty() ? nullptr : oIter->second.c_str();
     721             :     }
     722             : 
     723          12 :     CPLString osValue;
     724             :     try
     725             :     {
     726           6 :         osValue = poChannel->GetMetadataValue(pszName);
     727             :     }
     728           0 :     catch (const PCIDSKException &ex)
     729             :     {
     730           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     731           0 :         return nullptr;
     732             :     }
     733             : 
     734           6 :     oIter = m_oCacheMetadataItem
     735           6 :                 .insert(std::pair<std::string, std::string>(pszName, osValue))
     736             :                 .first;
     737           6 :     return oIter->second.empty() ? nullptr : oIter->second.c_str();
     738             : }
     739             : 
     740             : /************************************************************************/
     741             : /*                            GetMetadata()                             */
     742             : /************************************************************************/
     743             : 
     744           6 : char **PCIDSK2Band::GetMetadata(const char *pszDomain)
     745             : 
     746             : {
     747             :     /* -------------------------------------------------------------------- */
     748             :     /*      PCIDSK only supports metadata in the default domain.            */
     749             :     /* -------------------------------------------------------------------- */
     750           6 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
     751           1 :         return GDALPamRasterBand::GetMetadata(pszDomain);
     752             : 
     753             :     /* -------------------------------------------------------------------- */
     754             :     /*      If we have a cached result, just use that.                      */
     755             :     /* -------------------------------------------------------------------- */
     756           5 :     if (papszLastMDListValue != nullptr)
     757           0 :         return papszLastMDListValue;
     758             : 
     759             :     /* -------------------------------------------------------------------- */
     760             :     /*      Fetch and build the list.                                       */
     761             :     /* -------------------------------------------------------------------- */
     762             :     try
     763             :     {
     764          10 :         std::vector<std::string> aosKeys = poChannel->GetMetadataKeys();
     765             : 
     766          11 :         for (unsigned int i = 0; i < aosKeys.size(); i++)
     767             :         {
     768           6 :             if (aosKeys[i].c_str()[0] == '_')
     769           3 :                 continue;
     770             : 
     771           6 :             papszLastMDListValue = CSLSetNameValue(
     772           3 :                 papszLastMDListValue, aosKeys[i].c_str(),
     773           6 :                 poChannel->GetMetadataValue(aosKeys[i]).c_str());
     774             :         }
     775             :     }
     776           0 :     catch (const PCIDSKException &ex)
     777             :     {
     778           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     779           0 :         return nullptr;
     780             :     }
     781             : 
     782           5 :     return papszLastMDListValue;
     783             : }
     784             : 
     785             : /************************************************************************/
     786             : /* ==================================================================== */
     787             : /*                            PCIDSK2Dataset                            */
     788             : /* ==================================================================== */
     789             : /************************************************************************/
     790             : 
     791             : /************************************************************************/
     792             : /*                           PCIDSK2Dataset()                            */
     793             : /************************************************************************/
     794             : 
     795         243 : PCIDSK2Dataset::PCIDSK2Dataset()
     796         243 :     : papszLastMDListValue(nullptr), poFile(nullptr)
     797             : {
     798         243 : }
     799             : 
     800             : /************************************************************************/
     801             : /*                            ~PCIDSK2Dataset()                          */
     802             : /************************************************************************/
     803             : 
     804             : // FIXME? is an exception can really be thrown in the destructor, then it is
     805             : // very dangerous !
     806             : #ifdef _MSC_VER
     807             : #pragma warning(push)
     808             : #pragma warning(disable : 4702) /*  unreachable code */
     809             : #endif
     810         486 : PCIDSK2Dataset::~PCIDSK2Dataset()
     811             : {
     812         243 :     PCIDSK2Dataset::FlushCache(true);
     813             : 
     814        1426 :     while (!apoLayers.empty())
     815             :     {
     816        1183 :         delete apoLayers.back();
     817        1183 :         apoLayers.pop_back();
     818             :     }
     819             : 
     820         243 :     if (m_poSRS)
     821           7 :         m_poSRS->Release();
     822             : 
     823             :     try
     824             :     {
     825         243 :         if (poFile != nullptr)
     826         243 :             delete poFile;
     827             :     }
     828             : 
     829             :     /* -------------------------------------------------------------------- */
     830             :     /*      Trap exceptions.                                                */
     831             :     /* -------------------------------------------------------------------- */
     832             :     catch (const PCIDSKException &ex)
     833             :     {
     834             :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     835             :     }
     836             :     catch (...)
     837             :     {
     838             :         CPLError(CE_Failure, CPLE_AppDefined,
     839             :                  "PCIDSK SDK Failure in Close(), unexpected exception.");
     840             :     }
     841             : 
     842         243 :     CSLDestroy(papszLastMDListValue);
     843         486 : }
     844             : #ifdef _MSC_VER
     845             : #pragma warning(pop)
     846             : #endif
     847             : 
     848             : /************************************************************************/
     849             : /*                            GetFileList()                             */
     850             : /************************************************************************/
     851             : 
     852          57 : char **PCIDSK2Dataset::GetFileList()
     853             : 
     854             : {
     855          57 :     char **papszFileList = GDALPamDataset::GetFileList();
     856         114 :     CPLString osBaseDir = CPLGetPathSafe(GetDescription());
     857             : 
     858             :     try
     859             :     {
     860          77 :         for (int nChan = 1; nChan <= poFile->GetChannels(); nChan++)
     861             :         {
     862          20 :             PCIDSKChannel *poChannel = poFile->GetChannel(nChan);
     863          40 :             CPLString osChanFilename;
     864             :             uint64 image_offset, pixel_offset, line_offset;
     865             :             bool little_endian;
     866             : 
     867          20 :             poChannel->GetChanInfo(osChanFilename, image_offset, pixel_offset,
     868          20 :                                    line_offset, little_endian);
     869             : 
     870          20 :             if (osChanFilename != "")
     871             :             {
     872           1 :                 papszFileList = CSLAddString(
     873             :                     papszFileList,
     874           2 :                     CPLProjectRelativeFilenameSafe(osBaseDir, osChanFilename)
     875             :                         .c_str());
     876             :             }
     877             :         }
     878             : 
     879          57 :         return papszFileList;
     880             :     }
     881           0 :     catch (const PCIDSKException &ex)
     882             :     {
     883           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
     884           0 :         return papszFileList;
     885             :     }
     886             : }
     887             : 
     888             : /************************************************************************/
     889             : /*                             ProcessRPC()                             */
     890             : /************************************************************************/
     891             : 
     892         243 : void PCIDSK2Dataset::ProcessRPC()
     893             : 
     894             : {
     895             :     /* -------------------------------------------------------------------- */
     896             :     /*      Search all BIN segments looking for an RPC segment.             */
     897             :     /* -------------------------------------------------------------------- */
     898         243 :     PCIDSKSegment *poSeg = poFile->GetSegment(SEG_BIN, "");
     899         243 :     PCIDSKRPCSegment *poRPCSeg = nullptr;
     900             : 
     901         243 :     while (poSeg != nullptr &&
     902           0 :            (poRPCSeg = dynamic_cast<PCIDSKRPCSegment *>(poSeg)) == nullptr)
     903             : 
     904             :     {
     905           0 :         poSeg = poFile->GetSegment(SEG_BIN, "", poSeg->GetSegmentNumber());
     906             :     }
     907             : 
     908         243 :     if (poRPCSeg == nullptr)
     909         243 :         return;
     910             : 
     911             :     /* -------------------------------------------------------------------- */
     912             :     /*      Turn RPC segment into GDAL RFC 22 style metadata.               */
     913             :     /* -------------------------------------------------------------------- */
     914             :     try
     915             :     {
     916           0 :         CPLString osValue;
     917             :         double dfLineOffset, dfLineScale, dfSampOffset, dfSampScale;
     918             :         double dfLatOffset, dfLatScale, dfLongOffset, dfLongScale,
     919             :             dfHeightOffset, dfHeightScale;
     920             : 
     921           0 :         poRPCSeg->GetRPCTranslationCoeffs(
     922             :             dfLongOffset, dfLongScale, dfLatOffset, dfLatScale, dfHeightOffset,
     923             :             dfHeightScale, dfSampOffset, dfSampScale, dfLineOffset,
     924           0 :             dfLineScale);
     925             : 
     926           0 :         osValue.Printf("%.16g", dfLineOffset);
     927           0 :         GDALPamDataset::SetMetadataItem("LINE_OFF", osValue, "RPC");
     928             : 
     929           0 :         osValue.Printf("%.16g", dfLineScale);
     930           0 :         GDALPamDataset::SetMetadataItem("LINE_SCALE", osValue, "RPC");
     931             : 
     932           0 :         osValue.Printf("%.16g", dfSampOffset);
     933           0 :         GDALPamDataset::SetMetadataItem("SAMP_OFF", osValue, "RPC");
     934             : 
     935           0 :         osValue.Printf("%.16g", dfSampScale);
     936           0 :         GDALPamDataset::SetMetadataItem("SAMP_SCALE", osValue, "RPC");
     937             : 
     938           0 :         osValue.Printf("%.16g", dfLongOffset);
     939           0 :         GDALPamDataset::SetMetadataItem("LONG_OFF", osValue, "RPC");
     940             : 
     941           0 :         osValue.Printf("%.16g", dfLongScale);
     942           0 :         GDALPamDataset::SetMetadataItem("LONG_SCALE", osValue, "RPC");
     943             : 
     944           0 :         osValue.Printf("%.16g", dfLatOffset);
     945           0 :         GDALPamDataset::SetMetadataItem("LAT_OFF", osValue, "RPC");
     946             : 
     947           0 :         osValue.Printf("%.16g", dfLatScale);
     948           0 :         GDALPamDataset::SetMetadataItem("LAT_SCALE", osValue, "RPC");
     949             : 
     950           0 :         osValue.Printf("%.16g", dfHeightOffset);
     951           0 :         GDALPamDataset::SetMetadataItem("HEIGHT_OFF", osValue, "RPC");
     952             : 
     953           0 :         osValue.Printf("%.16g", dfHeightScale);
     954           0 :         GDALPamDataset::SetMetadataItem("HEIGHT_SCALE", osValue, "RPC");
     955             : 
     956           0 :         if (poRPCSeg->GetXNumerator().size() != 20 ||
     957           0 :             poRPCSeg->GetXDenominator().size() != 20 ||
     958           0 :             poRPCSeg->GetYNumerator().size() != 20 ||
     959           0 :             poRPCSeg->GetYDenominator().size() != 20)
     960             :         {
     961           0 :             GDALPamDataset::SetMetadata(nullptr, "RPC");
     962           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     963             :                      "Did not get 20 values in the RPC coefficients lists.");
     964           0 :             return;
     965             :         }
     966             : 
     967           0 :         std::vector<double> adfCoef = poRPCSeg->GetYNumerator();
     968           0 :         CPLString osCoefList = "";
     969           0 :         for (int i = 0; i < 20; i++)
     970             :         {
     971           0 :             osValue.Printf("%.16g ", adfCoef[i]);
     972           0 :             osCoefList += osValue;
     973             :         }
     974           0 :         GDALPamDataset::SetMetadataItem("LINE_NUM_COEFF", osCoefList, "RPC");
     975             : 
     976           0 :         adfCoef = poRPCSeg->GetYDenominator();
     977           0 :         osCoefList = "";
     978           0 :         for (int i = 0; i < 20; i++)
     979             :         {
     980           0 :             osValue.Printf("%.16g ", adfCoef[i]);
     981           0 :             osCoefList += osValue;
     982             :         }
     983           0 :         GDALPamDataset::SetMetadataItem("LINE_DEN_COEFF", osCoefList, "RPC");
     984             : 
     985           0 :         adfCoef = poRPCSeg->GetXNumerator();
     986           0 :         osCoefList = "";
     987           0 :         for (int i = 0; i < 20; i++)
     988             :         {
     989           0 :             osValue.Printf("%.16g ", adfCoef[i]);
     990           0 :             osCoefList += osValue;
     991             :         }
     992           0 :         GDALPamDataset::SetMetadataItem("SAMP_NUM_COEFF", osCoefList, "RPC");
     993             : 
     994           0 :         adfCoef = poRPCSeg->GetXDenominator();
     995           0 :         osCoefList = "";
     996           0 :         for (int i = 0; i < 20; i++)
     997             :         {
     998           0 :             osValue.Printf("%.16g ", adfCoef[i]);
     999           0 :             osCoefList += osValue;
    1000             :         }
    1001           0 :         GDALPamDataset::SetMetadataItem("SAMP_DEN_COEFF", osCoefList, "RPC");
    1002             :     }
    1003           0 :     catch (const PCIDSKException &ex)
    1004             :     {
    1005           0 :         GDALPamDataset::SetMetadata(nullptr, "RPC");
    1006           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1007             :     }
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                             FlushCache()                             */
    1012             : /************************************************************************/
    1013             : 
    1014         254 : CPLErr PCIDSK2Dataset::FlushCache(bool bAtClosing)
    1015             : 
    1016             : {
    1017         254 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    1018             : 
    1019         254 :     if (poFile)
    1020             :     {
    1021             :         try
    1022             :         {
    1023         254 :             poFile->Synchronize();
    1024             :         }
    1025           0 :         catch (const PCIDSKException &ex)
    1026             :         {
    1027           0 :             eErr = CE_Failure;
    1028           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1029             :         }
    1030             :     }
    1031         254 :     return eErr;
    1032             : }
    1033             : 
    1034             : /************************************************************************/
    1035             : /*                            SetMetadata()                             */
    1036             : /************************************************************************/
    1037             : 
    1038          25 : CPLErr PCIDSK2Dataset::SetMetadata(char **papszMD, const char *pszDomain)
    1039             : 
    1040             : {
    1041             :     /* -------------------------------------------------------------------- */
    1042             :     /*      PCIDSK only supports metadata in the default domain.            */
    1043             :     /* -------------------------------------------------------------------- */
    1044          25 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    1045           0 :         return GDALPamDataset::SetMetadata(papszMD, pszDomain);
    1046             : 
    1047             :     /* -------------------------------------------------------------------- */
    1048             :     /*      Set each item individually.                                     */
    1049             :     /* -------------------------------------------------------------------- */
    1050          25 :     CSLDestroy(papszLastMDListValue);
    1051          25 :     papszLastMDListValue = nullptr;
    1052          25 :     m_oCacheMetadataItem.clear();
    1053             : 
    1054          25 :     if (GetAccess() == GA_ReadOnly)
    1055             :     {
    1056           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1057             :                  "Unable to set metadata on read-only file.");
    1058           0 :         return CE_Failure;
    1059             :     }
    1060             : 
    1061             :     try
    1062             :     {
    1063          57 :         for (int iItem = 0; papszMD && papszMD[iItem]; iItem++)
    1064             :         {
    1065          32 :             char *pszItemName = nullptr;
    1066             :             const char *pszItemValue =
    1067          32 :                 CPLParseNameValue(papszMD[iItem], &pszItemName);
    1068          32 :             if (pszItemName != nullptr)
    1069             :             {
    1070          16 :                 poFile->SetMetadataValue(pszItemName, pszItemValue);
    1071          16 :                 CPLFree(pszItemName);
    1072             :             }
    1073             :         }
    1074             :     }
    1075           0 :     catch (const PCIDSKException &ex)
    1076             :     {
    1077           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1078           0 :         return CE_Failure;
    1079             :     }
    1080             : 
    1081          25 :     return CE_None;
    1082             : }
    1083             : 
    1084             : /************************************************************************/
    1085             : /*                          SetMetadataItem()                           */
    1086             : /************************************************************************/
    1087             : 
    1088         220 : CPLErr PCIDSK2Dataset::SetMetadataItem(const char *pszName,
    1089             :                                        const char *pszValue,
    1090             :                                        const char *pszDomain)
    1091             : 
    1092             : {
    1093             :     /* -------------------------------------------------------------------- */
    1094             :     /*      PCIDSK only supports metadata in the default domain.            */
    1095             :     /* -------------------------------------------------------------------- */
    1096         220 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    1097         216 :         return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1098             : 
    1099             :     /* -------------------------------------------------------------------- */
    1100             :     /*      Set on the file.                                                */
    1101             :     /* -------------------------------------------------------------------- */
    1102           4 :     CSLDestroy(papszLastMDListValue);
    1103           4 :     papszLastMDListValue = nullptr;
    1104           4 :     m_oCacheMetadataItem.clear();
    1105             : 
    1106           4 :     if (GetAccess() == GA_ReadOnly)
    1107             :     {
    1108           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1109             :                  "Unable to set metadata on read-only file.");
    1110           0 :         return CE_Failure;
    1111             :     }
    1112             : 
    1113             :     try
    1114             :     {
    1115           4 :         poFile->SetMetadataValue(pszName, pszValue);
    1116             :     }
    1117           0 :     catch (const PCIDSKException &ex)
    1118             :     {
    1119           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1120           0 :         return CE_Failure;
    1121             :     }
    1122             : 
    1123           4 :     return CE_None;
    1124             : }
    1125             : 
    1126             : /************************************************************************/
    1127             : /*                      GetMetadataDomainList()                         */
    1128             : /************************************************************************/
    1129             : 
    1130           0 : char **PCIDSK2Dataset::GetMetadataDomainList()
    1131             : {
    1132           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
    1133           0 :                                    TRUE, "", nullptr);
    1134             : }
    1135             : 
    1136             : /************************************************************************/
    1137             : /*                          GetMetadataItem()                           */
    1138             : /************************************************************************/
    1139             : 
    1140        1239 : const char *PCIDSK2Dataset::GetMetadataItem(const char *pszName,
    1141             :                                             const char *pszDomain)
    1142             : 
    1143             : {
    1144             :     /* -------------------------------------------------------------------- */
    1145             :     /*      PCIDSK only supports metadata in the default domain.            */
    1146             :     /* -------------------------------------------------------------------- */
    1147        1239 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    1148          96 :         return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
    1149             : 
    1150             :     /* -------------------------------------------------------------------- */
    1151             :     /*      Try and fetch (use cached value if available)                   */
    1152             :     /* -------------------------------------------------------------------- */
    1153        1143 :     auto oIter = m_oCacheMetadataItem.find(pszName);
    1154        1143 :     if (oIter != m_oCacheMetadataItem.end())
    1155             :     {
    1156        1054 :         return oIter->second.empty() ? nullptr : oIter->second.c_str();
    1157             :     }
    1158             : 
    1159         178 :     CPLString osValue;
    1160             :     try
    1161             :     {
    1162          89 :         osValue = poFile->GetMetadataValue(pszName);
    1163             :     }
    1164           0 :     catch (const PCIDSKException &ex)
    1165             :     {
    1166           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1167           0 :         return nullptr;
    1168             :     }
    1169             : 
    1170          89 :     oIter = m_oCacheMetadataItem
    1171          89 :                 .insert(std::pair<std::string, std::string>(pszName, osValue))
    1172             :                 .first;
    1173          89 :     return oIter->second.empty() ? nullptr : oIter->second.c_str();
    1174             : }
    1175             : 
    1176             : /************************************************************************/
    1177             : /*                            GetMetadata()                             */
    1178             : /************************************************************************/
    1179             : 
    1180          52 : char **PCIDSK2Dataset::GetMetadata(const char *pszDomain)
    1181             : 
    1182             : {
    1183             :     /* -------------------------------------------------------------------- */
    1184             :     /*      PCIDSK only supports metadata in the default domain.            */
    1185             :     /* -------------------------------------------------------------------- */
    1186          52 :     if (pszDomain != nullptr && strlen(pszDomain) > 0)
    1187          25 :         return GDALPamDataset::GetMetadata(pszDomain);
    1188             : 
    1189             :     /* -------------------------------------------------------------------- */
    1190             :     /*      If we have a cached result, just use that.                      */
    1191             :     /* -------------------------------------------------------------------- */
    1192          27 :     if (papszLastMDListValue != nullptr)
    1193           3 :         return papszLastMDListValue;
    1194             : 
    1195             :     /* -------------------------------------------------------------------- */
    1196             :     /*      Fetch and build the list.                                       */
    1197             :     /* -------------------------------------------------------------------- */
    1198             :     try
    1199             :     {
    1200          48 :         std::vector<std::string> aosKeys = poFile->GetMetadataKeys();
    1201             : 
    1202          38 :         for (unsigned int i = 0; i < aosKeys.size(); i++)
    1203             :         {
    1204          14 :             if (aosKeys[i].c_str()[0] == '_')
    1205           2 :                 continue;
    1206             : 
    1207          12 :             papszLastMDListValue =
    1208          12 :                 CSLSetNameValue(papszLastMDListValue, aosKeys[i].c_str(),
    1209          24 :                                 poFile->GetMetadataValue(aosKeys[i]).c_str());
    1210             :         }
    1211             :     }
    1212           0 :     catch (const PCIDSKException &ex)
    1213             :     {
    1214           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1215           0 :         return nullptr;
    1216             :     }
    1217             : 
    1218          24 :     return papszLastMDListValue;
    1219             : }
    1220             : 
    1221             : /************************************************************************/
    1222             : /*                          SetGeoTransform()                           */
    1223             : /************************************************************************/
    1224             : 
    1225          56 : CPLErr PCIDSK2Dataset::SetGeoTransform(double *padfTransform)
    1226             : {
    1227          56 :     PCIDSKGeoref *poGeoref = nullptr;
    1228             :     try
    1229             :     {
    1230          56 :         PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
    1231          56 :         poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
    1232             :     }
    1233           0 :     catch (const PCIDSKException &)
    1234             :     {
    1235             :         // I should really check whether this is an expected issue.
    1236             :     }
    1237             : 
    1238          56 :     if (poGeoref == nullptr)
    1239           0 :         return GDALPamDataset::SetGeoTransform(padfTransform);
    1240             : 
    1241          56 :     if (GetAccess() == GA_ReadOnly)
    1242             :     {
    1243           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1244             :                  "Unable to set GeoTransform on read-only file.");
    1245           0 :         return CE_Failure;
    1246             :     }
    1247             : 
    1248             :     try
    1249             :     {
    1250          56 :         poGeoref->WriteSimple(poGeoref->GetGeosys(), padfTransform[0],
    1251          56 :                               padfTransform[1], padfTransform[2],
    1252          56 :                               padfTransform[3], padfTransform[4],
    1253          56 :                               padfTransform[5]);
    1254             :     }
    1255           0 :     catch (const PCIDSKException &ex)
    1256             :     {
    1257           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1258           0 :         return CE_Failure;
    1259             :     }
    1260             : 
    1261          56 :     return CE_None;
    1262             : }
    1263             : 
    1264             : /************************************************************************/
    1265             : /*                          GetGeoTransform()                           */
    1266             : /************************************************************************/
    1267             : 
    1268          44 : CPLErr PCIDSK2Dataset::GetGeoTransform(double *padfTransform)
    1269             : {
    1270          44 :     PCIDSKGeoref *poGeoref = nullptr;
    1271             :     try
    1272             :     {
    1273          44 :         PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
    1274          44 :         poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
    1275             :     }
    1276           0 :     catch (const PCIDSKException &)
    1277             :     {
    1278             :         // I should really check whether this is an expected issue.
    1279             :     }
    1280             : 
    1281          44 :     if (poGeoref != nullptr)
    1282             :     {
    1283             :         try
    1284             :         {
    1285          44 :             poGeoref->GetTransform(padfTransform[0], padfTransform[1],
    1286          44 :                                    padfTransform[2], padfTransform[3],
    1287          44 :                                    padfTransform[4], padfTransform[5]);
    1288             :         }
    1289           0 :         catch (const PCIDSKException &ex)
    1290             :         {
    1291           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1292           0 :             return CE_Failure;
    1293             :         }
    1294             : 
    1295             :         // If we got anything non-default return it.
    1296          44 :         if (padfTransform[0] != 0.0 || padfTransform[1] != 1.0 ||
    1297           3 :             padfTransform[2] != 0.0 || padfTransform[3] != 0.0 ||
    1298           3 :             padfTransform[4] != 0.0 || padfTransform[5] != 1.0)
    1299          41 :             return CE_None;
    1300             :     }
    1301             : 
    1302             :     /* -------------------------------------------------------------------- */
    1303             :     /*      Check for worldfile if we have no other georeferencing.         */
    1304             :     /* -------------------------------------------------------------------- */
    1305           3 :     if (GDALReadWorldFile(GetDescription(), "pxw", padfTransform))
    1306           0 :         return CE_None;
    1307             : 
    1308           3 :     return GDALPamDataset::GetGeoTransform(padfTransform);
    1309             : }
    1310             : 
    1311             : /************************************************************************/
    1312             : /*                           SetSpatialRef()                            */
    1313             : /************************************************************************/
    1314             : 
    1315          56 : CPLErr PCIDSK2Dataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1316             : 
    1317             : {
    1318          56 :     PCIDSKGeoref *poGeoref = nullptr;
    1319             : 
    1320             :     try
    1321             :     {
    1322          56 :         PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
    1323          56 :         poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
    1324             :     }
    1325           0 :     catch (const PCIDSKException &)
    1326             :     {
    1327             :         // I should really check whether this is an expected issue.
    1328             :     }
    1329             : 
    1330          56 :     if (poGeoref == nullptr)
    1331             :     {
    1332           0 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1333             :     }
    1334             : 
    1335          56 :     char *pszGeosys = nullptr;
    1336          56 :     char *pszUnits = nullptr;
    1337          56 :     double *padfPrjParams = nullptr;
    1338             : 
    1339          56 :     if (poSRS == nullptr || poSRS->exportToPCI(&pszGeosys, &pszUnits,
    1340             :                                                &padfPrjParams) != OGRERR_NONE)
    1341             :     {
    1342           0 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1343             :     }
    1344             : 
    1345          56 :     if (GetAccess() == GA_ReadOnly)
    1346             :     {
    1347           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1348             :                  "Unable to set projection on read-only file.");
    1349           0 :         CPLFree(pszGeosys);
    1350           0 :         CPLFree(pszUnits);
    1351           0 :         CPLFree(padfPrjParams);
    1352           0 :         return CE_Failure;
    1353             :     }
    1354             : 
    1355             :     try
    1356             :     {
    1357             :         double adfGT[6];
    1358          56 :         poGeoref->GetTransform(adfGT[0], adfGT[1], adfGT[2], adfGT[3], adfGT[4],
    1359          56 :                                adfGT[5]);
    1360             : 
    1361          56 :         poGeoref->WriteSimple(pszGeosys, adfGT[0], adfGT[1], adfGT[2], adfGT[3],
    1362          56 :                               adfGT[4], adfGT[5]);
    1363             : 
    1364         112 :         std::vector<double> adfPCIParameters;
    1365        1008 :         for (unsigned int i = 0; i < 17; i++)
    1366         952 :             adfPCIParameters.push_back(padfPrjParams[i]);
    1367             : 
    1368          56 :         if (STARTS_WITH_CI(pszUnits, "FOOT"))
    1369           0 :             adfPCIParameters.push_back(
    1370           0 :                 static_cast<double>(static_cast<int>(PCIDSK::UNIT_US_FOOT)));
    1371          56 :         else if (EQUALN(pszUnits, "INTL FOOT", 9))
    1372           0 :             adfPCIParameters.push_back(
    1373           0 :                 static_cast<double>(static_cast<int>(PCIDSK::UNIT_INTL_FOOT)));
    1374          56 :         else if (EQUALN(pszUnits, "DEGREE", 6))
    1375          48 :             adfPCIParameters.push_back(
    1376          48 :                 static_cast<double>(static_cast<int>(PCIDSK::UNIT_DEGREE)));
    1377             :         else
    1378           8 :             adfPCIParameters.push_back(
    1379           8 :                 static_cast<double>(static_cast<int>(PCIDSK::UNIT_METER)));
    1380             : 
    1381          56 :         poGeoref->WriteParameters(adfPCIParameters);
    1382             :     }
    1383           0 :     catch (const PCIDSKException &ex)
    1384             :     {
    1385           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1386           0 :         return CE_Failure;
    1387             :     }
    1388             : 
    1389          56 :     CPLFree(pszGeosys);
    1390          56 :     CPLFree(pszUnits);
    1391          56 :     CPLFree(padfPrjParams);
    1392             : 
    1393          56 :     return CE_None;
    1394             : }
    1395             : 
    1396             : /************************************************************************/
    1397             : /*                          GetSpatialRef()                             */
    1398             : /************************************************************************/
    1399             : 
    1400          13 : const OGRSpatialReference *PCIDSK2Dataset::GetSpatialRef() const
    1401             : {
    1402          13 :     if (m_poSRS)
    1403           3 :         return m_poSRS;
    1404             : 
    1405          10 :     PCIDSKGeoref *poGeoref = nullptr;
    1406             : 
    1407             :     try
    1408             :     {
    1409          10 :         PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
    1410          10 :         poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
    1411             :     }
    1412           0 :     catch (const PCIDSKException &)
    1413             :     {
    1414             :         // I should really check whether this is an expected issue.
    1415             :     }
    1416             : 
    1417          10 :     if (poGeoref == nullptr)
    1418             :     {
    1419           0 :         return GDALPamDataset::GetSpatialRef();
    1420             :     }
    1421             : 
    1422          20 :     CPLString osGeosys;
    1423          10 :     const char *pszUnits = nullptr;
    1424             : 
    1425          20 :     std::vector<double> adfParameters;
    1426          10 :     adfParameters.resize(18);
    1427             : 
    1428             :     try
    1429             :     {
    1430          10 :         osGeosys = poGeoref->GetGeosys();
    1431          10 :         adfParameters = poGeoref->GetParameters();
    1432             :         const UnitCode code =
    1433          10 :             static_cast<UnitCode>(static_cast<int>(adfParameters[16]));
    1434             : 
    1435          10 :         if (code == PCIDSK::UNIT_DEGREE)
    1436           0 :             pszUnits = "DEGREE";
    1437          10 :         else if (code == PCIDSK::UNIT_METER)
    1438           0 :             pszUnits = "METER";
    1439          10 :         else if (code == PCIDSK::UNIT_US_FOOT)
    1440           0 :             pszUnits = "FOOT";
    1441          10 :         else if (code == PCIDSK::UNIT_INTL_FOOT)
    1442           0 :             pszUnits = "INTL FOOT";
    1443             :     }
    1444           0 :     catch (const PCIDSKException &ex)
    1445             :     {
    1446           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1447             :     }
    1448             : 
    1449          20 :     OGRSpatialReference oSRS;
    1450          10 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1451          10 :     if (oSRS.importFromPCI(osGeosys, pszUnits, &(adfParameters[0])) ==
    1452             :         OGRERR_NONE)
    1453             :     {
    1454           7 :         m_poSRS = oSRS.Clone();
    1455           7 :         return m_poSRS;
    1456             :     }
    1457             :     else
    1458             :     {
    1459           3 :         return GDALPamDataset::GetSpatialRef();
    1460             :     }
    1461             : }
    1462             : 
    1463             : /************************************************************************/
    1464             : /*                          IBuildOverviews()                           */
    1465             : /************************************************************************/
    1466             : 
    1467           3 : CPLErr PCIDSK2Dataset::IBuildOverviews(
    1468             :     const char *pszResampling, int nOverviews, const int *panOverviewList,
    1469             :     int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
    1470             :     void *pProgressData, CSLConstList papszOptions)
    1471             : 
    1472             : {
    1473             :     PCIDSK2Band *poBand =
    1474           3 :         reinterpret_cast<PCIDSK2Band *>(GetRasterBand(panBandList[0]));
    1475             : 
    1476             :     /* -------------------------------------------------------------------- */
    1477             :     /*      If RRD overviews requested, then invoke generic handling.       */
    1478             :     /* -------------------------------------------------------------------- */
    1479           3 :     bool bUseGenericHandling = false;
    1480             : 
    1481           3 :     if (CPLTestBool(CPLGetConfigOption("USE_RRD", "NO")))
    1482             :     {
    1483           1 :         bUseGenericHandling = true;
    1484             :     }
    1485             : 
    1486             :     /* -------------------------------------------------------------------- */
    1487             :     /*      If we don't have read access, then create the overviews         */
    1488             :     /*      externally.                                                     */
    1489             :     /* -------------------------------------------------------------------- */
    1490           3 :     if (GetAccess() != GA_Update)
    1491             :     {
    1492           1 :         CPLDebug("PCIDSK", "File open for read-only accessing, "
    1493             :                            "creating overviews externally.");
    1494             : 
    1495           1 :         bUseGenericHandling = true;
    1496             :     }
    1497             : 
    1498           3 :     if (bUseGenericHandling)
    1499             :     {
    1500           2 :         if (poBand->GetOverviewCount() != 0)
    1501             :         {
    1502           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1503             :                      "Cannot add external overviews when there are already "
    1504             :                      "internal overviews");
    1505           0 :             return CE_Failure;
    1506             :         }
    1507             : 
    1508           2 :         return GDALDataset::IBuildOverviews(
    1509             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    1510           2 :             pfnProgress, pProgressData, papszOptions);
    1511             :     }
    1512             : 
    1513           1 :     if (nListBands == 0)
    1514           0 :         return CE_None;
    1515             : 
    1516             :     /* -------------------------------------------------------------------- */
    1517             :     /*      Currently no support for clearing overviews.                    */
    1518             :     /* -------------------------------------------------------------------- */
    1519           1 :     if (nOverviews == 0)
    1520             :     {
    1521           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1522             :                  "PCIDSK2 driver does not currently support clearing existing "
    1523             :                  "overviews. ");
    1524           0 :         return CE_Failure;
    1525             :     }
    1526             : 
    1527             :     /* -------------------------------------------------------------------- */
    1528             :     /*      Establish which of the overview levels we already have, and     */
    1529             :     /*      which are new.  We assume that band 1 of the file is            */
    1530             :     /*      representative.                                                 */
    1531             :     /* -------------------------------------------------------------------- */
    1532             : 
    1533           1 :     int nNewOverviews = 0;
    1534             :     int *panNewOverviewList =
    1535           1 :         reinterpret_cast<int *>(CPLCalloc(sizeof(int), nOverviews));
    1536           2 :     std::vector<bool> abFoundOverviewFactor(nOverviews);
    1537           2 :     for (int i = 0; i < nOverviews && poBand != nullptr; i++)
    1538             :     {
    1539           1 :         for (int j = 0; j < poBand->GetOverviewCount(); j++)
    1540             :         {
    1541           0 :             GDALRasterBand *poOverview = poBand->GetOverview(j);
    1542             : 
    1543             :             int nOvFactor =
    1544           0 :                 GDALComputeOvFactor(poOverview->GetXSize(), poBand->GetXSize(),
    1545             :                                     poOverview->GetYSize(), poBand->GetYSize());
    1546             : 
    1547           0 :             if (nOvFactor == panOverviewList[i] ||
    1548           0 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    1549             :                                                 poBand->GetXSize(),
    1550             :                                                 poBand->GetYSize()))
    1551           0 :                 abFoundOverviewFactor[i] = true;
    1552             :         }
    1553             : 
    1554           1 :         if (!abFoundOverviewFactor[i])
    1555           1 :             panNewOverviewList[nNewOverviews++] = panOverviewList[i];
    1556             :     }
    1557             : 
    1558             :     /* -------------------------------------------------------------------- */
    1559             :     /*      Create the overviews that are missing.                          */
    1560             :     /* -------------------------------------------------------------------- */
    1561           2 :     for (int i = 0; i < nNewOverviews; i++)
    1562             :     {
    1563             :         try
    1564             :         {
    1565             :             // conveniently our resampling values mostly match PCIDSK.
    1566           2 :             poFile->CreateOverviews(nListBands, panBandList,
    1567           2 :                                     panNewOverviewList[i], pszResampling);
    1568             :         }
    1569           0 :         catch (const PCIDSKException &ex)
    1570             :         {
    1571           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1572           0 :             CPLFree(panNewOverviewList);
    1573           0 :             return CE_Failure;
    1574             :         }
    1575             :     }
    1576             : 
    1577           1 :     CPLFree(panNewOverviewList);
    1578           1 :     panNewOverviewList = nullptr;
    1579             : 
    1580           2 :     for (int iBand = 0; iBand < nListBands; iBand++)
    1581             :     {
    1582             :         poBand =
    1583           1 :             reinterpret_cast<PCIDSK2Band *>(GetRasterBand(panBandList[iBand]));
    1584           1 :         reinterpret_cast<PCIDSK2Band *>(poBand)->RefreshOverviewList();
    1585             :     }
    1586             : 
    1587             :     /* -------------------------------------------------------------------- */
    1588             :     /*      Actually generate the overview imagery.                         */
    1589             :     /* -------------------------------------------------------------------- */
    1590           1 :     CPLErr eErr = CE_None;
    1591           1 :     std::vector<int> anRegenLevels;
    1592             : 
    1593             :     GDALRasterBand **papoOverviewBands = reinterpret_cast<GDALRasterBand **>(
    1594           1 :         CPLCalloc(sizeof(void *), nOverviews));
    1595             : 
    1596           2 :     for (int iBand = 0; iBand < nListBands && eErr == CE_None; iBand++)
    1597             :     {
    1598           1 :         nNewOverviews = 0;
    1599             : 
    1600             :         poBand =
    1601           1 :             reinterpret_cast<PCIDSK2Band *>(GetRasterBand(panBandList[iBand]));
    1602             : 
    1603           2 :         for (int i = 0; i < nOverviews && poBand != nullptr; i++)
    1604             :         {
    1605           1 :             for (int j = 0; j < poBand->GetOverviewCount(); j++)
    1606             :             {
    1607           1 :                 GDALRasterBand *poOverview = poBand->GetOverview(j);
    1608             : 
    1609           1 :                 int nOvFactor = GDALComputeOvFactor(
    1610             :                     poOverview->GetXSize(), poBand->GetXSize(),
    1611             :                     poOverview->GetYSize(), poBand->GetYSize());
    1612             : 
    1613           1 :                 if (nOvFactor == panOverviewList[i] ||
    1614           0 :                     nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    1615             :                                                     poBand->GetXSize(),
    1616             :                                                     poBand->GetYSize()))
    1617             :                 {
    1618           1 :                     papoOverviewBands[nNewOverviews++] = poOverview;
    1619           1 :                     anRegenLevels.push_back(j);
    1620           1 :                     break;
    1621             :                 }
    1622             :             }
    1623             :         }
    1624             : 
    1625           1 :         if (nNewOverviews > 0)
    1626             :         {
    1627           1 :             eErr = GDALRegenerateOverviewsEx(
    1628             :                 (GDALRasterBandH)poBand, nNewOverviews,
    1629             :                 reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
    1630             :                 pszResampling, pfnProgress, pProgressData, papszOptions);
    1631             : 
    1632             :             // Mark the regenerated overviews as valid.
    1633           2 :             for (int i = 0; i < static_cast<int>(anRegenLevels.size()); i++)
    1634           1 :                 poBand->poChannel->SetOverviewValidity(anRegenLevels[i], true);
    1635             :         }
    1636             :     }
    1637             : 
    1638           1 :     CPLFree(papoOverviewBands);
    1639             : 
    1640           1 :     return eErr;
    1641             : }
    1642             : 
    1643             : /************************************************************************/
    1644             : /*                         PCIDSKTypeToGDAL()                           */
    1645             : /************************************************************************/
    1646             : 
    1647         473 : GDALDataType PCIDSK2Dataset::PCIDSKTypeToGDAL(eChanType eType)
    1648             : {
    1649         473 :     switch (eType)
    1650             :     {
    1651         335 :         case CHN_8U:
    1652         335 :             return GDT_Byte;
    1653             : 
    1654          66 :         case CHN_16U:
    1655          66 :             return GDT_UInt16;
    1656             : 
    1657          18 :         case CHN_16S:
    1658          18 :             return GDT_Int16;
    1659             : 
    1660          18 :         case CHN_32R:
    1661          18 :             return GDT_Float32;
    1662             : 
    1663           0 :         case CHN_BIT:
    1664           0 :             return GDT_Byte;
    1665             : 
    1666           0 :         case CHN_C16U:
    1667           0 :             return GDT_CInt16;
    1668             : 
    1669          18 :         case CHN_C16S:
    1670          18 :             return GDT_CInt16;
    1671             : 
    1672          18 :         case CHN_C32R:
    1673          18 :             return GDT_CFloat32;
    1674             : 
    1675           0 :         default:
    1676           0 :             return GDT_Unknown;
    1677             :     }
    1678             : }
    1679             : 
    1680             : /************************************************************************/
    1681             : /*                                Open()                                */
    1682             : /************************************************************************/
    1683             : 
    1684         137 : GDALDataset *PCIDSK2Dataset::Open(GDALOpenInfo *poOpenInfo)
    1685             : {
    1686         137 :     if (!PCIDSKDriverIdentify(poOpenInfo))
    1687           0 :         return nullptr;
    1688             : 
    1689             :     /* -------------------------------------------------------------------- */
    1690             :     /*      Try opening the file.                                           */
    1691             :     /* -------------------------------------------------------------------- */
    1692         137 :     PCIDSKFile *poFile = nullptr;
    1693             :     const int nMaxBandCount =
    1694         137 :         atoi(CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536"));
    1695             :     try
    1696             :     {
    1697         278 :         poFile = PCIDSK::Open(poOpenInfo->pszFilename,
    1698         137 :                               poOpenInfo->eAccess == GA_ReadOnly ? "r" : "r+",
    1699             :                               PCIDSK2GetInterfaces(), nMaxBandCount);
    1700         136 :         if (poFile == nullptr)
    1701             :         {
    1702           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1703             :                      "Failed to re-open %s within PCIDSK driver.\n",
    1704             :                      poOpenInfo->pszFilename);
    1705           0 :             return nullptr;
    1706             :         }
    1707             : 
    1708             :         const bool bValidRasterDimensions =
    1709         136 :             poFile->GetWidth() && poFile->GetHeight();
    1710         136 :         if (!bValidRasterDimensions &&
    1711           0 :             (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
    1712           0 :             (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
    1713             :         {
    1714           0 :             delete poFile;
    1715           0 :             return nullptr;
    1716             :         }
    1717             : 
    1718             :         /* Check if this is a vector-only PCIDSK file and that we are */
    1719             :         /* opened in raster-only mode */
    1720         402 :         if (poOpenInfo->eAccess == GA_ReadOnly &&
    1721         130 :             (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
    1722         120 :             (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0 &&
    1723         269 :             poFile->GetChannels() == 0 &&
    1724         139 :             poFile->GetSegment(PCIDSK::SEG_VEC, "") != nullptr)
    1725             :         {
    1726           2 :             CPLDebug("PCIDSK",
    1727             :                      "This is a vector-only PCIDSK dataset, "
    1728             :                      "but it has been opened in read-only in raster-only mode");
    1729           2 :             delete poFile;
    1730           2 :             return nullptr;
    1731             :         }
    1732             :         /* Reverse test */
    1733         396 :         if (poOpenInfo->eAccess == GA_ReadOnly &&
    1734         128 :             (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
    1735          10 :             (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
    1736         262 :             poFile->GetChannels() != 0 &&
    1737         134 :             poFile->GetSegment(PCIDSK::SEG_VEC, "") == nullptr)
    1738             :         {
    1739           0 :             CPLDebug("PCIDSK",
    1740             :                      "This is a raster-only PCIDSK dataset, "
    1741             :                      "but it has been opened in read-only in vector-only mode");
    1742           0 :             delete poFile;
    1743           0 :             return nullptr;
    1744             :         }
    1745             : 
    1746         134 :         return LLOpen(poOpenInfo->pszFilename, poFile, poOpenInfo->eAccess,
    1747         134 :                       poOpenInfo->GetSiblingFiles());
    1748             :     }
    1749             :     /* -------------------------------------------------------------------- */
    1750             :     /*      Trap exceptions.                                                */
    1751             :     /* -------------------------------------------------------------------- */
    1752           1 :     catch (const PCIDSKException &ex)
    1753             :     {
    1754           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1755           1 :         delete poFile;
    1756           1 :         return nullptr;
    1757             :     }
    1758           0 :     catch (...)
    1759             :     {
    1760           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1761             :                  "PCIDSK::Create() failed, unexpected exception.");
    1762           0 :         delete poFile;
    1763           0 :         return nullptr;
    1764             :     }
    1765             : }
    1766             : 
    1767             : /************************************************************************/
    1768             : /*                               LLOpen()                               */
    1769             : /*                                                                      */
    1770             : /*      Low level variant of open that takes the preexisting            */
    1771             : /*      PCIDSKFile.                                                     */
    1772             : /************************************************************************/
    1773             : 
    1774         243 : GDALDataset *PCIDSK2Dataset::LLOpen(const char *pszFilename,
    1775             :                                     PCIDSK::PCIDSKFile *poFile,
    1776             :                                     GDALAccess eAccessIn,
    1777             :                                     char **papszSiblingFiles)
    1778             : 
    1779             : {
    1780         243 :     PCIDSK2Dataset *poDS = new PCIDSK2Dataset();
    1781             :     /* -------------------------------------------------------------------- */
    1782             :     /*      Create a corresponding GDALDataset.                             */
    1783             :     /* -------------------------------------------------------------------- */
    1784         243 :     poDS->poFile = poFile;
    1785         243 :     poDS->eAccess = eAccessIn;
    1786         243 :     poDS->nRasterXSize = poFile->GetWidth();
    1787         243 :     poDS->nRasterYSize = poFile->GetHeight();
    1788             : 
    1789             :     const bool bValidRasterDimensions =
    1790         243 :         poFile->GetWidth() && poFile->GetHeight();
    1791         243 :     if (!bValidRasterDimensions)
    1792             :     {
    1793           0 :         poDS->nRasterXSize = 512;
    1794           0 :         poDS->nRasterYSize = 512;
    1795             :     }
    1796             : 
    1797             :     try
    1798             :     {
    1799             : 
    1800             :         /* --------------------------------------------------------------------
    1801             :          */
    1802             :         /*      Are we specifically PIXEL or BAND interleaving? */
    1803             :         /*                                                                      */
    1804             :         /*      We don't set anything for FILE since it is harder to know if */
    1805             :         /*      this is tiled or what the on disk interleaving is. */
    1806             :         /* --------------------------------------------------------------------
    1807             :          */
    1808         243 :         if (EQUAL(poFile->GetInterleaving().c_str(), "PIXEL"))
    1809           0 :             poDS->SetMetadataItem("IMAGE_STRUCTURE", "PIXEL",
    1810             :                                   "IMAGE_STRUCTURE");
    1811         243 :         else if (EQUAL(poFile->GetInterleaving().c_str(), "BAND"))
    1812         215 :             poDS->SetMetadataItem("IMAGE_STRUCTURE", "BAND", "IMAGE_STRUCTURE");
    1813             : 
    1814             :         /* --------------------------------------------------------------------
    1815             :          */
    1816             :         /*      Create band objects. */
    1817             :         /* --------------------------------------------------------------------
    1818             :          */
    1819         474 :         for (int iBand = 0;
    1820         474 :              bValidRasterDimensions && iBand < poFile->GetChannels(); iBand++)
    1821             :         {
    1822         231 :             PCIDSKChannel *poChannel = poFile->GetChannel(iBand + 1);
    1823         462 :             if (poChannel->GetBlockWidth() <= 0 ||
    1824         231 :                 poChannel->GetBlockHeight() <= 0)
    1825             :             {
    1826           0 :                 delete poDS;
    1827           0 :                 return nullptr;
    1828             :             }
    1829             : 
    1830         231 :             if (PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType()) ==
    1831             :                 GDT_Unknown)
    1832             :             {
    1833           0 :                 continue;
    1834             :             }
    1835             : 
    1836         231 :             poDS->SetBand(poDS->GetRasterCount() + 1,
    1837         231 :                           new PCIDSK2Band(poFile, poChannel));
    1838             :         }
    1839             : 
    1840             :         /* --------------------------------------------------------------------
    1841             :          */
    1842             :         /*      Create band objects for bitmap segments. */
    1843             :         /* --------------------------------------------------------------------
    1844             :          */
    1845         243 :         int nLastBitmapSegment = 0;
    1846         243 :         PCIDSKSegment *poBitSeg = nullptr;
    1847             : 
    1848         486 :         while (bValidRasterDimensions &&
    1849         486 :                (poBitSeg = poFile->GetSegment(SEG_BIT, "",
    1850         243 :                                               nLastBitmapSegment)) != nullptr)
    1851             :         {
    1852           0 :             PCIDSKChannel *poChannel = dynamic_cast<PCIDSKChannel *>(poBitSeg);
    1853           0 :             if (poChannel == nullptr || poChannel->GetBlockWidth() <= 0 ||
    1854           0 :                 poChannel->GetBlockHeight() <= 0)
    1855             :             {
    1856           0 :                 delete poDS;
    1857           0 :                 return nullptr;
    1858             :             }
    1859             : 
    1860           0 :             if (PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType()) ==
    1861             :                 GDT_Unknown)
    1862             :             {
    1863           0 :                 continue;
    1864             :             }
    1865             : 
    1866           0 :             poDS->SetBand(poDS->GetRasterCount() + 1,
    1867           0 :                           new PCIDSK2Band(poChannel));
    1868             : 
    1869           0 :             nLastBitmapSegment = poBitSeg->GetSegmentNumber();
    1870             :         }
    1871             : 
    1872             :         /* --------------------------------------------------------------------
    1873             :          */
    1874             :         /*      Create vector layers from vector segments. */
    1875             :         /* --------------------------------------------------------------------
    1876             :          */
    1877         243 :         PCIDSK::PCIDSKSegment *segobj = poFile->GetSegment(PCIDSK::SEG_VEC, "");
    1878         338 :         for (; segobj != nullptr;
    1879          95 :              segobj = poFile->GetSegment(PCIDSK::SEG_VEC, "",
    1880          95 :                                          segobj->GetSegmentNumber()))
    1881             :         {
    1882             :             PCIDSK::PCIDSKVectorSegment *poVecSeg =
    1883          95 :                 dynamic_cast<PCIDSK::PCIDSKVectorSegment *>(segobj);
    1884          95 :             if (poVecSeg)
    1885          95 :                 poDS->apoLayers.push_back(new OGRPCIDSKLayer(
    1886          95 :                     poDS, segobj, poVecSeg, eAccessIn == GA_Update));
    1887             :         }
    1888             : 
    1889             :         /* --------------------------------------------------------------------
    1890             :          */
    1891             :         /*      Process RPC segment, if there is one. */
    1892             :         /* --------------------------------------------------------------------
    1893             :          */
    1894         243 :         poDS->ProcessRPC();
    1895             : 
    1896             :         /* --------------------------------------------------------------------
    1897             :          */
    1898             :         /*      Initialize any PAM information. */
    1899             :         /* --------------------------------------------------------------------
    1900             :          */
    1901         243 :         poDS->SetDescription(pszFilename);
    1902         243 :         poDS->TryLoadXML(papszSiblingFiles);
    1903             : 
    1904             :         /* --------------------------------------------------------------------
    1905             :          */
    1906             :         /*      Open overviews. */
    1907             :         /* --------------------------------------------------------------------
    1908             :          */
    1909         243 :         poDS->oOvManager.Initialize(poDS, pszFilename, papszSiblingFiles);
    1910             : 
    1911         243 :         return poDS;
    1912             :     }
    1913             : 
    1914             :     /* -------------------------------------------------------------------- */
    1915             :     /*      Trap exceptions.                                                */
    1916             :     /* -------------------------------------------------------------------- */
    1917           0 :     catch (const PCIDSKException &ex)
    1918             :     {
    1919           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    1920             :     }
    1921           0 :     catch (...)
    1922             :     {
    1923           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1924             :                  "PCIDSK SDK Failure in Open(), unexpected exception.");
    1925             :     }
    1926             : 
    1927             :     /* -------------------------------------------------------------------- */
    1928             :     /*      In case of exception, close dataset                             */
    1929             :     /* -------------------------------------------------------------------- */
    1930           0 :     delete poDS;
    1931             : 
    1932           0 :     return nullptr;
    1933             : }
    1934             : 
    1935             : /************************************************************************/
    1936             : /*                               Create()                               */
    1937             : /************************************************************************/
    1938             : 
    1939         123 : GDALDataset *PCIDSK2Dataset::Create(const char *pszFilename, int nXSize,
    1940             :                                     int nYSize, int nBandsIn,
    1941             :                                     GDALDataType eType, char **papszParamList)
    1942             : 
    1943             : {
    1944             :     /* -------------------------------------------------------------------- */
    1945             :     /*      Prepare channel type list.                                      */
    1946             :     /* -------------------------------------------------------------------- */
    1947         246 :     std::vector<eChanType> aeChanTypes;
    1948             : 
    1949         123 :     if (eType == GDT_Float32)
    1950           3 :         aeChanTypes.resize(std::max(1, nBandsIn), CHN_32R);
    1951         120 :     else if (eType == GDT_Int16)
    1952           3 :         aeChanTypes.resize(std::max(1, nBandsIn), CHN_16S);
    1953         117 :     else if (eType == GDT_UInt16)
    1954          11 :         aeChanTypes.resize(std::max(1, nBandsIn), CHN_16U);
    1955         106 :     else if (eType == GDT_CInt16)
    1956           3 :         aeChanTypes.resize(std::max(1, nBandsIn), CHN_C16S);
    1957         103 :     else if (eType == GDT_CFloat32)
    1958           3 :         aeChanTypes.resize(std::max(1, nBandsIn), CHN_C32R);
    1959             :     else
    1960         100 :         aeChanTypes.resize(std::max(1, nBandsIn), CHN_8U);
    1961             : 
    1962             :     /* -------------------------------------------------------------------- */
    1963             :     /*      Reformat options.  Currently no support for jpeg compression    */
    1964             :     /*      quality.                                                        */
    1965             :     /* -------------------------------------------------------------------- */
    1966         246 :     CPLString osOptions;
    1967         123 :     const char *pszValue = CSLFetchNameValue(papszParamList, "INTERLEAVING");
    1968         123 :     if (pszValue == nullptr)
    1969         115 :         pszValue = "BAND";
    1970             : 
    1971         123 :     osOptions = pszValue;
    1972             : 
    1973         123 :     if (osOptions == "TILED")
    1974             :     {
    1975           7 :         pszValue = CSLFetchNameValue(papszParamList, "TILESIZE");
    1976           7 :         if (pszValue != nullptr)
    1977           6 :             osOptions += pszValue;
    1978             : 
    1979           7 :         pszValue = CSLFetchNameValue(papszParamList, "COMPRESSION");
    1980           7 :         if (pszValue != nullptr)
    1981             :         {
    1982           4 :             osOptions += " ";
    1983           4 :             osOptions += pszValue;
    1984             :         }
    1985             : 
    1986           7 :         pszValue = CSLFetchNameValue(papszParamList, "TILEVERSION");
    1987           7 :         if (pszValue != nullptr)
    1988             :         {
    1989           4 :             osOptions += " TILEV";
    1990           4 :             osOptions += pszValue;
    1991             :         }
    1992             :     }
    1993             : 
    1994             :     /* -------------------------------------------------------------------- */
    1995             :     /*      Try creation.                                                   */
    1996             :     /* -------------------------------------------------------------------- */
    1997             : 
    1998             :     try
    1999             :     {
    2000         123 :         if (nBandsIn == 0)
    2001             :         {
    2002          43 :             nXSize = 512;
    2003          43 :             nYSize = 512;
    2004             :         }
    2005         274 :         PCIDSKFile *poFile = PCIDSK::Create(pszFilename, nXSize, nYSize,
    2006         123 :                                             nBandsIn, &(aeChanTypes[0]),
    2007             :                                             osOptions, PCIDSK2GetInterfaces());
    2008             : 
    2009             :         /* --------------------------------------------------------------------
    2010             :          */
    2011             :         /*      Apply band descriptions, if provided as creation options. */
    2012             :         /* --------------------------------------------------------------------
    2013             :          */
    2014         131 :         for (size_t i = 0;
    2015         131 :              papszParamList != nullptr && papszParamList[i] != nullptr; i++)
    2016             :         {
    2017          22 :             if (STARTS_WITH_CI(papszParamList[i], "BANDDESC"))
    2018             :             {
    2019           0 :                 int nBand = atoi(papszParamList[i] + 8);
    2020           0 :                 const char *pszDescription = strstr(papszParamList[i], "=");
    2021           0 :                 if (pszDescription && nBand > 0 && nBand <= nBandsIn)
    2022             :                 {
    2023           0 :                     poFile->GetChannel(nBand)->SetDescription(pszDescription +
    2024           0 :                                                               1);
    2025             :                 }
    2026             :             }
    2027             :         }
    2028             : 
    2029         109 :         return LLOpen(pszFilename, poFile, GA_Update);
    2030             :     }
    2031             :     /* -------------------------------------------------------------------- */
    2032             :     /*      Trap exceptions.                                                */
    2033             :     /* -------------------------------------------------------------------- */
    2034          28 :     catch (const PCIDSKException &ex)
    2035             :     {
    2036          14 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    2037             :     }
    2038           0 :     catch (...)
    2039             :     {
    2040           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2041             :                  "PCIDSK::Create() failed, unexpected exception.");
    2042             :     }
    2043             : 
    2044          14 :     return nullptr;
    2045             : }
    2046             : 
    2047             : /************************************************************************/
    2048             : /*                           TestCapability()                           */
    2049             : /************************************************************************/
    2050             : 
    2051          79 : int PCIDSK2Dataset::TestCapability(const char *pszCap)
    2052             : 
    2053             : {
    2054          79 :     if (EQUAL(pszCap, ODsCCreateLayer))
    2055          37 :         return eAccess == GA_Update;
    2056          42 :     if (EQUAL(pszCap, ODsCRandomLayerWrite))
    2057           0 :         return eAccess == GA_Update;
    2058          42 :     if (EQUAL(pszCap, ODsCZGeometries))
    2059          12 :         return TRUE;
    2060             : 
    2061          30 :     return FALSE;
    2062             : }
    2063             : 
    2064             : /************************************************************************/
    2065             : /*                              GetLayer()                              */
    2066             : /************************************************************************/
    2067             : 
    2068        1655 : OGRLayer *PCIDSK2Dataset::GetLayer(int iLayer)
    2069             : 
    2070             : {
    2071        1655 :     if (iLayer < 0 || iLayer >= static_cast<int>(apoLayers.size()))
    2072           2 :         return nullptr;
    2073             : 
    2074        1653 :     return apoLayers[iLayer];
    2075             : }
    2076             : 
    2077             : /************************************************************************/
    2078             : /*                           ICreateLayer()                             */
    2079             : /************************************************************************/
    2080             : 
    2081        1089 : OGRLayer *PCIDSK2Dataset::ICreateLayer(const char *pszLayerName,
    2082             :                                        const OGRGeomFieldDefn *poGeomFieldDefn,
    2083             :                                        CSLConstList /*papszOptions*/)
    2084             : {
    2085             :     /* -------------------------------------------------------------------- */
    2086             :     /*      Verify we are in update mode.                                   */
    2087             :     /* -------------------------------------------------------------------- */
    2088        1089 :     if (eAccess != GA_Update)
    2089             :     {
    2090           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    2091             :                  "Data source %s opened read-only.\n"
    2092             :                  "New layer %s cannot be created.\n",
    2093           0 :                  GetDescription(), pszLayerName);
    2094           0 :         return nullptr;
    2095             :     }
    2096             : 
    2097        1089 :     const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    2098             :     const auto poSRS =
    2099        1089 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
    2100             : 
    2101             :     /* -------------------------------------------------------------------- */
    2102             :     /*      Figure out what type of layer we need.                          */
    2103             :     /* -------------------------------------------------------------------- */
    2104        2178 :     std::string osLayerType;
    2105             : 
    2106        1089 :     switch (wkbFlatten(eType))
    2107             :     {
    2108          10 :         case wkbPoint:
    2109          10 :             osLayerType = "POINTS";
    2110          10 :             break;
    2111             : 
    2112          10 :         case wkbLineString:
    2113          10 :             osLayerType = "ARCS";
    2114          10 :             break;
    2115             : 
    2116           6 :         case wkbPolygon:
    2117           6 :             osLayerType = "WHOLE_POLYGONS";
    2118           6 :             break;
    2119             : 
    2120           7 :         case wkbNone:
    2121           7 :             osLayerType = "TABLE";
    2122           7 :             break;
    2123             : 
    2124        1056 :         default:
    2125        1056 :             break;
    2126             :     }
    2127             : 
    2128             :     /* -------------------------------------------------------------------- */
    2129             :     /*      Create the segment.                                             */
    2130             :     /* -------------------------------------------------------------------- */
    2131             :     int nSegNum;
    2132             :     try
    2133             :     {
    2134        1093 :         nSegNum = poFile->CreateSegment(pszLayerName, "", PCIDSK::SEG_VEC, 0L);
    2135             :     }
    2136           1 :     catch (const PCIDSKException &ex)
    2137             :     {
    2138           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    2139           1 :         return nullptr;
    2140             :     }
    2141        1088 :     PCIDSK::PCIDSKSegment *poSeg = poFile->GetSegment(nSegNum);
    2142             :     PCIDSK::PCIDSKVectorSegment *poVecSeg =
    2143        1088 :         dynamic_cast<PCIDSK::PCIDSKVectorSegment *>(poSeg);
    2144        1088 :     if (poVecSeg == nullptr)
    2145           0 :         return nullptr;
    2146             : 
    2147        1088 :     if (osLayerType != "")
    2148          33 :         poSeg->SetMetadataValue("LAYER_TYPE", osLayerType);
    2149             : 
    2150             :     /* -------------------------------------------------------------------- */
    2151             :     /*      Do we need to apply a coordinate system?                        */
    2152             :     /* -------------------------------------------------------------------- */
    2153        1088 :     char *pszGeosys = nullptr;
    2154        1088 :     char *pszUnits = nullptr;
    2155        1088 :     double *padfPrjParams = nullptr;
    2156             : 
    2157        1088 :     if (poSRS != nullptr && poSRS->exportToPCI(&pszGeosys, &pszUnits,
    2158             :                                                &padfPrjParams) == OGRERR_NONE)
    2159             :     {
    2160             :         try
    2161             :         {
    2162           6 :             std::vector<double> adfPCIParameters;
    2163             : 
    2164         108 :             for (int i = 0; i < 17; i++)
    2165         102 :                 adfPCIParameters.push_back(padfPrjParams[i]);
    2166             : 
    2167           6 :             if (STARTS_WITH_CI(pszUnits, "FOOT"))
    2168           0 :                 adfPCIParameters.push_back(static_cast<double>(
    2169             :                     static_cast<int>(PCIDSK::UNIT_US_FOOT)));
    2170           6 :             else if (STARTS_WITH_CI(pszUnits, "INTL FOOT"))
    2171           0 :                 adfPCIParameters.push_back(static_cast<double>(
    2172             :                     static_cast<int>(PCIDSK::UNIT_INTL_FOOT)));
    2173           6 :             else if (STARTS_WITH_CI(pszUnits, "DEGREE"))
    2174           2 :                 adfPCIParameters.push_back(
    2175           2 :                     static_cast<double>(static_cast<int>(PCIDSK::UNIT_DEGREE)));
    2176             :             else
    2177           4 :                 adfPCIParameters.push_back(
    2178           4 :                     static_cast<double>(static_cast<int>(PCIDSK::UNIT_METER)));
    2179             : 
    2180           6 :             poVecSeg->SetProjection(pszGeosys, adfPCIParameters);
    2181             :         }
    2182           0 :         catch (const PCIDSKException &ex)
    2183             :         {
    2184           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
    2185             :         }
    2186             : 
    2187           6 :         CPLFree(pszGeosys);
    2188           6 :         CPLFree(pszUnits);
    2189           6 :         CPLFree(padfPrjParams);
    2190             :     }
    2191             : 
    2192             :     /* -------------------------------------------------------------------- */
    2193             :     /*      Create the layer object.                                        */
    2194             :     /* -------------------------------------------------------------------- */
    2195             : 
    2196        1088 :     apoLayers.push_back(new OGRPCIDSKLayer(this, poSeg, poVecSeg, TRUE));
    2197             : 
    2198        1088 :     return apoLayers.back();
    2199             : }
    2200             : 
    2201             : /************************************************************************/
    2202             : /*                        GDALRegister_PCIDSK()                         */
    2203             : /************************************************************************/
    2204             : 
    2205          13 : void GDALRegister_PCIDSK()
    2206             : 
    2207             : {
    2208          13 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    2209           0 :         return;
    2210             : 
    2211          13 :     GDALDriver *poDriver = new GDALDriver();
    2212          13 :     PCIDSKDriverSetCommonMetadata(poDriver);
    2213             : 
    2214          13 :     poDriver->pfnOpen = PCIDSK2Dataset::Open;
    2215          13 :     poDriver->pfnCreate = PCIDSK2Dataset::Create;
    2216             : 
    2217          13 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2218             : }

Generated by: LCOV version 1.14