LCOV - code coverage report
Current view: top level - frmts/raw - doq2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 155 198 78.3 %
Date: 2025-10-15 23:46:56 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  USGS DOQ Driver (Second Generation Format)
       4             :  * Purpose:  Implementation of DOQ2Dataset
       5             :  * Author:   Derrick J Brashear, shadow@dementia.org
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Derrick J Brashear
       9             :  * Copyright (c) 2009-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 "gdal_priv.h"
      17             : #include "rawdataset.h"
      18             : 
      19             : #ifndef UTM_FORMAT_defined
      20             : #define UTM_FORMAT_defined
      21             : 
      22             : static const char UTM_FORMAT[] =
      23             :     "PROJCS[\"%s / UTM zone %dN\",GEOGCS[%s,PRIMEM[\"Greenwich\",0],"
      24             :     "UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],"
      25             :     "PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%d],"
      26             :     "PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],"
      27             :     "PARAMETER[\"false_northing\",0],%s]";
      28             : 
      29             : static const char WGS84_DATUM[] =
      30             :     "\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]]";
      31             : 
      32             : static const char WGS72_DATUM[] =
      33             :     "\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"NWL 10D\",6378135,298.26]]";
      34             : 
      35             : static const char NAD27_DATUM[] =
      36             :     "\"NAD27\",DATUM[\"North_American_Datum_1927\","
      37             :     "SPHEROID[\"Clarke 1866\",6378206.4,294.978698213901]]";
      38             : 
      39             : static const char NAD83_DATUM[] =
      40             :     "\"NAD83\",DATUM[\"North_American_Datum_1983\","
      41             :     "SPHEROID[\"GRS 1980\",6378137,298.257222101]]";
      42             : 
      43             : #endif
      44             : 
      45             : /************************************************************************/
      46             : /* ==================================================================== */
      47             : /*                              DOQ2Dataset                             */
      48             : /* ==================================================================== */
      49             : /************************************************************************/
      50             : 
      51             : class DOQ2Dataset final : public RawDataset
      52             : {
      53             :     VSILFILE *fpImage = nullptr;  // Image data file.
      54             : 
      55             :     double dfULX = 0;
      56             :     double dfULY = 0;
      57             :     double dfXPixelSize = 0;
      58             :     double dfYPixelSize = 0;
      59             : 
      60             :     OGRSpatialReference m_oSRS{};
      61             : 
      62             :     CPL_DISALLOW_COPY_ASSIGN(DOQ2Dataset)
      63             : 
      64             :     CPLErr Close() override;
      65             : 
      66             :   public:
      67             :     DOQ2Dataset();
      68             :     ~DOQ2Dataset() override;
      69             : 
      70             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      71             : 
      72           0 :     const OGRSpatialReference *GetSpatialRef() const override
      73             :     {
      74           0 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
      75             :     }
      76             : 
      77             :     static GDALDataset *Open(GDALOpenInfo *);
      78             : };
      79             : 
      80             : /************************************************************************/
      81             : /*                            DOQ2Dataset()                             */
      82             : /************************************************************************/
      83             : 
      84           1 : DOQ2Dataset::DOQ2Dataset()
      85             : {
      86           1 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      87           1 : }
      88             : 
      89             : /************************************************************************/
      90             : /*                            ~DOQ2Dataset()                            */
      91             : /************************************************************************/
      92             : 
      93           2 : DOQ2Dataset::~DOQ2Dataset()
      94             : 
      95             : {
      96           1 :     DOQ2Dataset::Close();
      97           2 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                              Close()                                 */
     101             : /************************************************************************/
     102             : 
     103           2 : CPLErr DOQ2Dataset::Close()
     104             : {
     105           2 :     CPLErr eErr = CE_None;
     106           2 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     107             :     {
     108           1 :         if (DOQ2Dataset::FlushCache(true) != CE_None)
     109           0 :             eErr = CE_Failure;
     110             : 
     111           1 :         if (fpImage)
     112             :         {
     113           1 :             if (VSIFCloseL(fpImage) != 0)
     114             :             {
     115           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     116           0 :                 eErr = CE_Failure;
     117             :             }
     118             :         }
     119             : 
     120           1 :         if (GDALPamDataset::Close() != CE_None)
     121           0 :             eErr = CE_Failure;
     122             :     }
     123           2 :     return eErr;
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                          GetGeoTransform()                           */
     128             : /************************************************************************/
     129             : 
     130           1 : CPLErr DOQ2Dataset::GetGeoTransform(GDALGeoTransform &gt) const
     131             : 
     132             : {
     133           1 :     gt[0] = dfULX;
     134           1 :     gt[1] = dfXPixelSize;
     135           1 :     gt[2] = 0.0;
     136           1 :     gt[3] = dfULY;
     137           1 :     gt[4] = 0.0;
     138           1 :     gt[5] = -1 * dfYPixelSize;
     139             : 
     140           1 :     return CE_None;
     141             : }
     142             : 
     143             : /************************************************************************/
     144             : /*                                Open()                                */
     145             : /************************************************************************/
     146             : 
     147       34388 : GDALDataset *DOQ2Dataset::Open(GDALOpenInfo *poOpenInfo)
     148             : 
     149             : {
     150             :     /* -------------------------------------------------------------------- */
     151             :     /*      We assume the user is pointing to the binary (i.e. .bil) file.  */
     152             :     /* -------------------------------------------------------------------- */
     153       34388 :     if (poOpenInfo->nHeaderBytes < 212 || poOpenInfo->fpL == nullptr)
     154       31329 :         return nullptr;
     155             : 
     156        3059 :     if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     157             :                         "BEGIN_USGS_DOQ_HEADER"))
     158        3058 :         return nullptr;
     159             : 
     160             :     /* -------------------------------------------------------------------- */
     161             :     /*      Confirm the requested access is supported.                      */
     162             :     /* -------------------------------------------------------------------- */
     163           1 :     if (poOpenInfo->eAccess == GA_Update)
     164             :     {
     165           0 :         ReportUpdateNotSupportedByDriver("DOQ2");
     166           0 :         return nullptr;
     167             :     }
     168             : 
     169           1 :     int nBytesPerPixel = 0;
     170           1 :     int nWidth = 0;
     171           1 :     int nHeight = 0;
     172           1 :     int nBandStorage = 0;
     173           1 :     int nBandTypes = 0;
     174           1 :     const char *pszDatumLong = nullptr;
     175           1 :     const char *pszDatumShort = nullptr;
     176           1 :     const char *pszUnits = nullptr;
     177           1 :     int nZone = 0;
     178           1 :     int nProjType = 0;
     179           1 :     int nSkipBytes = 0;
     180           1 :     int nBandCount = 0;
     181           1 :     double dfULXMap = 0.0;
     182           1 :     double dfULYMap = 0.0;
     183           1 :     double dfXDim = 0.0;
     184           1 :     double dfYDim = 0.0;
     185           1 :     char **papszMetadata = nullptr;
     186             : 
     187             :     /* read and discard the first line */
     188           1 :     CPL_IGNORE_RET_VAL(CPLReadLineL(poOpenInfo->fpL));
     189             : 
     190           1 :     const char *pszLine = nullptr;
     191          46 :     while ((pszLine = CPLReadLineL(poOpenInfo->fpL)) != nullptr)
     192             :     {
     193          46 :         if (EQUAL(pszLine, "END_USGS_DOQ_HEADER"))
     194           0 :             break;
     195             : 
     196          46 :         char **papszTokens = CSLTokenizeString(pszLine);
     197          46 :         if (CSLCount(papszTokens) < 2)
     198             :         {
     199           1 :             CSLDestroy(papszTokens);
     200           1 :             break;
     201             :         }
     202             : 
     203          46 :         if (EQUAL(papszTokens[0], "SAMPLES_AND_LINES") &&
     204           1 :             CSLCount(papszTokens) >= 3)
     205             :         {
     206           1 :             nWidth = atoi(papszTokens[1]);
     207           1 :             nHeight = atoi(papszTokens[2]);
     208             :         }
     209          44 :         else if (EQUAL(papszTokens[0], "BYTE_COUNT"))
     210             :         {
     211           1 :             nSkipBytes = atoi(papszTokens[1]);
     212             :         }
     213          44 :         else if (EQUAL(papszTokens[0], "XY_ORIGIN") &&
     214           1 :                  CSLCount(papszTokens) >= 3)
     215             :         {
     216           1 :             dfULXMap = CPLAtof(papszTokens[1]);
     217           1 :             dfULYMap = CPLAtof(papszTokens[2]);
     218             :         }
     219          42 :         else if (EQUAL(papszTokens[0], "HORIZONTAL_RESOLUTION"))
     220             :         {
     221           1 :             dfXDim = CPLAtof(papszTokens[1]);
     222           1 :             dfYDim = dfXDim;
     223             :         }
     224          41 :         else if (EQUAL(papszTokens[0], "BAND_ORGANIZATION"))
     225             :         {
     226           1 :             if (EQUAL(papszTokens[1], "SINGLE FILE"))
     227           0 :                 nBandStorage = 1;
     228           1 :             if (EQUAL(papszTokens[1], "BSQ"))
     229           0 :                 nBandStorage = 1;
     230           1 :             if (EQUAL(papszTokens[1], "BIL"))
     231           0 :                 nBandStorage = 1;
     232           1 :             if (EQUAL(papszTokens[1], "BIP"))
     233           1 :                 nBandStorage = 4;
     234             :         }
     235          40 :         else if (EQUAL(papszTokens[0], "BAND_CONTENT"))
     236             :         {
     237           3 :             if (EQUAL(papszTokens[1], "BLACK&WHITE"))
     238           0 :                 nBandTypes = 1;
     239           3 :             else if (EQUAL(papszTokens[1], "COLOR"))
     240           0 :                 nBandTypes = 5;
     241           3 :             else if (EQUAL(papszTokens[1], "RGB"))
     242           0 :                 nBandTypes = 5;
     243           3 :             else if (EQUAL(papszTokens[1], "RED"))
     244           1 :                 nBandTypes = 5;
     245           2 :             else if (EQUAL(papszTokens[1], "GREEN"))
     246           1 :                 nBandTypes = 5;
     247           1 :             else if (EQUAL(papszTokens[1], "BLUE"))
     248           1 :                 nBandTypes = 5;
     249             : 
     250           3 :             nBandCount++;
     251             :         }
     252          37 :         else if (EQUAL(papszTokens[0], "BITS_PER_PIXEL"))
     253             :         {
     254           1 :             nBytesPerPixel = atoi(papszTokens[1]) / 8;
     255             :         }
     256          36 :         else if (EQUAL(papszTokens[0], "HORIZONTAL_COORDINATE_SYSTEM"))
     257             :         {
     258           1 :             if (EQUAL(papszTokens[1], "UTM"))
     259           1 :                 nProjType = 1;
     260           0 :             else if (EQUAL(papszTokens[1], "SPCS"))
     261           0 :                 nProjType = 2;
     262           0 :             else if (EQUAL(papszTokens[1], "GEOGRAPHIC"))
     263           0 :                 nProjType = 0;
     264             :         }
     265          35 :         else if (EQUAL(papszTokens[0], "COORDINATE_ZONE"))
     266             :         {
     267           1 :             nZone = atoi(papszTokens[1]);
     268             :         }
     269          34 :         else if (EQUAL(papszTokens[0], "HORIZONTAL_UNITS"))
     270             :         {
     271           1 :             if (EQUAL(papszTokens[1], "METERS"))
     272           1 :                 pszUnits = "UNIT[\"metre\",1]";
     273           0 :             else if (EQUAL(papszTokens[1], "FEET"))
     274           0 :                 pszUnits = "UNIT[\"US survey foot\",0.304800609601219]";
     275             :         }
     276          33 :         else if (EQUAL(papszTokens[0], "HORIZONTAL_DATUM"))
     277             :         {
     278           1 :             if (EQUAL(papszTokens[1], "NAD27"))
     279             :             {
     280           0 :                 pszDatumLong = NAD27_DATUM;
     281           0 :                 pszDatumShort = "NAD 27";
     282             :             }
     283           1 :             else if (EQUAL(papszTokens[1], " WGS72"))
     284             :             {
     285           0 :                 pszDatumLong = WGS72_DATUM;
     286           0 :                 pszDatumShort = "WGS 72";
     287             :             }
     288           1 :             else if (EQUAL(papszTokens[1], "WGS84"))
     289             :             {
     290           0 :                 pszDatumLong = WGS84_DATUM;
     291           0 :                 pszDatumShort = "WGS 84";
     292             :             }
     293           1 :             else if (EQUAL(papszTokens[1], "NAD83"))
     294             :             {
     295           1 :                 pszDatumLong = NAD83_DATUM;
     296           1 :                 pszDatumShort = "NAD 83";
     297             :             }
     298             :             else
     299             :             {
     300           0 :                 pszDatumLong = "DATUM[\"unknown\"]";
     301           0 :                 pszDatumShort = "unknown";
     302             :             }
     303             :         }
     304             :         else
     305             :         {
     306             :             /* we want to generically capture all the other metadata */
     307          32 :             CPLString osMetaDataValue;
     308             : 
     309         250 :             for (int iToken = 1; papszTokens[iToken] != nullptr; iToken++)
     310             :             {
     311         218 :                 if (EQUAL(papszTokens[iToken], "*"))
     312           1 :                     continue;
     313             : 
     314         217 :                 if (iToken > 1)
     315         186 :                     osMetaDataValue += " ";
     316         217 :                 osMetaDataValue += papszTokens[iToken];
     317             :             }
     318             :             papszMetadata =
     319          32 :                 CSLAddNameValue(papszMetadata, papszTokens[0], osMetaDataValue);
     320             :         }
     321             : 
     322          45 :         CSLDestroy(papszTokens);
     323             :     }
     324             : 
     325           1 :     CPLReadLineL(nullptr);
     326             : 
     327             :     /* -------------------------------------------------------------------- */
     328             :     /*      Do these values look coherent for a DOQ file?  It would be      */
     329             :     /*      nice to do a more comprehensive test than this!                 */
     330             :     /* -------------------------------------------------------------------- */
     331           1 :     if (nWidth < 500 || nWidth > 25000 || nHeight < 500 || nHeight > 25000 ||
     332           1 :         nBandStorage < 0 || nBandStorage > 4 || nBandTypes < 1 ||
     333           1 :         nBandTypes > 9 || nBytesPerPixel < 0)
     334             :     {
     335           0 :         CSLDestroy(papszMetadata);
     336           0 :         return nullptr;
     337             :     }
     338             : 
     339             :     /* -------------------------------------------------------------------- */
     340             :     /*      Check the configuration.  We don't currently handle all         */
     341             :     /*      variations, only the common ones.                               */
     342             :     /* -------------------------------------------------------------------- */
     343           1 :     if (nBandTypes > 5)
     344             :     {
     345           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     346             :                  "DOQ Data Type (%d) is not a supported configuration.",
     347             :                  nBandTypes);
     348           0 :         CSLDestroy(papszMetadata);
     349           0 :         return nullptr;
     350             :     }
     351             : 
     352             :     /* -------------------------------------------------------------------- */
     353             :     /*      Confirm the requested access is supported.                      */
     354             :     /* -------------------------------------------------------------------- */
     355           1 :     if (poOpenInfo->eAccess == GA_Update)
     356             :     {
     357           0 :         CSLDestroy(papszMetadata);
     358           0 :         ReportUpdateNotSupportedByDriver("DOQ2");
     359           0 :         return nullptr;
     360             :     }
     361             :     /* -------------------------------------------------------------------- */
     362             :     /*      Create a corresponding GDALDataset.                             */
     363             :     /* -------------------------------------------------------------------- */
     364           2 :     auto poDS = std::make_unique<DOQ2Dataset>();
     365             : 
     366           1 :     poDS->nRasterXSize = nWidth;
     367           1 :     poDS->nRasterYSize = nHeight;
     368             : 
     369           1 :     poDS->SetMetadata(papszMetadata);
     370           1 :     CSLDestroy(papszMetadata);
     371           1 :     papszMetadata = nullptr;
     372             : 
     373           1 :     poDS->fpImage = poOpenInfo->fpL;
     374           1 :     poOpenInfo->fpL = nullptr;
     375             : 
     376             :     /* -------------------------------------------------------------------- */
     377             :     /*      Compute layout of data.                                         */
     378             :     /* -------------------------------------------------------------------- */
     379           1 :     if (nBandCount < 2)
     380             :     {
     381           0 :         nBandCount = nBytesPerPixel;
     382           0 :         if (!GDALCheckBandCount(nBandCount, FALSE))
     383             :         {
     384           0 :             return nullptr;
     385             :         }
     386             :     }
     387             :     else
     388             :     {
     389           1 :         if (nBytesPerPixel > INT_MAX / nBandCount)
     390             :         {
     391           0 :             return nullptr;
     392             :         }
     393           1 :         nBytesPerPixel *= nBandCount;
     394             :     }
     395             : 
     396           1 :     if (nBytesPerPixel > INT_MAX / nWidth)
     397             :     {
     398           0 :         return nullptr;
     399             :     }
     400           1 :     const int nBytesPerLine = nBytesPerPixel * nWidth;
     401             : 
     402             :     /* -------------------------------------------------------------------- */
     403             :     /*      Create band information objects.                                */
     404             :     /* -------------------------------------------------------------------- */
     405           4 :     for (int i = 0; i < nBandCount; i++)
     406             :     {
     407             :         auto poBand = RawRasterBand::Create(
     408           6 :             poDS.get(), i + 1, poDS->fpImage, nSkipBytes + i, nBytesPerPixel,
     409             :             nBytesPerLine, GDT_Byte,
     410             :             RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN,
     411           3 :             RawRasterBand::OwnFP::NO);
     412           3 :         if (!poBand)
     413           0 :             return nullptr;
     414           3 :         poDS->SetBand(i + 1, std::move(poBand));
     415             :     }
     416             : 
     417           1 :     if (nProjType == 1)
     418             :     {
     419           2 :         poDS->m_oSRS.importFromWkt(
     420             :             CPLSPrintf(UTM_FORMAT, pszDatumShort ? pszDatumShort : "", nZone,
     421             :                        pszDatumLong ? pszDatumLong : "",
     422           1 :                        nZone >= 1 && nZone <= 60 ? nZone * 6 - 183 : 0,
     423             :                        pszUnits ? pszUnits : ""));
     424             :     }
     425             : 
     426           1 :     poDS->dfULX = dfULXMap;
     427           1 :     poDS->dfULY = dfULYMap;
     428             : 
     429           1 :     poDS->dfXPixelSize = dfXDim;
     430           1 :     poDS->dfYPixelSize = dfYDim;
     431             : 
     432             :     /* -------------------------------------------------------------------- */
     433             :     /*      Initialize any PAM information.                                 */
     434             :     /* -------------------------------------------------------------------- */
     435           1 :     poDS->SetDescription(poOpenInfo->pszFilename);
     436           1 :     poDS->TryLoadXML();
     437             : 
     438             :     /* -------------------------------------------------------------------- */
     439             :     /*      Check for overviews.                                            */
     440             :     /* -------------------------------------------------------------------- */
     441           1 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     442             : 
     443           1 :     return poDS.release();
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                         GDALRegister_DOQ1()                          */
     448             : /************************************************************************/
     449             : 
     450        2033 : void GDALRegister_DOQ2()
     451             : 
     452             : {
     453        2033 :     if (GDALGetDriverByName("DOQ2") != nullptr)
     454         283 :         return;
     455             : 
     456        1750 :     GDALDriver *poDriver = new GDALDriver();
     457             : 
     458        1750 :     poDriver->SetDescription("DOQ2");
     459        1750 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     460        1750 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "USGS DOQ (New Style)");
     461        1750 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/doq2.html");
     462        1750 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     463             : 
     464        1750 :     poDriver->pfnOpen = DOQ2Dataset::Open;
     465             : 
     466        1750 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     467             : }

Generated by: LCOV version 1.14