LCOV - code coverage report
Current view: top level - frmts/raw - ndfdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 143 174 82.2 %
Date: 2024-11-21 22:18:42 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NDF Driver
       4             :  * Purpose:  Implementation of NLAPS Data Format read support.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam
       9             :  * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_string.h"
      15             : #include "gdal_frmts.h"
      16             : #include "ogr_spatialref.h"
      17             : #include "rawdataset.h"
      18             : 
      19             : /************************************************************************/
      20             : /* ==================================================================== */
      21             : /*                              NDFDataset                              */
      22             : /* ==================================================================== */
      23             : /************************************************************************/
      24             : 
      25             : class NDFDataset final : public RawDataset
      26             : {
      27             :     double adfGeoTransform[6];
      28             : 
      29             :     OGRSpatialReference m_oSRS{};
      30             :     char **papszExtraFiles;
      31             : 
      32             :     char **papszHeader;
      33             :     const char *Get(const char *pszKey, const char *pszDefault);
      34             : 
      35             :     CPL_DISALLOW_COPY_ASSIGN(NDFDataset)
      36             : 
      37             :     CPLErr Close() override;
      38             : 
      39             :   public:
      40             :     NDFDataset();
      41             :     ~NDFDataset() override;
      42             : 
      43             :     CPLErr GetGeoTransform(double *padfTransform) override;
      44             : 
      45           1 :     const OGRSpatialReference *GetSpatialRef() const override
      46             :     {
      47           1 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
      48             :     }
      49             : 
      50             :     char **GetFileList(void) override;
      51             : 
      52             :     static GDALDataset *Open(GDALOpenInfo *);
      53             :     static int Identify(GDALOpenInfo *);
      54             : };
      55             : 
      56             : /************************************************************************/
      57             : /*                            NDFDataset()                             */
      58             : /************************************************************************/
      59             : 
      60           2 : NDFDataset::NDFDataset() : papszExtraFiles(nullptr), papszHeader(nullptr)
      61             : {
      62           2 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      63           2 :     adfGeoTransform[0] = 0.0;
      64           2 :     adfGeoTransform[1] = 1.0;
      65           2 :     adfGeoTransform[2] = 0.0;
      66           2 :     adfGeoTransform[3] = 0.0;
      67           2 :     adfGeoTransform[4] = 0.0;
      68           2 :     adfGeoTransform[5] = 1.0;
      69           2 : }
      70             : 
      71             : /************************************************************************/
      72             : /*                            ~NDFDataset()                             */
      73             : /************************************************************************/
      74             : 
      75           4 : NDFDataset::~NDFDataset()
      76             : 
      77             : {
      78           2 :     NDFDataset::Close();
      79           4 : }
      80             : 
      81             : /************************************************************************/
      82             : /*                              Close()                                 */
      83             : /************************************************************************/
      84             : 
      85           4 : CPLErr NDFDataset::Close()
      86             : {
      87           4 :     CPLErr eErr = CE_None;
      88           4 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
      89             :     {
      90           2 :         if (NDFDataset::FlushCache(true) != CE_None)
      91           0 :             eErr = CE_Failure;
      92             : 
      93           2 :         CSLDestroy(papszHeader);
      94           2 :         CSLDestroy(papszExtraFiles);
      95             : 
      96           2 :         if (GDALPamDataset::Close() != CE_None)
      97           0 :             eErr = CE_Failure;
      98             :     }
      99           4 :     return eErr;
     100             : }
     101             : 
     102             : /************************************************************************/
     103             : /*                          GetGeoTransform()                           */
     104             : /************************************************************************/
     105             : 
     106           1 : CPLErr NDFDataset::GetGeoTransform(double *padfTransform)
     107             : 
     108             : {
     109           1 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     110           1 :     return CE_None;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                                Get()                                 */
     115             : /*                                                                      */
     116             : /*      Fetch a value from the header by keyword.                       */
     117             : /************************************************************************/
     118             : 
     119          26 : const char *NDFDataset::Get(const char *pszKey, const char *pszDefault)
     120             : 
     121             : {
     122          26 :     const char *pszResult = CSLFetchNameValue(papszHeader, pszKey);
     123             : 
     124          26 :     if (pszResult == nullptr)
     125           0 :         return pszDefault;
     126             : 
     127          26 :     return pszResult;
     128             : }
     129             : 
     130             : /************************************************************************/
     131             : /*                            GetFileList()                             */
     132             : /************************************************************************/
     133             : 
     134           1 : char **NDFDataset::GetFileList()
     135             : 
     136             : {
     137             :     // Main data file, etc.
     138           1 :     char **papszFileList = GDALPamDataset::GetFileList();
     139             : 
     140             :     // Header file.
     141           1 :     papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
     142             : 
     143           1 :     return papszFileList;
     144             : }
     145             : 
     146             : /************************************************************************/
     147             : /*                            Identify()                                */
     148             : /************************************************************************/
     149             : 
     150       51572 : int NDFDataset::Identify(GDALOpenInfo *poOpenInfo)
     151             : 
     152             : {
     153             :     /* -------------------------------------------------------------------- */
     154             :     /*      The user must select the header file (i.e. .H1).                */
     155             :     /* -------------------------------------------------------------------- */
     156       55073 :     return poOpenInfo->nHeaderBytes >= 50 &&
     157        3501 :            (STARTS_WITH_CI(
     158             :                 reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
     159        3497 :                 "NDF_REVISION=2") ||
     160        3497 :             STARTS_WITH_CI(
     161             :                 reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
     162       51572 :                 "NDF_REVISION=0"));
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                                Open()                                */
     167             : /************************************************************************/
     168             : 
     169           2 : GDALDataset *NDFDataset::Open(GDALOpenInfo *poOpenInfo)
     170             : 
     171             : {
     172             :     /* -------------------------------------------------------------------- */
     173             :     /*      The user must select the header file (i.e. .H1).                */
     174             :     /* -------------------------------------------------------------------- */
     175           2 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     176           0 :         return nullptr;
     177             : 
     178             :     /* -------------------------------------------------------------------- */
     179             :     /*      Confirm the requested access is supported.                      */
     180             :     /* -------------------------------------------------------------------- */
     181           2 :     if (poOpenInfo->eAccess == GA_Update)
     182             :     {
     183           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     184             :                  "The NDF driver does not support update access to existing"
     185             :                  " datasets.");
     186           0 :         return nullptr;
     187             :     }
     188             :     /* -------------------------------------------------------------------- */
     189             :     /*      Read and process the header into a local name/value             */
     190             :     /*      stringlist.  We just take off the trailing semicolon.  The      */
     191             :     /*      keyword is already separated from the value by an equal         */
     192             :     /*      sign.                                                           */
     193             :     /* -------------------------------------------------------------------- */
     194             : 
     195           2 :     const char *pszLine = nullptr;
     196           2 :     const int nHeaderMax = 1000;
     197           2 :     int nHeaderLines = 0;
     198             :     char **papszHeader =
     199           2 :         static_cast<char **>(CPLMalloc(sizeof(char *) * (nHeaderMax + 1)));
     200             : 
     201         210 :     while (nHeaderLines < nHeaderMax &&
     202         212 :            (pszLine = CPLReadLineL(poOpenInfo->fpL)) != nullptr &&
     203         106 :            !EQUAL(pszLine, "END_OF_HDR;"))
     204             :     {
     205         104 :         if (strstr(pszLine, "=") == nullptr)
     206           0 :             break;
     207             : 
     208         104 :         char *pszFixed = CPLStrdup(pszLine);
     209         104 :         if (pszFixed[strlen(pszFixed) - 1] == ';')
     210         104 :             pszFixed[strlen(pszFixed) - 1] = '\0';
     211             : 
     212         104 :         papszHeader[nHeaderLines++] = pszFixed;
     213         104 :         papszHeader[nHeaderLines] = nullptr;
     214             :     }
     215           2 :     CPL_IGNORE_RET_VAL(VSIFCloseL(poOpenInfo->fpL));
     216           2 :     poOpenInfo->fpL = nullptr;
     217             : 
     218           2 :     if (CSLFetchNameValue(papszHeader, "PIXELS_PER_LINE") == nullptr ||
     219           2 :         CSLFetchNameValue(papszHeader, "LINES_PER_DATA_FILE") == nullptr ||
     220           6 :         CSLFetchNameValue(papszHeader, "BITS_PER_PIXEL") == nullptr ||
     221           2 :         CSLFetchNameValue(papszHeader, "PIXEL_FORMAT") == nullptr)
     222             :     {
     223           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     224             :                  "Dataset appears to be NDF but is missing a required field.");
     225           0 :         CSLDestroy(papszHeader);
     226           0 :         return nullptr;
     227             :     }
     228             : 
     229           4 :     if (!EQUAL(CSLFetchNameValue(papszHeader, "PIXEL_FORMAT"), "BYTE") ||
     230           2 :         !EQUAL(CSLFetchNameValue(papszHeader, "BITS_PER_PIXEL"), "8"))
     231             :     {
     232           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     233             :                  "Currently NDF driver supports only 8bit BYTE format.");
     234           0 :         CSLDestroy(papszHeader);
     235           0 :         return nullptr;
     236             :     }
     237             : 
     238             :     /* -------------------------------------------------------------------- */
     239             :     /*      Confirm the requested access is supported.                      */
     240             :     /* -------------------------------------------------------------------- */
     241           2 :     if (poOpenInfo->eAccess == GA_Update)
     242             :     {
     243           0 :         CSLDestroy(papszHeader);
     244           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     245             :                  "The NDF driver does not support update access to existing"
     246             :                  " datasets.\n");
     247           0 :         return nullptr;
     248             :     }
     249             : 
     250             :     /* -------------------------------------------------------------------- */
     251             :     /*      Create a corresponding GDALDataset.                             */
     252             :     /* -------------------------------------------------------------------- */
     253           4 :     auto poDS = std::make_unique<NDFDataset>();
     254           2 :     poDS->papszHeader = papszHeader;
     255             : 
     256           2 :     poDS->nRasterXSize = atoi(poDS->Get("PIXELS_PER_LINE", ""));
     257           2 :     poDS->nRasterYSize = atoi(poDS->Get("LINES_PER_DATA_FILE", ""));
     258             : 
     259             :     /* -------------------------------------------------------------------- */
     260             :     /*      Create a raw raster band for each file.                         */
     261             :     /* -------------------------------------------------------------------- */
     262             :     const char *pszBand =
     263           2 :         CSLFetchNameValue(papszHeader, "NUMBER_OF_BANDS_IN_VOLUME");
     264           2 :     if (pszBand == nullptr)
     265             :     {
     266           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find band count");
     267           0 :         return nullptr;
     268             :     }
     269           2 :     const int nBands = atoi(pszBand);
     270             : 
     271           4 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
     272           2 :         !GDALCheckBandCount(nBands, FALSE))
     273             :     {
     274           0 :         return nullptr;
     275             :     }
     276             : 
     277           4 :     for (int iBand = 0; iBand < nBands; iBand++)
     278             :     {
     279             :         char szKey[100];
     280           2 :         snprintf(szKey, sizeof(szKey), "BAND%d_FILENAME", iBand + 1);
     281           2 :         CPLString osFilename = poDS->Get(szKey, "");
     282             : 
     283             :         // NDF1 file do not include the band filenames.
     284           2 :         if (osFilename.empty())
     285             :         {
     286             :             char szBandExtension[15];
     287           0 :             snprintf(szBandExtension, sizeof(szBandExtension), "I%d",
     288             :                      iBand + 1);
     289             :             osFilename =
     290           0 :                 CPLResetExtension(poOpenInfo->pszFilename, szBandExtension);
     291             :         }
     292             :         else
     293             :         {
     294           2 :             CPLString osBasePath = CPLGetPath(poOpenInfo->pszFilename);
     295           2 :             osFilename = CPLFormFilename(osBasePath, osFilename, nullptr);
     296             :         }
     297             : 
     298           2 :         VSILFILE *fpRaw = VSIFOpenL(osFilename, "rb");
     299           2 :         if (fpRaw == nullptr)
     300             :         {
     301           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     302             :                      "Failed to open band file: %s", osFilename.c_str());
     303           0 :             return nullptr;
     304             :         }
     305           2 :         poDS->papszExtraFiles = CSLAddString(poDS->papszExtraFiles, osFilename);
     306             : 
     307             :         auto poBand = RawRasterBand::Create(
     308           2 :             poDS.get(), iBand + 1, fpRaw, 0, 1, poDS->nRasterXSize, GDT_Byte,
     309             :             RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN,
     310           2 :             RawRasterBand::OwnFP::YES);
     311           2 :         if (!poBand)
     312           0 :             return nullptr;
     313             : 
     314           2 :         snprintf(szKey, sizeof(szKey), "BAND%d_NAME", iBand + 1);
     315           2 :         poBand->SetDescription(poDS->Get(szKey, ""));
     316             : 
     317           2 :         snprintf(szKey, sizeof(szKey), "BAND%d_WAVELENGTHS", iBand + 1);
     318           2 :         poBand->SetMetadataItem("WAVELENGTHS", poDS->Get(szKey, ""));
     319             : 
     320           2 :         snprintf(szKey, sizeof(szKey), "BAND%d_RADIOMETRIC_GAINS/BIAS",
     321             :                  iBand + 1);
     322           2 :         poBand->SetMetadataItem("RADIOMETRIC_GAINS_BIAS", poDS->Get(szKey, ""));
     323             : 
     324           2 :         poDS->SetBand(iBand + 1, std::move(poBand));
     325             :     }
     326             : 
     327             :     /* -------------------------------------------------------------------- */
     328             :     /*      Fetch and parse USGS projection parameters.                     */
     329             :     /* -------------------------------------------------------------------- */
     330           2 :     double adfUSGSParams[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     331             :     const CPLStringList aosParamTokens(CSLTokenizeStringComplex(
     332           4 :         poDS->Get("USGS_PROJECTION_NUMBER", ""), ",", FALSE, TRUE));
     333             : 
     334           2 :     if (aosParamTokens.size() >= 15)
     335             :     {
     336           0 :         for (int i = 0; i < 15; i++)
     337           0 :             adfUSGSParams[i] = CPLAtof(aosParamTokens[i]);
     338             :     }
     339             : 
     340             :     /* -------------------------------------------------------------------- */
     341             :     /*      Minimal georef support ... should add full USGS style           */
     342             :     /*      support at some point.                                          */
     343             :     /* -------------------------------------------------------------------- */
     344           2 :     const int nUSGSProjection = atoi(poDS->Get("USGS_PROJECTION_NUMBER", ""));
     345           2 :     const int nZone = atoi(poDS->Get("USGS_MAP_ZONE", "0"));
     346             : 
     347           4 :     OGRSpatialReference oSRS;
     348           2 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     349           2 :     oSRS.importFromUSGS(nUSGSProjection, nZone, adfUSGSParams, 12);
     350             : 
     351           4 :     const CPLString osDatum = poDS->Get("HORIZONTAL_DATUM", "");
     352           2 :     if (EQUAL(osDatum, "WGS84") || EQUAL(osDatum, "NAD83") ||
     353           0 :         EQUAL(osDatum, "NAD27"))
     354             :     {
     355           2 :         oSRS.SetWellKnownGeogCS(osDatum);
     356             :     }
     357           0 :     else if (STARTS_WITH_CI(osDatum, "NAD27"))
     358             :     {
     359           0 :         oSRS.SetWellKnownGeogCS("NAD27");
     360             :     }
     361             :     else
     362             :     {
     363           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     364             :                  "Unrecognized datum name in NLAPS/NDF file:%s, "
     365             :                  "assuming WGS84.",
     366             :                  osDatum.c_str());
     367           0 :         oSRS.SetWellKnownGeogCS("WGS84");
     368             :     }
     369             : 
     370           2 :     if (oSRS.GetRoot() != nullptr)
     371             :     {
     372           2 :         poDS->m_oSRS = std::move(oSRS);
     373             :     }
     374             : 
     375             :     /* -------------------------------------------------------------------- */
     376             :     /*      Get geotransform.                                               */
     377             :     /* -------------------------------------------------------------------- */
     378             :     char **papszUL =
     379           2 :         CSLTokenizeString2(poDS->Get("UPPER_LEFT_CORNER", ""), ",", 0);
     380             :     char **papszUR =
     381           2 :         CSLTokenizeString2(poDS->Get("UPPER_RIGHT_CORNER", ""), ",", 0);
     382             :     char **papszLL =
     383           2 :         CSLTokenizeString2(poDS->Get("LOWER_LEFT_CORNER", ""), ",", 0);
     384             : 
     385           4 :     if (CSLCount(papszUL) == 4 && CSLCount(papszUR) == 4 &&
     386           2 :         CSLCount(papszLL) == 4)
     387             :     {
     388           2 :         poDS->adfGeoTransform[0] = CPLAtof(papszUL[2]);
     389           2 :         poDS->adfGeoTransform[1] = (CPLAtof(papszUR[2]) - CPLAtof(papszUL[2])) /
     390           2 :                                    (poDS->nRasterXSize - 1);
     391           2 :         poDS->adfGeoTransform[2] = (CPLAtof(papszUR[3]) - CPLAtof(papszUL[3])) /
     392           2 :                                    (poDS->nRasterXSize - 1);
     393             : 
     394           2 :         poDS->adfGeoTransform[3] = CPLAtof(papszUL[3]);
     395           2 :         poDS->adfGeoTransform[4] = (CPLAtof(papszLL[2]) - CPLAtof(papszUL[2])) /
     396           2 :                                    (poDS->nRasterYSize - 1);
     397           2 :         poDS->adfGeoTransform[5] = (CPLAtof(papszLL[3]) - CPLAtof(papszUL[3])) /
     398           2 :                                    (poDS->nRasterYSize - 1);
     399             : 
     400             :         // Move origin up-left half a pixel.
     401           2 :         poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5;
     402           2 :         poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[4] * 0.5;
     403           2 :         poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[2] * 0.5;
     404           2 :         poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5;
     405             :     }
     406             : 
     407           2 :     CSLDestroy(papszUL);
     408           2 :     CSLDestroy(papszLL);
     409           2 :     CSLDestroy(papszUR);
     410             : 
     411             :     /* -------------------------------------------------------------------- */
     412             :     /*      Initialize any PAM information.                                 */
     413             :     /* -------------------------------------------------------------------- */
     414           2 :     poDS->SetDescription(poOpenInfo->pszFilename);
     415           2 :     poDS->TryLoadXML();
     416             : 
     417             :     /* -------------------------------------------------------------------- */
     418             :     /*      Check for overviews.                                            */
     419             :     /* -------------------------------------------------------------------- */
     420           2 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     421             : 
     422           2 :     return poDS.release();
     423             : }
     424             : 
     425             : /************************************************************************/
     426             : /*                          GDALRegister_NDF()                          */
     427             : /************************************************************************/
     428             : 
     429        1595 : void GDALRegister_NDF()
     430             : 
     431             : {
     432        1595 :     if (GDALGetDriverByName("NDF") != nullptr)
     433         302 :         return;
     434             : 
     435        1293 :     GDALDriver *poDriver = new GDALDriver();
     436             : 
     437        1293 :     poDriver->SetDescription("NDF");
     438        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     439        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NLAPS Data Format");
     440        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ndf.html");
     441        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     442             : 
     443        1293 :     poDriver->pfnIdentify = NDFDataset::Identify;
     444        1293 :     poDriver->pfnOpen = NDFDataset::Open;
     445             : 
     446        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     447             : }

Generated by: LCOV version 1.14