LCOV - code coverage report
Current view: top level - frmts/tsx - tsxdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 26 340 7.6 %
Date: 2024-05-03 15:49:35 Functions: 2 17 11.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:     TerraSAR-X XML Product Support
       4             :  * Purpose:     Support for TerraSAR-X XML Metadata files
       5             :  * Author:      Philippe Vachon <philippe@cowpig.ca>
       6             :  * Description: This driver adds support for reading metadata and georef data
       7             :  *              associated with TerraSAR-X products.
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2007, Philippe Vachon <philippe@cowpig.ca>
      11             :  * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * Permission is hereby granted, free of charge, to any person obtaining a
      14             :  * copy of this software and associated documentation files (the "Software"),
      15             :  * to deal in the Software without restriction, including without limitation
      16             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      17             :  * and/or sell copies of the Software, and to permit persons to whom the
      18             :  * Software is furnished to do so, subject to the following conditions:
      19             :  *
      20             :  * The above copyright notice and this permission notice shall be included
      21             :  * in all copies or substantial portions of the Software.
      22             :  *
      23             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      24             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      25             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      26             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      28             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      29             :  * DEALINGS IN THE SOFTWARE.
      30             :  ****************************************************************************/
      31             : 
      32             : #include "cpl_minixml.h"
      33             : #include "gdal_frmts.h"
      34             : #include "gdal_pam.h"
      35             : #include "ogr_spatialref.h"
      36             : 
      37             : #define MAX_GCPS 5000  // this should be more than enough ground control points
      38             : 
      39             : namespace gdal::TSX
      40             : {
      41             : enum ePolarization
      42             : {
      43             :     HH = 0,
      44             :     HV,
      45             :     VH,
      46             :     VV
      47             : };
      48             : 
      49             : enum eProductType
      50             : {
      51             :     eSSC = 0,
      52             :     eMGD,
      53             :     eEEC,
      54             :     eGEC,
      55             :     eUnknown
      56             : };
      57             : }  // namespace gdal::TSX
      58             : 
      59             : using namespace gdal::TSX;
      60             : 
      61             : /************************************************************************/
      62             : /* Helper Functions                                                     */
      63             : /************************************************************************/
      64             : 
      65             : /* GetFilePath: return a relative path to a file within an XML node.
      66             :  * Returns Null on failure
      67             :  */
      68           0 : static CPLString GetFilePath(CPLXMLNode *psXMLNode, const char **pszNodeType)
      69             : {
      70             :     const char *pszDirectory =
      71           0 :         CPLGetXMLValue(psXMLNode, "file.location.path", "");
      72             :     const char *pszFilename =
      73           0 :         CPLGetXMLValue(psXMLNode, "file.location.filename", "");
      74           0 :     *pszNodeType = CPLGetXMLValue(psXMLNode, "type", " ");
      75             : 
      76           0 :     if (pszDirectory == nullptr || pszFilename == nullptr)
      77             :     {
      78           0 :         return "";
      79             :     }
      80             : 
      81           0 :     return CPLString(pszDirectory) + '/' + pszFilename;
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /* ==================================================================== */
      86             : /*                                TSXDataset                                 */
      87             : /* ==================================================================== */
      88             : /************************************************************************/
      89             : 
      90             : class TSXDataset final : public GDALPamDataset
      91             : {
      92             :     int nGCPCount;
      93             :     GDAL_GCP *pasGCPList;
      94             : 
      95             :     OGRSpatialReference m_oGCPSRS{};
      96             : 
      97             :     OGRSpatialReference m_oSRS{};
      98             :     double adfGeoTransform[6];
      99             :     bool bHaveGeoTransform;
     100             : 
     101             :     eProductType nProduct;
     102             : 
     103             :   public:
     104             :     TSXDataset();
     105             :     virtual ~TSXDataset();
     106             : 
     107             :     virtual int GetGCPCount() override;
     108             :     const OGRSpatialReference *GetGCPSpatialRef() const override;
     109             :     virtual const GDAL_GCP *GetGCPs() override;
     110             : 
     111             :     CPLErr GetGeoTransform(double *padfTransform) override;
     112             :     const OGRSpatialReference *GetSpatialRef() const override;
     113             : 
     114             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
     115             :     static int Identify(GDALOpenInfo *poOpenInfo);
     116             : 
     117             :   private:
     118             :     bool getGCPsFromGEOREF_XML(char *pszGeorefFilename);
     119             : };
     120             : 
     121             : /************************************************************************/
     122             : /* ==================================================================== */
     123             : /*                                TSXRasterBand                           */
     124             : /* ==================================================================== */
     125             : /************************************************************************/
     126             : 
     127             : class TSXRasterBand final : public GDALPamRasterBand
     128             : {
     129             :     GDALDataset *poBand;
     130             :     ePolarization ePol;
     131             : 
     132             :   public:
     133             :     TSXRasterBand(TSXDataset *poDSIn, GDALDataType eDataType,
     134             :                   ePolarization ePol, GDALDataset *poBand);
     135             :     virtual ~TSXRasterBand();
     136             : 
     137             :     virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
     138             :                               void *pImage) override;
     139             : 
     140             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
     141             : };
     142             : 
     143             : /************************************************************************/
     144             : /*                            TSXRasterBand                             */
     145             : /************************************************************************/
     146             : 
     147           0 : TSXRasterBand::TSXRasterBand(TSXDataset *poDSIn, GDALDataType eDataTypeIn,
     148           0 :                              ePolarization ePolIn, GDALDataset *poBandIn)
     149           0 :     : poBand(poBandIn), ePol(ePolIn)
     150             : {
     151           0 :     poDS = poDSIn;
     152           0 :     eDataType = eDataTypeIn;
     153             : 
     154           0 :     switch (ePol)
     155             :     {
     156           0 :         case HH:
     157           0 :             SetMetadataItem("POLARIMETRIC_INTERP", "HH");
     158           0 :             break;
     159           0 :         case HV:
     160           0 :             SetMetadataItem("POLARIMETRIC_INTERP", "HV");
     161           0 :             break;
     162           0 :         case VH:
     163           0 :             SetMetadataItem("POLARIMETRIC_INTERP", "VH");
     164           0 :             break;
     165           0 :         case VV:
     166           0 :             SetMetadataItem("POLARIMETRIC_INTERP", "VV");
     167           0 :             break;
     168             :     }
     169             : 
     170             :     /* now setup the actual raster reader */
     171           0 :     GDALRasterBand *poSrcBand = poBandIn->GetRasterBand(1);
     172           0 :     poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     173           0 : }
     174             : 
     175             : /************************************************************************/
     176             : /*                            TSXRasterBand()                           */
     177             : /************************************************************************/
     178             : 
     179           0 : TSXRasterBand::~TSXRasterBand()
     180             : {
     181           0 :     if (poBand != nullptr)
     182           0 :         GDALClose(reinterpret_cast<GDALRasterBandH>(poBand));
     183           0 : }
     184             : 
     185             : /************************************************************************/
     186             : /*                             IReadBlock()                             */
     187             : /************************************************************************/
     188             : 
     189           0 : CPLErr TSXRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     190             : {
     191             :     int nRequestYSize;
     192             : 
     193             :     /* Check if the last strip is partial so we can avoid over-requesting */
     194           0 :     if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
     195             :     {
     196           0 :         nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
     197           0 :         memset(pImage, 0,
     198           0 :                static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
     199           0 :                    nBlockXSize * nBlockYSize);
     200             :     }
     201             :     else
     202             :     {
     203           0 :         nRequestYSize = nBlockYSize;
     204             :     }
     205             : 
     206             :     /* Read Complex Data */
     207           0 :     if (eDataType == GDT_CInt16)
     208             :     {
     209           0 :         return poBand->RasterIO(
     210           0 :             GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
     211             :             nBlockXSize, nRequestYSize, pImage, nBlockXSize, nRequestYSize,
     212           0 :             GDT_CInt16, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
     213             :     }
     214             : 
     215             :     // Detected Product
     216           0 :     return poBand->RasterIO(
     217           0 :         GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
     218             :         nBlockXSize, nRequestYSize, pImage, nBlockXSize, nRequestYSize,
     219           0 :         GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
     220             : }
     221             : 
     222             : /************************************************************************/
     223             : /* ==================================================================== */
     224             : /*                                TSXDataset                                */
     225             : /* ==================================================================== */
     226             : /************************************************************************/
     227             : 
     228             : /************************************************************************/
     229             : /*                             TSXDataset()                             */
     230             : /************************************************************************/
     231             : 
     232           0 : TSXDataset::TSXDataset()
     233             :     : nGCPCount(0), pasGCPList(nullptr), bHaveGeoTransform(false),
     234           0 :       nProduct(eUnknown)
     235             : {
     236           0 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     237           0 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     238           0 :     adfGeoTransform[0] = 0.0;
     239           0 :     adfGeoTransform[1] = 1.0;
     240           0 :     adfGeoTransform[2] = 0.0;
     241           0 :     adfGeoTransform[3] = 0.0;
     242           0 :     adfGeoTransform[4] = 0.0;
     243           0 :     adfGeoTransform[5] = 1.0;
     244           0 : }
     245             : 
     246             : /************************************************************************/
     247             : /*                            ~TSXDataset()                             */
     248             : /************************************************************************/
     249             : 
     250           0 : TSXDataset::~TSXDataset()
     251             : {
     252           0 :     FlushCache(true);
     253             : 
     254           0 :     if (nGCPCount > 0)
     255             :     {
     256           0 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     257           0 :         CPLFree(pasGCPList);
     258             :     }
     259           0 : }
     260             : 
     261             : /************************************************************************/
     262             : /*                              Identify()                              */
     263             : /************************************************************************/
     264             : 
     265       50077 : int TSXDataset::Identify(GDALOpenInfo *poOpenInfo)
     266             : {
     267       50077 :     if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 260)
     268             :     {
     269       46549 :         if (poOpenInfo->bIsDirectory)
     270             :         {
     271             :             const CPLString osFilename = CPLFormCIFilename(
     272         362 :                 poOpenInfo->pszFilename,
     273         362 :                 CPLGetFilename(poOpenInfo->pszFilename), "xml");
     274             : 
     275             :             /* Check if the filename contains TSX1_SAR (TerraSAR-X) or TDX1_SAR
     276             :              * (TanDEM-X) or PAZ1_SAR (PAZ) */
     277         724 :             if (!(STARTS_WITH_CI(CPLGetBasename(osFilename), "TSX1_SAR") ||
     278         362 :                   STARTS_WITH_CI(CPLGetBasename(osFilename), "TDX1_SAR") ||
     279         362 :                   STARTS_WITH_CI(CPLGetBasename(osFilename), "PAZ1_SAR")))
     280         362 :                 return 0;
     281             : 
     282             :             VSIStatBufL sStat;
     283           0 :             if (VSIStatL(osFilename, &sStat) == 0)
     284           0 :                 return 1;
     285             :         }
     286             : 
     287       46187 :         return 0;
     288             :     }
     289             : 
     290             :     /* Check if the filename contains TSX1_SAR (TerraSAR-X) or TDX1_SAR
     291             :      * (TanDEM-X) or PAZ1_SAR (PAZ) */
     292        7056 :     if (!(STARTS_WITH_CI(CPLGetBasename(poOpenInfo->pszFilename), "TSX1_SAR") ||
     293        3528 :           STARTS_WITH_CI(CPLGetBasename(poOpenInfo->pszFilename), "TDX1_SAR") ||
     294        3528 :           STARTS_WITH_CI(CPLGetBasename(poOpenInfo->pszFilename), "PAZ1_SAR")))
     295        3528 :         return 0;
     296             : 
     297             :     /* finally look for the <level1Product tag */
     298           0 :     if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     299             :                         "<level1Product"))
     300           0 :         return 0;
     301             : 
     302           0 :     return 1;
     303             : }
     304             : 
     305             : /************************************************************************/
     306             : /*                                getGCPsFromGEOREF_XML()               */
     307             : /*Reads georeferencing information from the TerraSAR-X GEOREF.XML file    */
     308             : /*and writes the information to the dataset's gcp list and projection     */
     309             : /*string.                                                                */
     310             : /*Returns true on success.                                                */
     311             : /************************************************************************/
     312           0 : bool TSXDataset::getGCPsFromGEOREF_XML(char *pszGeorefFilename)
     313             : {
     314             :     // open GEOREF.xml
     315           0 :     CPLXMLNode *psGeorefData = CPLParseXMLFile(pszGeorefFilename);
     316           0 :     if (psGeorefData == nullptr)
     317           0 :         return false;
     318             : 
     319             :     // get the ellipsoid and semi-major, semi-minor axes
     320           0 :     OGRSpatialReference osr;
     321             :     CPLXMLNode *psSphere =
     322           0 :         CPLGetXMLNode(psGeorefData, "=geoReference.referenceFrames.sphere");
     323           0 :     if (psSphere != nullptr)
     324             :     {
     325             :         const char *pszEllipsoidName =
     326           0 :             CPLGetXMLValue(psSphere, "ellipsoidID", "");
     327             :         const double minor_axis =
     328           0 :             CPLAtof(CPLGetXMLValue(psSphere, "semiMinorAxis", "0.0"));
     329             :         const double major_axis =
     330           0 :             CPLAtof(CPLGetXMLValue(psSphere, "semiMajorAxis", "0.0"));
     331             :         // save datum parameters to the spatial reference
     332           0 :         if (EQUAL(pszEllipsoidName, "") || minor_axis == 0.0 ||
     333             :             major_axis == 0.0)
     334             :         {
     335           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     336             :                      "Warning- incomplete"
     337             :                      " ellipsoid information.  Using wgs-84 parameters.\n");
     338           0 :             osr.SetWellKnownGeogCS("WGS84");
     339             :         }
     340           0 :         else if (EQUAL(pszEllipsoidName, "WGS84"))
     341             :         {
     342           0 :             osr.SetWellKnownGeogCS("WGS84");
     343             :         }
     344             :         else
     345             :         {
     346           0 :             const double inv_flattening =
     347           0 :                 major_axis / (major_axis - minor_axis);
     348           0 :             osr.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
     349             :         }
     350             :     }
     351             : 
     352             :     // get gcps
     353             :     CPLXMLNode *psGeolocationGrid =
     354           0 :         CPLGetXMLNode(psGeorefData, "=geoReference.geolocationGrid");
     355           0 :     if (psGeolocationGrid == nullptr)
     356             :     {
     357           0 :         CPLDestroyXMLNode(psGeorefData);
     358           0 :         return false;
     359             :     }
     360           0 :     nGCPCount = atoi(
     361             :         CPLGetXMLValue(psGeolocationGrid, "numberOfGridPoints.total", "0"));
     362             :     // count the gcps if the given count value is invalid
     363           0 :     CPLXMLNode *psNode = nullptr;
     364           0 :     if (nGCPCount <= 0)
     365             :     {
     366           0 :         for (psNode = psGeolocationGrid->psChild; psNode != nullptr;
     367           0 :              psNode = psNode->psNext)
     368           0 :             if (EQUAL(psNode->pszValue, "gridPoint"))
     369           0 :                 nGCPCount++;
     370             :     }
     371             :     // if there are no gcps, fail
     372           0 :     if (nGCPCount <= 0)
     373             :     {
     374           0 :         CPLDestroyXMLNode(psGeorefData);
     375           0 :         return false;
     376             :     }
     377             : 
     378             :     // put some reasonable limits of the number of gcps
     379           0 :     if (nGCPCount > MAX_GCPS)
     380           0 :         nGCPCount = MAX_GCPS;
     381             :     // allocate memory for the gcps
     382           0 :     pasGCPList =
     383           0 :         reinterpret_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nGCPCount));
     384             : 
     385             :     // loop through all gcps and set info
     386             : 
     387             :     // save the number allocated to ensure it does not run off the end of the
     388             :     // array
     389           0 :     const int gcps_allocated = nGCPCount;
     390           0 :     nGCPCount = 0;  // reset to zero and count
     391             :     // do a check on the grid point to make sure it has lat,long row, and column
     392             :     // it seems that only SSC products contain row, col - how to map lat long
     393             :     // otherwise?? for now fail if row and col are not present - just check the
     394             :     // first and assume the rest are the same
     395           0 :     for (psNode = psGeolocationGrid->psChild; psNode != nullptr;
     396           0 :          psNode = psNode->psNext)
     397             :     {
     398           0 :         if (!EQUAL(psNode->pszValue, "gridPoint"))
     399           0 :             continue;
     400             : 
     401           0 :         if (!strcmp(CPLGetXMLValue(psNode, "col", "error"), "error") ||
     402           0 :             !strcmp(CPLGetXMLValue(psNode, "row", "error"), "error") ||
     403           0 :             !strcmp(CPLGetXMLValue(psNode, "lon", "error"), "error") ||
     404           0 :             !strcmp(CPLGetXMLValue(psNode, "lat", "error"), "error"))
     405             :         {
     406           0 :             CPLDestroyXMLNode(psGeorefData);
     407           0 :             return false;
     408             :         }
     409             :     }
     410           0 :     for (psNode = psGeolocationGrid->psChild; psNode != nullptr;
     411           0 :          psNode = psNode->psNext)
     412             :     {
     413             :         // break out if the end of the array has been reached
     414           0 :         if (nGCPCount >= gcps_allocated)
     415             :         {
     416           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     417             :                      "GDAL TSX driver: Truncating the number of GCPs.");
     418           0 :             break;
     419             :         }
     420             : 
     421           0 :         GDAL_GCP *psGCP = pasGCPList + nGCPCount;
     422             : 
     423           0 :         if (!EQUAL(psNode->pszValue, "gridPoint"))
     424           0 :             continue;
     425             : 
     426           0 :         nGCPCount++;
     427             : 
     428             :         char szID[32];
     429           0 :         snprintf(szID, sizeof(szID), "%d", nGCPCount);
     430           0 :         psGCP->pszId = CPLStrdup(szID);
     431           0 :         psGCP->pszInfo = CPLStrdup("");
     432           0 :         psGCP->dfGCPPixel = CPLAtof(CPLGetXMLValue(psNode, "col", "0"));
     433           0 :         psGCP->dfGCPLine = CPLAtof(CPLGetXMLValue(psNode, "row", "0"));
     434           0 :         psGCP->dfGCPX = CPLAtof(CPLGetXMLValue(psNode, "lon", ""));
     435           0 :         psGCP->dfGCPY = CPLAtof(CPLGetXMLValue(psNode, "lat", ""));
     436             :         // looks like height is in meters - should it be converted so xyz are
     437             :         // all on the same scale??
     438           0 :         psGCP->dfGCPZ = 0;
     439             :         // CPLAtof(CPLGetXMLValue(psNode,"height",""));
     440             :     }
     441             : 
     442           0 :     m_oGCPSRS = std::move(osr);
     443             : 
     444           0 :     CPLDestroyXMLNode(psGeorefData);
     445             : 
     446           0 :     return true;
     447             : }
     448             : 
     449             : /************************************************************************/
     450             : /*                                Open()                                */
     451             : /************************************************************************/
     452             : 
     453           0 : GDALDataset *TSXDataset::Open(GDALOpenInfo *poOpenInfo)
     454             : {
     455             :     /* -------------------------------------------------------------------- */
     456             :     /*      Is this a TerraSAR-X product file?                              */
     457             :     /* -------------------------------------------------------------------- */
     458           0 :     if (!TSXDataset::Identify(poOpenInfo))
     459             :     {
     460           0 :         return nullptr; /* nope */
     461             :     }
     462             : 
     463             :     /* -------------------------------------------------------------------- */
     464             :     /*      Confirm the requested access is supported.                      */
     465             :     /* -------------------------------------------------------------------- */
     466           0 :     if (poOpenInfo->eAccess == GA_Update)
     467             :     {
     468           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     469             :                  "The TSX driver does not support update access to existing"
     470             :                  " datasets.\n");
     471           0 :         return nullptr;
     472             :     }
     473             : 
     474           0 :     CPLString osFilename;
     475             : 
     476           0 :     if (poOpenInfo->bIsDirectory)
     477             :     {
     478             :         osFilename =
     479           0 :             CPLFormCIFilename(poOpenInfo->pszFilename,
     480           0 :                               CPLGetFilename(poOpenInfo->pszFilename), "xml");
     481             :     }
     482             :     else
     483           0 :         osFilename = poOpenInfo->pszFilename;
     484             : 
     485             :     /* Ingest the XML */
     486           0 :     CPLXMLNode *psData = CPLParseXMLFile(osFilename);
     487           0 :     if (psData == nullptr)
     488           0 :         return nullptr;
     489             : 
     490             :     /* find the product components */
     491             :     CPLXMLNode *psComponents =
     492           0 :         CPLGetXMLNode(psData, "=level1Product.productComponents");
     493           0 :     if (psComponents == nullptr)
     494             :     {
     495           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     496             :                  "Unable to find <productComponents> tag in file.\n");
     497           0 :         CPLDestroyXMLNode(psData);
     498           0 :         return nullptr;
     499             :     }
     500             : 
     501             :     /* find the product info tag */
     502             :     CPLXMLNode *psProductInfo =
     503           0 :         CPLGetXMLNode(psData, "=level1Product.productInfo");
     504           0 :     if (psProductInfo == nullptr)
     505             :     {
     506           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     507             :                  "Unable to find <productInfo> tag in file.\n");
     508           0 :         CPLDestroyXMLNode(psData);
     509           0 :         return nullptr;
     510             :     }
     511             : 
     512             :     /* -------------------------------------------------------------------- */
     513             :     /*      Create the dataset.                                             */
     514             :     /* -------------------------------------------------------------------- */
     515             : 
     516           0 :     TSXDataset *poDS = new TSXDataset();
     517             : 
     518             :     /* -------------------------------------------------------------------- */
     519             :     /*      Read in product info.                                           */
     520             :     /* -------------------------------------------------------------------- */
     521             : 
     522           0 :     poDS->SetMetadataItem(
     523             :         "SCENE_CENTRE_TIME",
     524             :         CPLGetXMLValue(psProductInfo,
     525           0 :                        "sceneInfo.sceneCenterCoord.azimuthTimeUTC", "unknown"));
     526           0 :     poDS->SetMetadataItem("OPERATIONAL_MODE",
     527             :                           CPLGetXMLValue(psProductInfo,
     528             :                                          "generationInfo.groundOperationsType",
     529           0 :                                          "unknown"));
     530           0 :     poDS->SetMetadataItem(
     531             :         "ORBIT_CYCLE",
     532           0 :         CPLGetXMLValue(psProductInfo, "missionInfo.orbitCycle", "unknown"));
     533           0 :     poDS->SetMetadataItem(
     534             :         "ABSOLUTE_ORBIT",
     535           0 :         CPLGetXMLValue(psProductInfo, "missionInfo.absOrbit", "unknown"));
     536           0 :     poDS->SetMetadataItem(
     537             :         "ORBIT_DIRECTION",
     538           0 :         CPLGetXMLValue(psProductInfo, "missionInfo.orbitDirection", "unknown"));
     539           0 :     poDS->SetMetadataItem("IMAGING_MODE",
     540             :                           CPLGetXMLValue(psProductInfo,
     541             :                                          "acquisitionInfo.imagingMode",
     542           0 :                                          "unknown"));
     543           0 :     poDS->SetMetadataItem("PRODUCT_VARIANT",
     544             :                           CPLGetXMLValue(psProductInfo,
     545             :                                          "productVariantInfo.productVariant",
     546           0 :                                          "unknown"));
     547           0 :     char *pszDataType = CPLStrdup(CPLGetXMLValue(
     548             :         psProductInfo, "imageDataInfo.imageDataType", "unknown"));
     549           0 :     poDS->SetMetadataItem("IMAGE_TYPE", pszDataType);
     550             : 
     551             :     /* Get raster information */
     552           0 :     int nRows = atoi(CPLGetXMLValue(
     553             :         psProductInfo, "imageDataInfo.imageRaster.numberOfRows", ""));
     554           0 :     int nCols = atoi(CPLGetXMLValue(
     555             :         psProductInfo, "imageDataInfo.imageRaster.numberOfColumns", ""));
     556             : 
     557           0 :     poDS->nRasterXSize = nCols;
     558           0 :     poDS->nRasterYSize = nRows;
     559             : 
     560           0 :     poDS->SetMetadataItem("ROW_SPACING",
     561             :                           CPLGetXMLValue(psProductInfo,
     562             :                                          "imageDataInfo.imageRaster.rowSpacing",
     563           0 :                                          "unknown"));
     564           0 :     poDS->SetMetadataItem(
     565             :         "COL_SPACING",
     566             :         CPLGetXMLValue(psProductInfo, "imageDataInfo.imageRaster.columnSpacing",
     567           0 :                        "unknown"));
     568           0 :     poDS->SetMetadataItem(
     569             :         "COL_SPACING_UNITS",
     570             :         CPLGetXMLValue(psProductInfo,
     571             :                        "imageDataInfo.imageRaster.columnSpacing.units",
     572           0 :                        "unknown"));
     573             : 
     574             :     /* Get equivalent number of looks */
     575           0 :     poDS->SetMetadataItem(
     576             :         "AZIMUTH_LOOKS",
     577             :         CPLGetXMLValue(psProductInfo, "imageDataInfo.imageRaster.azimuthLooks",
     578           0 :                        "unknown"));
     579           0 :     poDS->SetMetadataItem("RANGE_LOOKS",
     580             :                           CPLGetXMLValue(psProductInfo,
     581             :                                          "imageDataInfo.imageRaster.rangeLooks",
     582           0 :                                          "unknown"));
     583             : 
     584           0 :     const char *pszProductVariant = CPLGetXMLValue(
     585             :         psProductInfo, "productVariantInfo.productVariant", "unknown");
     586             : 
     587           0 :     poDS->SetMetadataItem("PRODUCT_VARIANT", pszProductVariant);
     588             : 
     589             :     /* Determine what product variant this is */
     590           0 :     if (STARTS_WITH_CI(pszProductVariant, "SSC"))
     591           0 :         poDS->nProduct = eSSC;
     592           0 :     else if (STARTS_WITH_CI(pszProductVariant, "MGD"))
     593           0 :         poDS->nProduct = eMGD;
     594           0 :     else if (STARTS_WITH_CI(pszProductVariant, "EEC"))
     595           0 :         poDS->nProduct = eEEC;
     596           0 :     else if (STARTS_WITH_CI(pszProductVariant, "GEC"))
     597           0 :         poDS->nProduct = eGEC;
     598             :     else
     599           0 :         poDS->nProduct = eUnknown;
     600             : 
     601             :     /* Start reading in the product components */
     602           0 :     char *pszGeorefFile = nullptr;
     603           0 :     CPLErr geoTransformErr = CE_Failure;
     604           0 :     for (CPLXMLNode *psComponent = psComponents->psChild;
     605           0 :          psComponent != nullptr; psComponent = psComponent->psNext)
     606             :     {
     607           0 :         const char *pszType = nullptr;
     608             :         const char *pszPath =
     609           0 :             CPLFormFilename(CPLGetDirname(osFilename),
     610           0 :                             GetFilePath(psComponent, &pszType).c_str(), "");
     611           0 :         const char *pszPolLayer = CPLGetXMLValue(psComponent, "polLayer", " ");
     612             : 
     613           0 :         if (!STARTS_WITH_CI(pszType, " "))
     614             :         {
     615           0 :             if (STARTS_WITH_CI(pszType, "MAPPING_GRID"))
     616             :             {
     617             :                 /* the mapping grid... save as a metadata item this path */
     618           0 :                 poDS->SetMetadataItem("MAPPING_GRID", pszPath);
     619             :             }
     620           0 :             else if (STARTS_WITH_CI(pszType, "GEOREF"))
     621             :             {
     622             :                 /* save the path to the georef data for later use */
     623           0 :                 CPLFree(pszGeorefFile);
     624           0 :                 pszGeorefFile = CPLStrdup(pszPath);
     625             :             }
     626             :         }
     627           0 :         else if (!STARTS_WITH_CI(pszPolLayer, " ") &&
     628           0 :                  STARTS_WITH_CI(psComponent->pszValue, "imageData"))
     629             :         {
     630             :             /* determine the polarization of this band */
     631             :             ePolarization ePol;
     632           0 :             if (STARTS_WITH_CI(pszPolLayer, "HH"))
     633             :             {
     634           0 :                 ePol = HH;
     635             :             }
     636           0 :             else if (STARTS_WITH_CI(pszPolLayer, "HV"))
     637             :             {
     638           0 :                 ePol = HV;
     639             :             }
     640           0 :             else if (STARTS_WITH_CI(pszPolLayer, "VH"))
     641             :             {
     642           0 :                 ePol = VH;
     643             :             }
     644             :             else
     645             :             {
     646           0 :                 ePol = VV;
     647             :             }
     648             : 
     649           0 :             GDALDataType eDataType = STARTS_WITH_CI(pszDataType, "COMPLEX")
     650           0 :                                          ? GDT_CInt16
     651             :                                          : GDT_UInt16;
     652             : 
     653             :             /* try opening the file that represents that band */
     654             :             GDALDataset *poBandData =
     655           0 :                 GDALDataset::FromHandle(GDALOpen(pszPath, GA_ReadOnly));
     656           0 :             if (poBandData != nullptr)
     657             :             {
     658             :                 TSXRasterBand *poBand =
     659           0 :                     new TSXRasterBand(poDS, eDataType, ePol, poBandData);
     660           0 :                 poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
     661             : 
     662             :                 // copy georeferencing info from the band
     663             :                 // need error checking??
     664             :                 // it will just save the info from the last band
     665           0 :                 const auto poSrcSRS = poBandData->GetSpatialRef();
     666           0 :                 if (poSrcSRS)
     667           0 :                     poDS->m_oSRS = *poSrcSRS;
     668             : 
     669             :                 geoTransformErr =
     670           0 :                     poBandData->GetGeoTransform(poDS->adfGeoTransform);
     671             :             }
     672             :         }
     673             :     }
     674             : 
     675             :     // now check if there is a geotransform
     676           0 :     if (!poDS->m_oSRS.IsEmpty() && geoTransformErr == CE_None)
     677             :     {
     678           0 :         poDS->bHaveGeoTransform = TRUE;
     679             :     }
     680             :     else
     681             :     {
     682           0 :         poDS->bHaveGeoTransform = FALSE;
     683           0 :         poDS->m_oSRS.Clear();
     684           0 :         poDS->adfGeoTransform[0] = 0.0;
     685           0 :         poDS->adfGeoTransform[1] = 1.0;
     686           0 :         poDS->adfGeoTransform[2] = 0.0;
     687           0 :         poDS->adfGeoTransform[3] = 0.0;
     688           0 :         poDS->adfGeoTransform[4] = 0.0;
     689           0 :         poDS->adfGeoTransform[5] = 1.0;
     690             :     }
     691             : 
     692           0 :     CPLFree(pszDataType);
     693             : 
     694             :     /* -------------------------------------------------------------------- */
     695             :     /*      Check and set matrix representation.                            */
     696             :     /* -------------------------------------------------------------------- */
     697             : 
     698           0 :     if (poDS->GetRasterCount() == 4)
     699             :     {
     700           0 :         poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
     701             :     }
     702             : 
     703             :     /* -------------------------------------------------------------------- */
     704             :     /*      Read the four corners and centre GCPs in                        */
     705             :     /* -------------------------------------------------------------------- */
     706             : 
     707             :     CPLXMLNode *psSceneInfo =
     708           0 :         CPLGetXMLNode(psData, "=level1Product.productInfo.sceneInfo");
     709           0 :     if (psSceneInfo != nullptr)
     710             :     {
     711             :         /* extract the GCPs from the provided file */
     712           0 :         bool success = false;
     713           0 :         if (pszGeorefFile != nullptr)
     714           0 :             success = poDS->getGCPsFromGEOREF_XML(pszGeorefFile);
     715             : 
     716             :         // if the gcp's cannot be extracted from the georef file, try to get the
     717             :         // corner coordinates for now just SSC because the others don't have
     718             :         // refColumn and refRow
     719           0 :         if (!success && poDS->nProduct == eSSC)
     720             :         {
     721           0 :             int nGCP = 0;
     722           0 :             double dfAvgHeight = CPLAtof(
     723             :                 CPLGetXMLValue(psSceneInfo, "sceneAverageHeight", "0.0"));
     724             : 
     725             :             // count and allocate gcps - there should be five - 4 corners and a
     726             :             // centre
     727           0 :             poDS->nGCPCount = 0;
     728           0 :             CPLXMLNode *psNode = psSceneInfo->psChild;
     729           0 :             for (; psNode != nullptr; psNode = psNode->psNext)
     730             :             {
     731           0 :                 if (!EQUAL(psNode->pszValue, "sceneCenterCoord") &&
     732           0 :                     !EQUAL(psNode->pszValue, "sceneCornerCoord"))
     733           0 :                     continue;
     734             : 
     735           0 :                 poDS->nGCPCount++;
     736             :             }
     737           0 :             if (poDS->nGCPCount > 0)
     738             :             {
     739           0 :                 poDS->pasGCPList =
     740           0 :                     (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount);
     741             : 
     742             :                 /* iterate over GCPs */
     743           0 :                 for (psNode = psSceneInfo->psChild; psNode != nullptr;
     744           0 :                      psNode = psNode->psNext)
     745             :                 {
     746           0 :                     GDAL_GCP *psGCP = poDS->pasGCPList + nGCP;
     747             : 
     748           0 :                     if (!EQUAL(psNode->pszValue, "sceneCenterCoord") &&
     749           0 :                         !EQUAL(psNode->pszValue, "sceneCornerCoord"))
     750           0 :                         continue;
     751             : 
     752           0 :                     psGCP->dfGCPPixel =
     753           0 :                         CPLAtof(CPLGetXMLValue(psNode, "refColumn", "0.0"));
     754           0 :                     psGCP->dfGCPLine =
     755           0 :                         CPLAtof(CPLGetXMLValue(psNode, "refRow", "0.0"));
     756           0 :                     psGCP->dfGCPX =
     757           0 :                         CPLAtof(CPLGetXMLValue(psNode, "lon", "0.0"));
     758           0 :                     psGCP->dfGCPY =
     759           0 :                         CPLAtof(CPLGetXMLValue(psNode, "lat", "0.0"));
     760           0 :                     psGCP->dfGCPZ = dfAvgHeight;
     761           0 :                     psGCP->pszId = CPLStrdup(CPLSPrintf("%d", nGCP));
     762           0 :                     psGCP->pszInfo = CPLStrdup("");
     763             : 
     764           0 :                     nGCP++;
     765             :                 }
     766             : 
     767             :                 // set the projection string - the fields are lat/long - seems
     768             :                 // to be WGS84 datum
     769           0 :                 poDS->m_oGCPSRS.SetWellKnownGeogCS("WGS84");
     770             :             }
     771             :         }
     772             : 
     773             :         // gcps override geotransform - does it make sense to have both??
     774           0 :         if (poDS->nGCPCount > 0)
     775             :         {
     776           0 :             poDS->bHaveGeoTransform = FALSE;
     777           0 :             poDS->m_oSRS.Clear();
     778           0 :             poDS->adfGeoTransform[0] = 0.0;
     779           0 :             poDS->adfGeoTransform[1] = 1.0;
     780           0 :             poDS->adfGeoTransform[2] = 0.0;
     781           0 :             poDS->adfGeoTransform[3] = 0.0;
     782           0 :             poDS->adfGeoTransform[4] = 0.0;
     783           0 :             poDS->adfGeoTransform[5] = 1.0;
     784             :         }
     785             :     }
     786             :     else
     787             :     {
     788           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     789             :                  "Unable to find sceneInfo tag in XML document. "
     790             :                  "Proceeding with caution.");
     791             :     }
     792             : 
     793           0 :     CPLFree(pszGeorefFile);
     794             : 
     795             :     /* -------------------------------------------------------------------- */
     796             :     /*      Initialize any PAM information.                                 */
     797             :     /* -------------------------------------------------------------------- */
     798           0 :     poDS->SetDescription(poOpenInfo->pszFilename);
     799           0 :     poDS->TryLoadXML();
     800             : 
     801             :     /* -------------------------------------------------------------------- */
     802             :     /*      Check for overviews.                                            */
     803             :     /* -------------------------------------------------------------------- */
     804           0 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
     805             : 
     806           0 :     CPLDestroyXMLNode(psData);
     807             : 
     808           0 :     return poDS;
     809             : }
     810             : 
     811             : /************************************************************************/
     812             : /*                            GetGCPCount()                             */
     813             : /************************************************************************/
     814             : 
     815           0 : int TSXDataset::GetGCPCount()
     816             : {
     817           0 :     return nGCPCount;
     818             : }
     819             : 
     820             : /************************************************************************/
     821             : /*                          GetGCPSpatialRef()                          */
     822             : /************************************************************************/
     823             : 
     824           0 : const OGRSpatialReference *TSXDataset::GetGCPSpatialRef() const
     825             : {
     826           0 :     return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
     827             : }
     828             : 
     829             : /************************************************************************/
     830             : /*                               GetGCPs()                              */
     831             : /************************************************************************/
     832             : 
     833           0 : const GDAL_GCP *TSXDataset::GetGCPs()
     834             : {
     835           0 :     return pasGCPList;
     836             : }
     837             : 
     838             : /************************************************************************/
     839             : /*                          GetSpatialRef()                             */
     840             : /************************************************************************/
     841             : 
     842           0 : const OGRSpatialReference *TSXDataset::GetSpatialRef() const
     843             : 
     844             : {
     845           0 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     846             : }
     847             : 
     848             : /************************************************************************/
     849             : /*                               GetGeotransform()                      */
     850             : /************************************************************************/
     851           0 : CPLErr TSXDataset::GetGeoTransform(double *padfTransform)
     852             : {
     853           0 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     854             : 
     855           0 :     if (bHaveGeoTransform)
     856           0 :         return CE_None;
     857             : 
     858           0 :     return CE_Failure;
     859             : }
     860             : 
     861             : /************************************************************************/
     862             : /*                         GDALRegister_TSX()                           */
     863             : /************************************************************************/
     864             : 
     865        1511 : void GDALRegister_TSX()
     866             : {
     867        1511 :     if (GDALGetDriverByName("TSX") != nullptr)
     868         295 :         return;
     869             : 
     870        1216 :     GDALDriver *poDriver = new GDALDriver();
     871             : 
     872        1216 :     poDriver->SetDescription("TSX");
     873        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     874        1216 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "TerraSAR-X Product");
     875        1216 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/tsx.html");
     876        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     877             : 
     878        1216 :     poDriver->pfnOpen = TSXDataset::Open;
     879        1216 :     poDriver->pfnIdentify = TSXDataset::Identify;
     880             : 
     881        1216 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     882             : }

Generated by: LCOV version 1.14