LCOV - code coverage report
Current view: top level - frmts/pds - pdsdrivercore.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 198 201 98.5 %
Date: 2026-06-27 16:33:51 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Planetary drivers
       5             :  * Author:   Even Rouault
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_frmts.h"
      14             : #include "gdalplugindriverproxy.h"
      15             : 
      16             : #include "pdsdrivercore.h"
      17             : 
      18             : #include "nasakeywordhandler.h"
      19             : 
      20             : /************************************************************************/
      21             : /*                    GetVICARLabelOffsetFromPDS3()                     */
      22             : /************************************************************************/
      23             : 
      24           5 : vsi_l_offset GetVICARLabelOffsetFromPDS3(const char *pszHdr, VSILFILE *fp,
      25             :                                          std::string &osVICARHeader)
      26             : {
      27           5 :     const char *pszPDSVersionID = strstr(pszHdr, "PDS_VERSION_ID");
      28           5 :     int nOffset = 0;
      29           5 :     if (pszPDSVersionID)
      30           5 :         nOffset = static_cast<int>(pszPDSVersionID - pszHdr);
      31             : 
      32          10 :     NASAKeywordHandler oKeywords;
      33           5 :     if (oKeywords.Ingest(fp, nOffset))
      34             :     {
      35             :         const int nRecordBytes =
      36           5 :             atoi(oKeywords.GetKeyword("RECORD_BYTES", "0"));
      37             :         const int nImageHeader =
      38           5 :             atoi(oKeywords.GetKeyword("^IMAGE_HEADER", "0"));
      39           5 :         if (nRecordBytes > 0 && nImageHeader > 0)
      40             :         {
      41           5 :             const auto nImgHeaderOffset =
      42           5 :                 static_cast<vsi_l_offset>(nImageHeader - 1) * nRecordBytes;
      43           5 :             osVICARHeader.resize(1024);
      44             :             size_t nMemb;
      45           5 :             if (VSIFSeekL(fp, nImgHeaderOffset, SEEK_SET) == 0 &&
      46           5 :                 (nMemb = VSIFReadL(&osVICARHeader[0], 1, osVICARHeader.size(),
      47          10 :                                    fp)) != 0 &&
      48           5 :                 osVICARHeader.find("LBLSIZE") != std::string::npos)
      49             :             {
      50           5 :                 osVICARHeader.resize(nMemb);
      51           5 :                 return nImgHeaderOffset;
      52             :             }
      53             :         }
      54             :     }
      55           0 :     return 0;
      56             : }
      57             : 
      58             : /************************************************************************/
      59             : /*                         PDSDriverIdentify()                          */
      60             : /************************************************************************/
      61             : 
      62       65561 : int PDSDriverIdentify(GDALOpenInfo *poOpenInfo)
      63             : 
      64             : {
      65       65561 :     if (poOpenInfo->pabyHeader == nullptr || poOpenInfo->fpL == nullptr)
      66       58405 :         return FALSE;
      67             : 
      68        7156 :     const char *pszHdr = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
      69        7156 :     if (strstr(pszHdr, "PDS_VERSION_ID") == nullptr &&
      70        7077 :         strstr(pszHdr, "ODL_VERSION_ID") == nullptr)
      71             :     {
      72        7077 :         return FALSE;
      73             :     }
      74             : 
      75             :     // Some PDS3 images include a VICAR header pointed by ^IMAGE_HEADER.
      76             :     // If the user sets GDAL_TRY_PDS3_WITH_VICAR=YES, then we will gracefully
      77             :     // hand over the file to the VICAR dataset.
      78         158 :     std::string unused;
      79          79 :     if (CPLTestBool(CPLGetConfigOption("GDAL_TRY_PDS3_WITH_VICAR", "NO")) &&
      80          80 :         !STARTS_WITH(poOpenInfo->pszFilename, "/vsisubfile/") &&
      81           1 :         GetVICARLabelOffsetFromPDS3(pszHdr, poOpenInfo->fpL, unused) > 0)
      82             :     {
      83           1 :         CPLDebug("PDS3", "File is detected to have a VICAR header. "
      84             :                          "Handing it over to the VICAR driver");
      85           1 :         return FALSE;
      86             :     }
      87             : 
      88          78 :     return TRUE;
      89             : }
      90             : 
      91             : /************************************************************************/
      92             : /*                     PDSDriverSetCommonMetadata()                     */
      93             : /************************************************************************/
      94             : 
      95        1873 : void PDSDriverSetCommonMetadata(GDALDriver *poDriver)
      96             : {
      97        1873 :     poDriver->SetDescription(PDS_DRIVER_NAME);
      98        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
      99        1873 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NASA Planetary Data System");
     100        1873 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/pds.html");
     101        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     102             : 
     103        1873 :     poDriver->pfnIdentify = PDSDriverIdentify;
     104        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     105        1873 : }
     106             : 
     107             : /************************************************************************/
     108             : /*                         PDS4DriverIdentify()                         */
     109             : /************************************************************************/
     110             : 
     111       77325 : int PDS4DriverIdentify(GDALOpenInfo *poOpenInfo)
     112             : {
     113       77325 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "PDS4:"))
     114          30 :         return TRUE;
     115       77295 :     if (poOpenInfo->nHeaderBytes == 0)
     116       61168 :         return FALSE;
     117             : 
     118       16127 :     const auto HasProductSomethingRootElement = [](const char *pszStr)
     119             :     {
     120       31768 :         return strstr(pszStr, "Product_Observational") != nullptr ||
     121       15641 :                strstr(pszStr, "Product_Ancillary") != nullptr ||
     122       47405 :                strstr(pszStr, "Product_Browse") != nullptr ||
     123       31764 :                strstr(pszStr, "Product_Collection") != nullptr;
     124             :     };
     125       16127 :     const auto HasPDS4Schema = [](const char *pszStr)
     126       16127 :     { return strstr(pszStr, "://pds.nasa.gov/pds4/pds/v1") != nullptr; };
     127             : 
     128       16127 :     for (int i = 0; i < 2; ++i)
     129             :     {
     130       16127 :         const char *pszHeader =
     131             :             reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     132       16127 :         int nMatches = 0;
     133       16127 :         if (HasProductSomethingRootElement(pszHeader))
     134         492 :             nMatches++;
     135       16127 :         if (HasPDS4Schema(pszHeader))
     136         492 :             nMatches++;
     137       16127 :         if (nMatches == 2)
     138             :         {
     139         492 :             return TRUE;
     140             :         }
     141       15635 :         if (i == 0)
     142             :         {
     143       15635 :             if (nMatches == 0 || poOpenInfo->nHeaderBytes >= 8192)
     144             :                 break;
     145             :             // If we have found one of the 2 matching elements to identify
     146             :             // PDS4 products, but have only ingested the default 1024 bytes,
     147             :             // then try to ingest more.
     148           0 :             poOpenInfo->TryToIngest(8192);
     149             :         }
     150             :     }
     151       15635 :     return FALSE;
     152             : }
     153             : 
     154             : /************************************************************************/
     155             : /*                    PDS4DriverSetCommonMetadata()                     */
     156             : /************************************************************************/
     157             : 
     158        1873 : void PDS4DriverSetCommonMetadata(GDALDriver *poDriver)
     159             : {
     160        1873 :     poDriver->SetDescription(PDS4_DRIVER_NAME);
     161        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     162        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     163        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     164        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     165        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
     166        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
     167        1873 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
     168        1873 :                               "Name Type WidthPrecision");
     169        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     170             : 
     171        1873 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     172        1873 :                               "NASA Planetary Data System 4");
     173        1873 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/pds4.html");
     174        1873 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "xml");
     175        1873 :     poDriver->SetMetadataItem(
     176             :         GDAL_DMD_CREATIONDATATYPES,
     177             :         "Byte Int8 UInt16 Int16 UInt32 Int32 UInt64 Int64 "
     178        1873 :         "Float32 Float64 CFloat32 CFloat64");
     179        1873 :     poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, "<OpenOptionList/>");
     180        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     181        1873 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
     182        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES");
     183        1873 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
     184             : 
     185        1873 :     poDriver->SetMetadataItem(
     186             :         GDAL_DMD_OPENOPTIONLIST,
     187             :         "<OpenOptionList>"
     188             :         "  <Option name='LAT' type='string' scope='vector' description="
     189             :         "'Name of a field containing a Latitude value' default='Latitude'/>"
     190             :         "  <Option name='LONG' type='string' scope='vector' description="
     191             :         "'Name of a field containing a Longitude value' default='Longitude'/>"
     192             :         "  <Option name='ALT' type='string' scope='vector' description="
     193             :         "'Name of a field containing a Altitude value' default='Altitude'/>"
     194             :         "  <Option name='WKT' type='string' scope='vector' description="
     195             :         "'Name of a field containing a geometry encoded in the WKT format' "
     196             :         "default='WKT'/>"
     197             :         "  <Option name='KEEP_GEOM_COLUMNS' scope='vector' type='boolean' "
     198             :         "description="
     199             :         "'whether to add original x/y/geometry columns as regular fields.' "
     200             :         "default='NO' />"
     201        1873 :         "</OpenOptionList>");
     202             : 
     203        1873 :     poDriver->SetMetadataItem(
     204             :         GDAL_DMD_CREATIONOPTIONLIST,
     205             :         "<CreationOptionList>"
     206             :         "  <Option name='IMAGE_FILENAME' type='string' scope='raster' "
     207             :         "description="
     208             :         "'Image filename'/>"
     209             :         "  <Option name='IMAGE_EXTENSION' type='string' scope='raster' "
     210             :         "description="
     211             :         "'Extension of the binary raw/geotiff file'/>"
     212             :         "  <Option name='CREATE_LABEL_ONLY' scope='raster' type='boolean' "
     213             :         "description="
     214             :         "'whether to create only the XML label when converting from an "
     215             :         "existing raw format.' default='NO' />"
     216             :         "  <Option name='IMAGE_FORMAT' type='string-select' scope='raster' "
     217             :         "description='Format of the image file' default='RAW'>"
     218             :         "     <Value>RAW</Value>"
     219             :         "     <Value>GEOTIFF</Value>"
     220             :         "  </Option>"
     221             : #ifdef notdef
     222             :         "  <Option name='GEOTIFF_OPTIONS' type='string' scope='raster' "
     223             :         "description='Comma separated list of KEY=VALUE tuples to forward "
     224             :         "to the GeoTIFF driver'/>"
     225             : #endif
     226             :         "  <Option name='INTERLEAVE' type='string-select' scope='raster' "
     227             :         "description="
     228             :         "'Pixel organization' default='BSQ'>"
     229             :         "     <Value>BSQ</Value>"
     230             :         "     <Value>BIP</Value>"
     231             :         "     <Value>BIL</Value>"
     232             :         "  </Option>"
     233             :         "  <Option name='VAR_*' type='string' scope='raster,vector' "
     234             :         "description="
     235             :         "'Value to substitute to a variable in the template'/>"
     236             :         "  <Option name='TEMPLATE' type='string' scope='raster,vector' "
     237             :         "description="
     238             :         "'.xml template to use'/>"
     239             :         "  <Option name='USE_SRC_LABEL' type='boolean' scope='raster' "
     240             :         "description='Whether to use source label in PDS4 to PDS4 conversions' "
     241             :         "default='YES'/>"
     242             :         "  <Option name='LATITUDE_TYPE' type='string-select' "
     243             :         "scope='raster,vector' "
     244             :         "description='Value of latitude_type' default='Planetocentric'>"
     245             :         "     <Value>Planetocentric</Value>"
     246             :         "     <Value>Planetographic</Value>"
     247             :         "  </Option>"
     248             :         "  <Option name='LONGITUDE_DIRECTION' type='string-select' "
     249             :         "scope='raster,vector' "
     250             :         "description='Value of longitude_direction' "
     251             :         "default='Positive East'>"
     252             :         "     <Value>Positive East</Value>"
     253             :         "     <Value>Positive West</Value>"
     254             :         "  </Option>"
     255             :         "  <Option name='RADII' type='string' scope='raster,vector' "
     256             :         "description='Value of form "
     257             :         "semi_major_radius,semi_minor_radius to override the ones of the SRS'/>"
     258             :         "  <Option name='ARRAY_TYPE' type='string-select' scope='raster' "
     259             :         "description='Name of the "
     260             :         "Array XML element' default='Array_3D_Image'>"
     261             :         "     <Value>Array</Value>"
     262             :         "     <Value>Array_2D</Value>"
     263             :         "     <Value>Array_2D_Image</Value>"
     264             :         "     <Value>Array_2D_Map</Value>"
     265             :         "     <Value>Array_2D_Spectrum</Value>"
     266             :         "     <Value>Array_3D</Value>"
     267             :         "     <Value>Array_3D_Image</Value>"
     268             :         "     <Value>Array_3D_Movie</Value>"
     269             :         "     <Value>Array_3D_Spectrum</Value>"
     270             :         "  </Option>"
     271             :         "  <Option name='ARRAY_IDENTIFIER' type='string' scope='raster' "
     272             :         "description='Identifier to put in the Array element'/>"
     273             :         "  <Option name='UNIT' type='string' scope='raster' "
     274             :         "description='Name of the unit of the array elements'/>"
     275             :         "  <Option name='BOUNDING_DEGREES' type='string' scope='raster,vector' "
     276             :         "description='Manually set bounding box with the syntax "
     277             :         "west_lon,south_lat,east_lon,north_lat'/>"
     278             :         "  <Option name='PROPAGATE_SRC_METADATA' type='boolean' scope='raster' "
     279             :         "description='Whether to propagate particular metadata domains, such "
     280             :         "as json:ISIS3' default='YES'/>"
     281        1873 :         "</CreationOptionList>");
     282             : 
     283        1873 :     poDriver->SetMetadataItem(
     284             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
     285             :         "<LayerCreationOptionList>"
     286             :         "  <Option name='TABLE_TYPE' type='string-select' description='Type of "
     287             :         "table' default='DELIMITED'>"
     288             :         "     <Value>DELIMITED</Value>"
     289             :         "     <Value>CHARACTER</Value>"
     290             :         "     <Value>BINARY</Value>"
     291             :         "  </Option>"
     292             :         "  <Option name='LINE_ENDING' type='string-select' description="
     293             :         "'end-of-line sequence. Only applies for "
     294             :         "TABLE_TYPE=DELIMITED/CHARACTER' "
     295             :         "default='CRLF'>"
     296             :         "    <Value>CRLF</Value>"
     297             :         "    <Value>LF</Value>"
     298             :         "  </Option>"
     299             :         "  <Option name='GEOM_COLUMNS' type='string-select' description='How "
     300             :         "geometry is encoded' default='AUTO'>"
     301             :         "     <Value>AUTO</Value>"
     302             :         "     <Value>WKT</Value>"
     303             :         "     <Value>LONG_LAT</Value>"
     304             :         "  </Option>"
     305             :         "  <Option name='CREATE_VRT' type='boolean' description='Whether to "
     306             :         "generate "
     307             :         "a OGR VRT file. Only applies for TABLE_TYPE=DELIMITED' default='YES'/>"
     308             :         "  <Option name='LAT' type='string' description="
     309             :         "'Name of a field containing a Latitude value' default='Latitude'/>"
     310             :         "  <Option name='LONG' type='string' description="
     311             :         "'Name of a field containing a Longitude value' default='Longitude'/>"
     312             :         "  <Option name='ALT' type='string' description="
     313             :         "'Name of a field containing a Altitude value' default='Altitude'/>"
     314             :         "  <Option name='WKT' type='string' description="
     315             :         "'Name of a field containing a WKT value' default='WKT'/>"
     316        1873 :         "</LayerCreationOptionList>");
     317             : 
     318        1873 :     poDriver->SetMetadataItem(
     319             :         GDAL_DMD_CREATIONFIELDDATATYPES,
     320        1873 :         "Integer Integer64 Real String Date DateTime Time");
     321        1873 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
     322             : 
     323        1873 :     poDriver->pfnIdentify = PDS4DriverIdentify;
     324        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     325        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
     326        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
     327             : 
     328        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
     329        1873 :     poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "Features");
     330        1873 : }
     331             : 
     332             : /************************************************************************/
     333             : /*                        ISIS2DriverIdentify()                         */
     334             : /************************************************************************/
     335             : 
     336       65536 : int ISIS2DriverIdentify(GDALOpenInfo *poOpenInfo)
     337             : {
     338       65536 :     if (poOpenInfo->pabyHeader == nullptr)
     339       58357 :         return FALSE;
     340             : 
     341        7179 :     if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
     342             :                "^QUBE") == nullptr)
     343        7175 :         return FALSE;
     344             : 
     345           4 :     return TRUE;
     346             : }
     347             : 
     348             : /************************************************************************/
     349             : /*                    ISIS2DriverSetCommonMetadata()                    */
     350             : /************************************************************************/
     351             : 
     352        1873 : void ISIS2DriverSetCommonMetadata(GDALDriver *poDriver)
     353             : {
     354        1873 :     poDriver->SetDescription(ISIS2_DRIVER_NAME);
     355        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     356        1873 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     357        1873 :                               "USGS Astrogeology ISIS cube (Version 2)");
     358        1873 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isis2.html");
     359        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     360             : 
     361        1873 :     poDriver->pfnIdentify = ISIS2DriverIdentify;
     362        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     363        1873 : }
     364             : 
     365             : /************************************************************************/
     366             : /*                        ISIS3DriverIdentify()                         */
     367             : /************************************************************************/
     368             : 
     369       66152 : int ISIS3DriverIdentify(GDALOpenInfo *poOpenInfo)
     370             : {
     371       66152 :     if (poOpenInfo->fpL != nullptr && poOpenInfo->pabyHeader != nullptr &&
     372        7670 :         strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
     373             :                "IsisCube") != nullptr)
     374         543 :         return TRUE;
     375             : 
     376       65609 :     return FALSE;
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                    ISIS3DriverSetCommonMetadata()                    */
     381             : /************************************************************************/
     382             : 
     383        1873 : void ISIS3DriverSetCommonMetadata(GDALDriver *poDriver)
     384             : {
     385        1873 :     poDriver->SetDescription(ISIS3_DRIVER_NAME);
     386        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     387        1873 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     388        1873 :                               "USGS Astrogeology ISIS cube (Version 3)");
     389        1873 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isis3.html");
     390        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     391        1873 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "lbl cub");
     392        1873 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
     393        1873 :                               "Byte UInt16 Int16 Float32");
     394        1873 :     poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, "<OpenOptionList/>");
     395             : 
     396        1873 :     poDriver->SetMetadataItem(
     397             :         GDAL_DMD_OPENOPTIONLIST,
     398             :         "<OpenOptionList>"
     399             :         "  <Option name='INCLUDE_OFFLINE_CONTENT' type='boolean' default='YES' "
     400             :         "description='Whether to include a _data member in "
     401             :         "json:ISIS3 metadata with offline content of label objects'/>"
     402             :         "  <Option name='MAX_SIZE_OFFLINE_CONTENT' type='string' "
     403             :         "default='100000000' description='Maximum size of offline content to "
     404             :         "include in _data member, in bytes' min='0'/>"
     405        1873 :         "</OpenOptionList>");
     406             : 
     407        1873 :     poDriver->SetMetadataItem(
     408             :         GDAL_DMD_CREATIONOPTIONLIST,
     409             :         "<CreationOptionList>"
     410             :         "  <Option name='DATA_LOCATION' type='string-select' "
     411             :         "description='Location of pixel data' default='LABEL'>"
     412             :         "     <Value>LABEL</Value>"
     413             :         "     <Value>EXTERNAL</Value>"
     414             :         "     <Value>GEOTIFF</Value>"
     415             :         "  </Option>"
     416             :         "  <Option name='GEOTIFF_AS_REGULAR_EXTERNAL' type='boolean' "
     417             :         "description='Whether the GeoTIFF file, if uncompressed, should be "
     418             :         "registered as a regular raw file' default='YES'/>"
     419             :         "  <Option name='GEOTIFF_OPTIONS' type='string' "
     420             :         "description='Comma separated list of KEY=VALUE tuples to forward "
     421             :         "to the GeoTIFF driver'/>"
     422             :         "  <Option name='EXTERNAL_FILENAME' type='string' "
     423             :         "description='Override default external filename. "
     424             :         "Only for DATA_LOCATION=EXTERNAL or GEOTIFF'/>"
     425             :         "  <Option name='TILED' type='boolean' "
     426             :         "description='Whether the pixel data should be tiled' default='NO'/>"
     427             :         "  <Option name='BLOCKXSIZE' type='int' "
     428             :         "description='Tile width' default='256'/>"
     429             :         "  <Option name='BLOCKYSIZE' type='int' "
     430             :         "description='Tile height' default='256'/>"
     431             :         "  <Option name='COMMENT' type='string' "
     432             :         "description='Comment to add into the label'/>"
     433             :         "  <Option name='LATITUDE_TYPE' type='string-select' "
     434             :         "description='Value of Mapping.LatitudeType' default='Planetocentric'>"
     435             :         "     <Value>Planetocentric</Value>"
     436             :         "     <Value>Planetographic</Value>"
     437             :         "  </Option>"
     438             :         "  <Option name='LONGITUDE_DIRECTION' type='string-select' "
     439             :         "description='Value of Mapping.LongitudeDirection' "
     440             :         "default='PositiveEast'>"
     441             :         "     <Value>PositiveEast</Value>"
     442             :         "     <Value>PositiveWest</Value>"
     443             :         "  </Option>"
     444             :         "  <Option name='TARGET_NAME' type='string' description='Value of "
     445             :         "Mapping.TargetName'/>"
     446             :         "  <Option name='FORCE_360' type='boolean' "
     447             :         "description='Whether to force longitudes in [0,360] range' "
     448             :         "default='NO'/>"
     449             :         "  <Option name='WRITE_BOUNDING_DEGREES' type='boolean' "
     450             :         "description='Whether to write Min/MaximumLong/Latitude values' "
     451             :         "default='YES'/>"
     452             :         "  <Option name='BOUNDING_DEGREES' type='string' "
     453             :         "description='Manually set bounding box with the syntax "
     454             :         "min_long,min_lat,max_long,max_lat'/>"
     455             :         "  <Option name='USE_SRC_LABEL' type='boolean' "
     456             :         "description='Whether to use source label in ISIS3 to ISIS3 "
     457             :         "conversions' "
     458             :         "default='YES'/>"
     459             :         "  <Option name='USE_SRC_MAPPING' type='boolean' "
     460             :         "description='Whether to use Mapping group from source label in "
     461             :         "ISIS3 to ISIS3 conversions' "
     462             :         "default='NO'/>"
     463             :         "  <Option name='USE_SRC_HISTORY' type='boolean' "
     464             :         "description='Whether to use content pointed by the History object in "
     465             :         "ISIS3 to ISIS3 conversions' "
     466             :         "default='YES'/>"
     467             :         "  <Option name='ADD_GDAL_HISTORY' type='boolean' "
     468             :         "description='Whether to add GDAL specific history in the content "
     469             :         "pointed "
     470             :         "by the History object in "
     471             :         "ISIS3 to ISIS3 conversions' "
     472             :         "default='YES'/>"
     473             :         "  <Option name='GDAL_HISTORY' type='string' "
     474             :         "description='Manually defined GDAL history. Must be formatted as "
     475             :         "ISIS3 "
     476             :         "PDL. If not specified, it is automatically composed.'/>"
     477        1873 :         "</CreationOptionList>");
     478             : 
     479        1873 :     poDriver->pfnIdentify = ISIS3DriverIdentify;
     480        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     481        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
     482        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
     483        1873 : }
     484             : 
     485             : /************************************************************************/
     486             : /*                        VICARGetLabelOffset()                         */
     487             : /************************************************************************/
     488             : 
     489       76914 : vsi_l_offset VICARGetLabelOffset(GDALOpenInfo *poOpenInfo)
     490             : 
     491             : {
     492       76914 :     if (poOpenInfo->pabyHeader == nullptr || poOpenInfo->fpL == nullptr)
     493       61193 :         return static_cast<vsi_l_offset>(-1);
     494             : 
     495        7054 :     const auto HasFoundVICARKeywords = [](const char *pszHeader)
     496             :     {
     497        7284 :         return strstr(pszHeader, "LBLSIZE") != nullptr &&
     498         230 :                strstr(pszHeader, "FORMAT") != nullptr &&
     499         230 :                strstr(pszHeader, "NL") != nullptr &&
     500        7514 :                strstr(pszHeader, "NS") != nullptr &&
     501        7284 :                strstr(pszHeader, "NB") != nullptr;
     502             :     };
     503             : 
     504       31442 :     std::string osHeader;
     505       15721 :     const char *pszHeader =
     506             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     507             :     // Some PDS3 images include a VICAR header pointed by ^IMAGE_HEADER.
     508             :     // If the user sets GDAL_TRY_PDS3_WITH_VICAR=YES, then we will gracefully
     509             :     // hand over the file to the VICAR dataset.
     510       15721 :     vsi_l_offset nOffset = 0;
     511             :     const bool bTryPDS3WithVicar =
     512       15724 :         CPLTestBool(CPLGetConfigOption("GDAL_TRY_PDS3_WITH_VICAR", "NO")) &&
     513           3 :         !STARTS_WITH(poOpenInfo->pszFilename, "/vsisubfile/");
     514       15721 :     if (bTryPDS3WithVicar && (nOffset = GetVICARLabelOffsetFromPDS3(
     515             :                                   pszHeader, poOpenInfo->fpL, osHeader)) > 0)
     516             :     {
     517           2 :         pszHeader = osHeader.c_str();
     518             :     }
     519             : 
     520       15721 :     if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
     521        8673 :         (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0)
     522             :     {
     523             :         // If opening in vector-only mode, then check when have NBB != 0
     524        8671 :         const char *pszNBB = strstr(pszHeader, "NBB");
     525        8671 :         if (pszNBB == nullptr)
     526        8668 :             return static_cast<vsi_l_offset>(-1);
     527           3 :         const char *pszEqualSign = strchr(pszNBB, '=');
     528           3 :         if (pszEqualSign == nullptr)
     529           0 :             return static_cast<vsi_l_offset>(-1);
     530           3 :         if (atoi(pszEqualSign + 1) == 0)
     531           1 :             return static_cast<vsi_l_offset>(-1);
     532             :     }
     533             : 
     534        7052 :     if (HasFoundVICARKeywords(pszHeader))
     535             :     {
     536             :         // If we find VICAR keywords, but the file starts with PDS_VERSION_ID,
     537             :         // it might be a PDS3 image that includes a VICAR header. Check if
     538             :         // this is the case.
     539         228 :         if (nOffset == 0 && STARTS_WITH(pszHeader, "PDS_VERSION_ID"))
     540             :         {
     541           4 :             if (!bTryPDS3WithVicar &&
     542           2 :                 (!GDALGetDriverByName("PDS") ||
     543           2 :                  poOpenInfo->IsSingleAllowedDriver("VICAR")))
     544             :             {
     545           2 :                 const auto nOffset2 = GetVICARLabelOffsetFromPDS3(
     546             :                     pszHeader, poOpenInfo->fpL, osHeader);
     547           2 :                 if (nOffset2 > 0 && HasFoundVICARKeywords(osHeader.c_str()))
     548             :                 {
     549           2 :                     return nOffset2;
     550             :                 }
     551             :             }
     552             :         }
     553         226 :         return nOffset;
     554             :     }
     555        6824 :     return static_cast<vsi_l_offset>(-1);
     556             : }
     557             : 
     558             : /************************************************************************/
     559             : /*                        VICARDriverIdentify()                         */
     560             : /************************************************************************/
     561             : 
     562       76799 : static int VICARDriverIdentify(GDALOpenInfo *poOpenInfo)
     563             : {
     564       76799 :     return VICARGetLabelOffset(poOpenInfo) != static_cast<vsi_l_offset>(-1);
     565             : }
     566             : 
     567             : /************************************************************************/
     568             : /*                    VICARDriverSetCommonMetadata()                    */
     569             : /************************************************************************/
     570             : 
     571        1873 : void VICARDriverSetCommonMetadata(GDALDriver *poDriver)
     572             : {
     573        1873 :     poDriver->SetDescription(VICAR_DRIVER_NAME);
     574        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     575        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     576        1873 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MIPL VICAR file");
     577        1873 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/vicar.html");
     578        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     579        1873 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
     580        1873 :                               "Byte Int16 Int32 Float32 Float64 CFloat32");
     581        1873 :     poDriver->SetMetadataItem(
     582             :         GDAL_DMD_CREATIONOPTIONLIST,
     583             :         "<CreationOptionList>"
     584             :         "  <Option name='GEOREF_FORMAT' type='string-select' "
     585             :         "description='How to encode georeferencing information' "
     586             :         "default='MIPL'>"
     587             :         "     <Value>MIPL</Value>"
     588             :         "     <Value>GEOTIFF</Value>"
     589             :         "  </Option>"
     590             :         "  <Option name='COORDINATE_SYSTEM_NAME' type='string-select' "
     591             :         "description='Value of MAP.COORDINATE_SYSTEM_NAME' "
     592             :         "default='PLANETOCENTRIC'>"
     593             :         "     <Value>PLANETOCENTRIC</Value>"
     594             :         "     <Value>PLANETOGRAPHIC</Value>"
     595             :         "  </Option>"
     596             :         "  <Option name='POSITIVE_LONGITUDE_DIRECTION' type='string-select' "
     597             :         "description='Value of MAP.POSITIVE_LONGITUDE_DIRECTION' "
     598             :         "default='EAST'>"
     599             :         "     <Value>EAST</Value>"
     600             :         "     <Value>WEST</Value>"
     601             :         "  </Option>"
     602             :         "  <Option name='TARGET_NAME' type='string' description='Value of "
     603             :         "MAP.TARGET_NAME'/>"
     604             :         "  <Option name='USE_SRC_LABEL' type='boolean' "
     605             :         "description='Whether to use source label in VICAR to VICAR "
     606             :         "conversions' "
     607             :         "default='YES'/>"
     608             :         "  <Option name='USE_SRC_MAP' type='boolean' "
     609             :         "description='Whether to use MAP property from source label in "
     610             :         "VICAR to VICAR conversions' "
     611             :         "default='NO'/>"
     612             :         "  <Option name='LABEL' type='string' "
     613             :         "description='Label to use, either as a JSON string or a filename "
     614             :         "containing one'/>"
     615             :         "  <Option name='COMPRESS' type='string-select' "
     616             :         "description='Compression method' default='NONE'>"
     617             :         "     <Value>NONE</Value>"
     618             :         "     <Value>BASIC</Value>"
     619             :         "     <Value>BASIC2</Value>"
     620             :         "  </Option>"
     621        1873 :         "</CreationOptionList>");
     622             : 
     623        1873 :     poDriver->pfnIdentify = VICARDriverIdentify;
     624        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     625        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
     626        1873 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
     627        1873 : }
     628             : 
     629             : /************************************************************************/
     630             : /*                      DeclareDeferredPDSPlugin()                      */
     631             : /************************************************************************/
     632             : 
     633             : #ifdef PLUGIN_FILENAME
     634             : void DeclareDeferredPDSPlugin()
     635             : {
     636             :     if (GDALGetDriverByName(PDS_DRIVER_NAME) != nullptr)
     637             :     {
     638             :         return;
     639             :     }
     640             :     {
     641             :         auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     642             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     643             :         poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     644             :                                   PLUGIN_INSTALLATION_MESSAGE);
     645             : #endif
     646             :         PDSDriverSetCommonMetadata(poDriver);
     647             :         GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     648             :     }
     649             :     {
     650             :         auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     651             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     652             :         poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     653             :                                   PLUGIN_INSTALLATION_MESSAGE);
     654             : #endif
     655             :         PDS4DriverSetCommonMetadata(poDriver);
     656             :         GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     657             :     }
     658             :     {
     659             :         auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     660             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     661             :         poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     662             :                                   PLUGIN_INSTALLATION_MESSAGE);
     663             : #endif
     664             :         ISIS2DriverSetCommonMetadata(poDriver);
     665             :         GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     666             :     }
     667             :     {
     668             :         auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     669             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     670             :         poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     671             :                                   PLUGIN_INSTALLATION_MESSAGE);
     672             : #endif
     673             :         ISIS3DriverSetCommonMetadata(poDriver);
     674             :         GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     675             :     }
     676             :     {
     677             :         auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     678             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     679             :         poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     680             :                                   PLUGIN_INSTALLATION_MESSAGE);
     681             : #endif
     682             :         VICARDriverSetCommonMetadata(poDriver);
     683             :         GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     684             :     }
     685             : }
     686             : #endif

Generated by: LCOV version 1.14