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

Generated by: LCOV version 1.14