LCOV - code coverage report
Current view: top level - frmts/raw - doq1dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 106 149 71.1 %
Date: 2025-10-21 22:35:35 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  USGS DOQ Driver (First Generation Format)
       4             :  * Purpose:  Implementation of DOQ1Dataset
       5             :  * Author:   Frank Warmerdam, warmerda@home.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_frmts.h"
      15             : #include "cpl_string.h"
      16             : #include "gdal_priv.h"
      17             : #include "rawdataset.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <cmath>
      21             : 
      22             : #ifndef UTM_FORMAT_defined
      23             : #define UTM_FORMAT_defined
      24             : 
      25             : static const char UTM_FORMAT[] =
      26             :     "PROJCS[\"%s / UTM zone %dN\",GEOGCS[%s,PRIMEM[\"Greenwich\",0],"
      27             :     "UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],"
      28             :     "PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%d],"
      29             :     "PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],"
      30             :     "PARAMETER[\"false_northing\",0],%s]";
      31             : 
      32             : static const char WGS84_DATUM[] =
      33             :     "\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]]";
      34             : 
      35             : static const char WGS72_DATUM[] =
      36             :     "\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"NWL 10D\",6378135,298.26]]";
      37             : 
      38             : static const char NAD27_DATUM[] =
      39             :     "\"NAD27\",DATUM[\"North_American_Datum_1927\","
      40             :     "SPHEROID[\"Clarke 1866\",6378206.4,294.978698213901]]";
      41             : 
      42             : static const char NAD83_DATUM[] =
      43             :     "\"NAD83\",DATUM[\"North_American_Datum_1983\","
      44             :     "SPHEROID[\"GRS 1980\",6378137,298.257222101]]";
      45             : 
      46             : #endif
      47             : 
      48             : /************************************************************************/
      49             : /*                            DOQGetField()                             */
      50             : /************************************************************************/
      51             : 
      52       12268 : static double DOQGetField(unsigned char *pabyData, int nBytes)
      53             : 
      54             : {
      55       12268 :     char szWork[128] = {'\0'};
      56             : 
      57       12268 :     memcpy(szWork, reinterpret_cast<const char *>(pabyData), nBytes);
      58       12268 :     szWork[nBytes] = '\0';
      59             : 
      60       67574 :     for (int i = 0; i < nBytes; i++)
      61             :     {
      62       55306 :         if (szWork[i] == 'D' || szWork[i] == 'd')
      63         430 :             szWork[i] = 'E';
      64             :     }
      65             : 
      66       24536 :     return CPLAtof(szWork);
      67             : }
      68             : 
      69             : /************************************************************************/
      70             : /*                         DOQGetDescription()                          */
      71             : /************************************************************************/
      72             : 
      73           2 : static void DOQGetDescription(GDALDataset *poDS, unsigned char *pabyData)
      74             : 
      75             : {
      76           2 :     char szWork[128] = {' '};
      77             : 
      78           2 :     const char *pszDescBegin = "USGS GeoTIFF DOQ 1:12000 Q-Quad of ";
      79           2 :     memcpy(szWork, pszDescBegin, strlen(pszDescBegin));
      80           2 :     memcpy(szWork + strlen(pszDescBegin),
      81             :            reinterpret_cast<const char *>(pabyData + 0), 38);
      82             : 
      83           2 :     int i = 0;
      84           2 :     while (*(szWork + 72 - i) == ' ')
      85             :     {
      86           0 :         i++;
      87             :     }
      88           2 :     i--;
      89             : 
      90           2 :     memcpy(szWork + 73 - i, reinterpret_cast<const char *>(pabyData + 38), 2);
      91           2 :     memcpy(szWork + 76 - i, reinterpret_cast<const char *>(pabyData + 44), 2);
      92           2 :     szWork[77 - i] = '\0';
      93             : 
      94           2 :     poDS->SetMetadataItem("DOQ_DESC", szWork);
      95           2 : }
      96             : 
      97             : /************************************************************************/
      98             : /* ==================================================================== */
      99             : /*                              DOQ1Dataset                             */
     100             : /* ==================================================================== */
     101             : /************************************************************************/
     102             : 
     103             : class DOQ1Dataset final : public RawDataset
     104             : {
     105             :     VSILFILE *fpImage = nullptr;  // Image data file.
     106             : 
     107             :     double dfULX = 0;
     108             :     double dfULY = 0;
     109             :     double dfXPixelSize = 0;
     110             :     double dfYPixelSize = 0;
     111             : 
     112             :     OGRSpatialReference m_oSRS{};
     113             : 
     114             :     CPL_DISALLOW_COPY_ASSIGN(DOQ1Dataset)
     115             : 
     116             :     CPLErr Close() override;
     117             : 
     118             :   public:
     119             :     DOQ1Dataset();
     120             :     ~DOQ1Dataset() override;
     121             : 
     122             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     123             : 
     124           0 :     const OGRSpatialReference *GetSpatialRef() const override
     125             :     {
     126           0 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     127             :     }
     128             : 
     129             :     static GDALDataset *Open(GDALOpenInfo *);
     130             : };
     131             : 
     132             : /************************************************************************/
     133             : /*                            DOQ1Dataset()                             */
     134             : /************************************************************************/
     135             : 
     136           2 : DOQ1Dataset::DOQ1Dataset()
     137             : {
     138           2 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     139           2 : }
     140             : 
     141             : /************************************************************************/
     142             : /*                            ~DOQ1Dataset()                            */
     143             : /************************************************************************/
     144             : 
     145           4 : DOQ1Dataset::~DOQ1Dataset()
     146             : 
     147             : {
     148           2 :     DOQ1Dataset::Close();
     149           4 : }
     150             : 
     151             : /************************************************************************/
     152             : /*                              Close()                                 */
     153             : /************************************************************************/
     154             : 
     155           4 : CPLErr DOQ1Dataset::Close()
     156             : {
     157           4 :     CPLErr eErr = CE_None;
     158           4 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     159             :     {
     160           2 :         if (DOQ1Dataset::FlushCache(true) != CE_None)
     161           0 :             eErr = CE_Failure;
     162             : 
     163           2 :         if (fpImage)
     164             :         {
     165           2 :             if (VSIFCloseL(fpImage) != 0)
     166             :             {
     167           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     168           0 :                 eErr = CE_Failure;
     169             :             }
     170             :         }
     171             : 
     172           2 :         if (GDALPamDataset::Close() != CE_None)
     173           0 :             eErr = CE_Failure;
     174             :     }
     175           4 :     return eErr;
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                          GetGeoTransform()                           */
     180             : /************************************************************************/
     181             : 
     182           0 : CPLErr DOQ1Dataset::GetGeoTransform(GDALGeoTransform &gt) const
     183             : 
     184             : {
     185           0 :     gt[0] = dfULX;
     186           0 :     gt[1] = dfXPixelSize;
     187           0 :     gt[2] = 0.0;
     188           0 :     gt[3] = dfULY;
     189           0 :     gt[4] = 0.0;
     190           0 :     gt[5] = -1 * dfYPixelSize;
     191             : 
     192           0 :     return CE_None;
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                                Open()                                */
     197             : /************************************************************************/
     198             : 
     199       34432 : GDALDataset *DOQ1Dataset::Open(GDALOpenInfo *poOpenInfo)
     200             : 
     201             : {
     202             :     /* -------------------------------------------------------------------- */
     203             :     /*      We assume the user is pointing to the binary (i.e. .bil) file.  */
     204             :     /* -------------------------------------------------------------------- */
     205       34432 :     if (poOpenInfo->nHeaderBytes < 212 || poOpenInfo->fpL == nullptr)
     206       31369 :         return nullptr;
     207             : 
     208             :     /* -------------------------------------------------------------------- */
     209             :     /*      Attempt to extract a few key values from the header.            */
     210             :     /* -------------------------------------------------------------------- */
     211        3063 :     const double dfWidth = DOQGetField(poOpenInfo->pabyHeader + 150, 6);
     212        3063 :     const double dfHeight = DOQGetField(poOpenInfo->pabyHeader + 144, 6);
     213        3063 :     const double dfBandStorage = DOQGetField(poOpenInfo->pabyHeader + 162, 3);
     214        3063 :     const double dfBandTypes = DOQGetField(poOpenInfo->pabyHeader + 156, 3);
     215             : 
     216             :     /* -------------------------------------------------------------------- */
     217             :     /*      Do these values look coherent for a DOQ file?  It would be      */
     218             :     /*      nice to do a more comprehensive test than this!                 */
     219             :     /* -------------------------------------------------------------------- */
     220          57 :     if (dfWidth < 500 || dfWidth > 25000 || std::isnan(dfWidth) ||
     221           2 :         dfHeight < 500 || dfHeight > 25000 || std::isnan(dfHeight) ||
     222           2 :         dfBandStorage < 0 || dfBandStorage > 4 || std::isnan(dfBandStorage) ||
     223        3120 :         dfBandTypes < 1 || dfBandTypes > 9 || std::isnan(dfBandTypes))
     224        3061 :         return nullptr;
     225             : 
     226           2 :     const int nWidth = static_cast<int>(dfWidth);
     227           2 :     const int nHeight = static_cast<int>(dfHeight);
     228             :     /*const int nBandStorage = static_cast<int>(dfBandStorage);*/
     229           2 :     const int nBandTypes = static_cast<int>(dfBandTypes);
     230             : 
     231             :     /* -------------------------------------------------------------------- */
     232             :     /*      Check the configuration.  We don't currently handle all         */
     233             :     /*      variations, only the common ones.                               */
     234             :     /* -------------------------------------------------------------------- */
     235           2 :     if (nBandTypes > 5)
     236             :     {
     237           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     238             :                  "DOQ Data Type (%d) is not a supported configuration.",
     239             :                  nBandTypes);
     240           0 :         return nullptr;
     241             :     }
     242             : 
     243             :     /* -------------------------------------------------------------------- */
     244             :     /*      Confirm the requested access is supported.                      */
     245             :     /* -------------------------------------------------------------------- */
     246           2 :     if (poOpenInfo->eAccess == GA_Update)
     247             :     {
     248           0 :         ReportUpdateNotSupportedByDriver("DOQ1");
     249           0 :         return nullptr;
     250             :     }
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Create a corresponding GDALDataset.                             */
     254             :     /* -------------------------------------------------------------------- */
     255           4 :     auto poDS = std::make_unique<DOQ1Dataset>();
     256             : 
     257             :     /* -------------------------------------------------------------------- */
     258             :     /*      Capture some information from the file that is of interest.     */
     259             :     /* -------------------------------------------------------------------- */
     260           2 :     poDS->nRasterXSize = nWidth;
     261           2 :     poDS->nRasterYSize = nHeight;
     262             : 
     263           2 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
     264             : 
     265             :     /* -------------------------------------------------------------------- */
     266             :     /*      Compute layout of data.                                         */
     267             :     /* -------------------------------------------------------------------- */
     268           2 :     int nBytesPerPixel = 0;
     269             : 
     270           2 :     if (nBandTypes < 5)
     271           2 :         nBytesPerPixel = 1;
     272             :     else /* if( nBandTypes == 5 ) */
     273           0 :         nBytesPerPixel = 3;
     274             : 
     275           2 :     const int nBytesPerLine = nBytesPerPixel * nWidth;
     276           2 :     const int nSkipBytes = 4 * nBytesPerLine;
     277             : 
     278             :     /* -------------------------------------------------------------------- */
     279             :     /*      Create band information objects.                                */
     280             :     /* -------------------------------------------------------------------- */
     281           4 :     for (int i = 0; i < nBytesPerPixel; i++)
     282             :     {
     283             :         auto poBand = RawRasterBand::Create(
     284           4 :             poDS.get(), i + 1, poDS->fpImage, nSkipBytes + i, nBytesPerPixel,
     285             :             nBytesPerLine, GDT_Byte,
     286             :             RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN,
     287           2 :             RawRasterBand::OwnFP::NO);
     288           2 :         if (!poBand)
     289           0 :             return nullptr;
     290           2 :         poDS->SetBand(i + 1, std::move(poBand));
     291             :     }
     292             : 
     293             :     /* -------------------------------------------------------------------- */
     294             :     /*      Set the description.                                            */
     295             :     /* -------------------------------------------------------------------- */
     296           2 :     DOQGetDescription(poDS.get(), poOpenInfo->pabyHeader);
     297             : 
     298             :     /* -------------------------------------------------------------------- */
     299             :     /*      Establish the projection string.                                */
     300             :     /* -------------------------------------------------------------------- */
     301           2 :     if (static_cast<int>(DOQGetField(poOpenInfo->pabyHeader + 195, 3)) == 1)
     302             :     {
     303             :         int nZone =
     304           2 :             static_cast<int>(DOQGetField(poOpenInfo->pabyHeader + 198, 6));
     305           2 :         if (nZone < 0 || nZone > 60)
     306           0 :             nZone = 0;
     307             : 
     308           2 :         const char *pszUnits = nullptr;
     309           2 :         if (static_cast<int>(DOQGetField(poOpenInfo->pabyHeader + 204, 3)) == 1)
     310           0 :             pszUnits = "UNIT[\"US survey foot\",0.304800609601219]";
     311             :         else
     312           2 :             pszUnits = "UNIT[\"metre\",1]";
     313             : 
     314           2 :         const char *pszDatumLong = nullptr;
     315           2 :         const char *pszDatumShort = nullptr;
     316           2 :         switch (static_cast<int>(DOQGetField(poOpenInfo->pabyHeader + 167, 2)))
     317             :         {
     318           0 :             case 1:
     319           0 :                 pszDatumLong = NAD27_DATUM;
     320           0 :                 pszDatumShort = "NAD 27";
     321           0 :                 break;
     322             : 
     323           0 :             case 2:
     324           0 :                 pszDatumLong = WGS72_DATUM;
     325           0 :                 pszDatumShort = "WGS 72";
     326           0 :                 break;
     327             : 
     328           2 :             case 3:
     329           2 :                 pszDatumLong = WGS84_DATUM;
     330           2 :                 pszDatumShort = "WGS 84";
     331           2 :                 break;
     332             : 
     333           0 :             case 4:
     334           0 :                 pszDatumLong = NAD83_DATUM;
     335           0 :                 pszDatumShort = "NAD 83";
     336           0 :                 break;
     337             : 
     338           0 :             default:
     339           0 :                 pszDatumLong = "DATUM[\"unknown\"]";
     340           0 :                 pszDatumShort = "unknown";
     341           0 :                 break;
     342             :         }
     343             : 
     344           4 :         poDS->m_oSRS.importFromWkt(CPLSPrintf(UTM_FORMAT, pszDatumShort, nZone,
     345           2 :                                               pszDatumLong, nZone * 6 - 183,
     346             :                                               pszUnits));
     347             :     }
     348             : 
     349             :     /* -------------------------------------------------------------------- */
     350             :     /*      Read the georeferencing information.                            */
     351             :     /* -------------------------------------------------------------------- */
     352           2 :     unsigned char abyRecordData[500] = {'\0'};
     353             : 
     354           4 :     if (VSIFSeekL(poDS->fpImage, nBytesPerLine * 2, SEEK_SET) != 0 ||
     355           2 :         VSIFReadL(abyRecordData, sizeof(abyRecordData), 1, poDS->fpImage) != 1)
     356             :     {
     357           0 :         CPLError(CE_Failure, CPLE_FileIO, "Header read error on %s.",
     358             :                  poOpenInfo->pszFilename);
     359           0 :         return nullptr;
     360             :     }
     361             : 
     362           2 :     poDS->dfULX = DOQGetField(abyRecordData + 288, 24);
     363           2 :     poDS->dfULY = DOQGetField(abyRecordData + 312, 24);
     364             : 
     365           4 :     if (VSIFSeekL(poDS->fpImage, nBytesPerLine * 3, SEEK_SET) != 0 ||
     366           2 :         VSIFReadL(abyRecordData, sizeof(abyRecordData), 1, poDS->fpImage) != 1)
     367             :     {
     368           0 :         CPLError(CE_Failure, CPLE_FileIO, "Header read error on %s.",
     369             :                  poOpenInfo->pszFilename);
     370           0 :         return nullptr;
     371             :     }
     372             : 
     373           2 :     poDS->dfXPixelSize = DOQGetField(abyRecordData + 59, 12);
     374           2 :     poDS->dfYPixelSize = DOQGetField(abyRecordData + 71, 12);
     375             : 
     376             :     /* -------------------------------------------------------------------- */
     377             :     /*      Initialize any PAM information.                                 */
     378             :     /* -------------------------------------------------------------------- */
     379           2 :     poDS->SetDescription(poOpenInfo->pszFilename);
     380           2 :     poDS->TryLoadXML();
     381             : 
     382             :     /* -------------------------------------------------------------------- */
     383             :     /*      Check for overviews.                                            */
     384             :     /* -------------------------------------------------------------------- */
     385           2 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     386             : 
     387           2 :     return poDS.release();
     388             : }
     389             : 
     390             : /************************************************************************/
     391             : /*                         GDALRegister_DOQ1()                          */
     392             : /************************************************************************/
     393             : 
     394        2038 : void GDALRegister_DOQ1()
     395             : 
     396             : {
     397        2038 :     if (GDALGetDriverByName("DOQ1") != nullptr)
     398         283 :         return;
     399             : 
     400        1755 :     GDALDriver *poDriver = new GDALDriver();
     401             : 
     402        1755 :     poDriver->SetDescription("DOQ1");
     403        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     404        1755 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "USGS DOQ (Old Style)");
     405        1755 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/doq1.html");
     406        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     407             : 
     408        1755 :     poDriver->pfnOpen = DOQ1Dataset::Open;
     409             : 
     410        1755 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     411             : }

Generated by: LCOV version 1.14