LCOV - code coverage report
Current view: top level - frmts/sentinel2 - sentinel2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1457 1549 94.1 %
Date: XXXX-XX-XX Functions: 51 53 96.2 %
Branches: 2155 3735 57.7 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  *
       3                 :            :  * Project:  GDAL
       4                 :            :  * Purpose:  Sentinel2 products
       5                 :            :  * Author:   Even Rouault, <even.rouault at spatialys.com>
       6                 :            :  * Funded by: Centre National d'Etudes Spatiales (CNES)
       7                 :            :  *
       8                 :            :  ******************************************************************************
       9                 :            :  * Copyright (c) 2015, Even Rouault, <even.rouault at spatialys.com>
      10                 :            :  *
      11                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :            :  * copy of this software and associated documentation files (the "Software"),
      13                 :            :  * to deal in the Software without restriction, including without limitation
      14                 :            :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :            :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :            :  * Software is furnished to do so, subject to the following conditions:
      17                 :            :  *
      18                 :            :  * The above copyright notice and this permission notice shall be included
      19                 :            :  * in all copies or substantial portions of the Software.
      20                 :            :  *
      21                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :            :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :            :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :            :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :            :  * DEALINGS IN THE SOFTWARE.
      28                 :            :  ****************************************************************************/
      29                 :            : 
      30                 :            : #include "cpl_minixml.h"
      31                 :            : #include "cpl_string.h"
      32                 :            : #include "gdal_pam.h"
      33                 :            : #include "gdal_proxy.h"
      34                 :            : #include "ogr_spatialref.h"
      35                 :            : #include "ogr_geometry.h"
      36                 :            : #include "gdaljp2metadata.h"
      37                 :            : #include "../vrt/vrtdataset.h"
      38                 :            : 
      39                 :            : #include <algorithm>
      40                 :            : #include <map>
      41                 :            : #include <set>
      42                 :            : #include <vector>
      43                 :            : 
      44                 :            : #ifdef HAVE_UNISTD_H
      45                 :            : #include <unistd.h>
      46                 :            : #endif
      47                 :            : 
      48                 :            : #ifndef STARTS_WITH_CI
      49                 :            : #define STARTS_WITH_CI(a,b) EQUALN(a,b,strlen(b))
      50                 :            : #endif
      51                 :            : 
      52                 :            : CPL_CVSID("$Id$");
      53                 :            : 
      54                 :            : CPL_C_START
      55                 :            : // TODO: Leave this declaration while Sentinel2 folks use this as a
      56                 :            : // plugin with GDAL 1.x.
      57                 :            : void GDALRegister_SENTINEL2();
      58                 :            : CPL_C_END
      59                 :            : 
      60                 :            : typedef enum
      61                 :            : {
      62                 :            :     SENTINEL2_L1B,
      63                 :            :     SENTINEL2_L1C,
      64                 :            :     SENTINEL2_L2A
      65                 :            : } SENTINEL2Level;
      66                 :            : 
      67                 :            : typedef struct
      68                 :            : {
      69                 :            :     const char* pszBandName;
      70                 :            :     int         nResolution; /* meters */
      71                 :            :     int         nWaveLength; /* nanometers */
      72                 :            :     int         nBandWidth;  /* nanometers */
      73                 :            :     GDALColorInterp eColorInterp;
      74                 :            : } SENTINEL2BandDescription;
      75                 :            : 
      76                 :            : static const SENTINEL2BandDescription asBandDesc[] =
      77                 :            : {
      78                 :            :     { "B1", 60, 443, 20, GCI_Undefined },
      79                 :            :     { "B2", 10, 490, 65, GCI_BlueBand },
      80                 :            :     { "B3", 10, 560, 35, GCI_GreenBand },
      81                 :            :     { "B4", 10, 665, 30, GCI_RedBand },
      82                 :            :     { "B5", 20, 705, 15, GCI_Undefined },
      83                 :            :     { "B6", 20, 740, 15, GCI_Undefined },
      84                 :            :     { "B7", 20, 783, 20, GCI_Undefined },
      85                 :            :     { "B8", 10, 842, 115, GCI_Undefined },
      86                 :            :     { "B8A", 20, 865, 20, GCI_Undefined },
      87                 :            :     { "B9", 60, 945, 20, GCI_Undefined },
      88                 :            :     { "B10", 60, 1375, 30, GCI_Undefined },
      89                 :            :     { "B11", 20, 1610, 90, GCI_Undefined },
      90                 :            :     { "B12", 20, 2190, 180, GCI_Undefined },
      91                 :            : };
      92                 :            : 
      93                 :            : #define NB_BANDS (sizeof(asBandDesc)/sizeof(asBandDesc[0]))
      94                 :            : 
      95                 :            : typedef enum
      96                 :            : {
      97                 :            :     TL_IMG_DATA,                /* Tile is located in IMG_DATA/ */
      98                 :            :     TL_IMG_DATA_Rxxm,           /* Tile is located in IMG_DATA/Rxxm/ */
      99                 :            :     TL_QI_DATA                  /* Tile is located in QI_DATA/ */
     100                 :            : } SENTINEL2_L2A_Tilelocation;
     101                 :            : 
     102                 :            : typedef struct
     103                 :            : {
     104                 :            :     const char* pszBandName;
     105                 :            :     const char* pszBandDescription;
     106                 :            :     SENTINEL2_L2A_Tilelocation eLocation;
     107                 :            : } SENTINEL2_L2A_BandDescription;
     108                 :            : 
     109 [ +  - ][ +  - ]:          1 : class L1CSafeCompatGranuleDescription
                 [ +  - ]
     110                 :            : {
     111                 :            : public:
     112                 :            :     CPLString osMTDTLPath; // GRANULE/L1C_T30TXT_A007999_20170102T111441/MTD_TL.xml
     113                 :            :     CPLString osBandPrefixPath; // GRANULE/L1C_T30TXT_A007999_20170102T111441/IMG_DATA/T30TXT_20170102T111442_
     114                 :            : };
     115                 :            : 
     116                 :            : static const SENTINEL2_L2A_BandDescription asL2ABandDesc[] =
     117                 :            : {
     118                 :            :     { "AOT", "Aerosol Optical Thickness map (at 550nm)", TL_IMG_DATA_Rxxm },
     119                 :            :     { "WVP", "Scene-average Water Vapour map", TL_IMG_DATA_Rxxm },
     120                 :            :     { "SCL", "Scene Classification", TL_IMG_DATA },
     121                 :            :     { "CLD", "Raster mask values range from 0 for high confidence clear sky to 100 for high confidence cloudy", TL_QI_DATA },
     122                 :            :     { "SNW", "Raster mask values range from 0 for high confidence NO snow/ice to 100 for high confidence snow/ice", TL_QI_DATA },
     123                 :            : };
     124                 :            : 
     125                 :            : #define NB_L2A_BANDS (sizeof(asL2ABandDesc)/sizeof(asL2ABandDesc[0]))
     126                 :            : 
     127                 :            : static
     128                 :            : const char* SENTINEL2GetOption( GDALOpenInfo* poOpenInfo,
     129                 :            :                                 const char* pszName,
     130                 :            :                                 const char* pszDefaultVal = NULL );
     131                 :            : static bool SENTINEL2GetTileInfo(const char* pszFilename,
     132                 :            :                                  int* pnWidth, int* pnHeight, int *pnBits);
     133                 :            : 
     134                 :            : /************************************************************************/
     135                 :            : /*                           SENTINEL2GranuleInfo                       */
     136                 :            : /************************************************************************/
     137                 :            : 
     138 [ +  - ][ +  - ]:          1 : class SENTINEL2GranuleInfo
                 [ +  - ]
     139                 :            : {
     140                 :            :     public:
     141                 :            :         CPLString osPath;
     142                 :            :         CPLString osBandPrefixPath; // for Sentinel 2C SafeCompact
     143                 :            :         double    dfMinX, dfMinY, dfMaxX, dfMaxY;
     144                 :            :         int       nWidth, nHeight;
     145                 :            : };
     146                 :            : 
     147                 :            : /************************************************************************/
     148                 :            : /* ==================================================================== */
     149                 :            : /*                         SENTINEL2Dataset                             */
     150                 :            : /* ==================================================================== */
     151                 :            : /************************************************************************/
     152                 :            : 
     153         [ -  + ]:          1 : class SENTINEL2DatasetContainer: public GDALPamDataset
     154                 :            : {
     155                 :            :     public:
     156                 :          1 :         SENTINEL2DatasetContainer() {}
     157                 :            : };
     158                 :            : 
     159                 :            : class SENTINEL2Dataset : public VRTDataset
     160                 :            : {
     161                 :            :         std::vector<CPLString>   aosNonJP2Files;
     162                 :            : 
     163                 :            :         void   AddL1CL2ABandMetadata(SENTINEL2Level eLevel,
     164                 :            :                                      CPLXMLNode* psRoot,
     165                 :            :                                      const std::vector<CPLString>& aosBands);
     166                 :            : 
     167                 :            :         static SENTINEL2Dataset *CreateL1CL2ADataset(
     168                 :            :                 SENTINEL2Level eLevel,
     169                 :            :                 bool bIsSafeCompact,
     170                 :            :                 const std::vector<CPLString>& aosGranuleList,
     171                 :            :                 const std::vector<L1CSafeCompatGranuleDescription>& aoL1CSafeCompactGranuleList,
     172                 :            :                 std::vector<CPLString>& aosNonJP2Files,
     173                 :            :                 int nSubDSPrecision,
     174                 :            :                 bool bIsPreview,
     175                 :            :                 bool bIsTCI,
     176                 :            :                 int nSubDSEPSGCode,
     177                 :            :                 bool bAlpha,
     178                 :            :                 const std::vector<CPLString>& aosBands,
     179                 :            :                 int nSaturatedVal,
     180                 :            :                 int nNodataVal);
     181                 :            : 
     182                 :            :     public:
     183                 :            :                     SENTINEL2Dataset(int nXSize, int nYSize);
     184                 :            :         virtual ~SENTINEL2Dataset();
     185                 :            : 
     186                 :            :         virtual char** GetFileList() override;
     187                 :            : 
     188                 :            :         static GDALDataset *Open( GDALOpenInfo * );
     189                 :            :         static GDALDataset *OpenL1BUserProduct( GDALOpenInfo * );
     190                 :            :         static GDALDataset *OpenL1BGranule( const char* pszFilename,
     191                 :            :                                             CPLXMLNode** ppsRoot = NULL,
     192                 :            :                                             int nResolutionOfInterest = 0,
     193                 :            :                                             std::set<CPLString> *poBandSet = NULL);
     194                 :            :         static GDALDataset *OpenL1BSubdataset( GDALOpenInfo * );
     195                 :            :         static GDALDataset *OpenL1C_L2A( const char* pszFilename,
     196                 :            :                                          SENTINEL2Level eLevel );
     197                 :            :         static GDALDataset *OpenL1CTile( const char* pszFilename,
     198                 :            :                                          CPLXMLNode** ppsRootMainMTD = NULL,
     199                 :            :                                          int nResolutionOfInterest = 0,
     200                 :            :                                          std::set<CPLString>* poBandSet = NULL);
     201                 :            :         static GDALDataset *OpenL1CTileSubdataset( GDALOpenInfo * );
     202                 :            :         static GDALDataset *OpenL1C_L2ASubdataset( GDALOpenInfo *,
     203                 :            :                                                    SENTINEL2Level eLevel );
     204                 :            : 
     205                 :            :         static int Identify( GDALOpenInfo * );
     206                 :            : };
     207                 :            : 
     208                 :            : /************************************************************************/
     209                 :            : /* ==================================================================== */
     210                 :            : /*                         SENTINEL2AlphaBand                           */
     211                 :            : /* ==================================================================== */
     212                 :            : /************************************************************************/
     213                 :            : 
     214         [ -  + ]:          1 : class SENTINEL2AlphaBand: public VRTSourcedRasterBand
     215                 :            : {
     216                 :            :                     int m_nSaturatedVal;
     217                 :            :                     int m_nNodataVal;
     218                 :            : 
     219                 :            :     public:
     220                 :            :                      SENTINEL2AlphaBand( GDALDataset *poDS, int nBand,
     221                 :            :                                          GDALDataType eType,
     222                 :            :                                          int nXSize, int nYSize,
     223                 :            :                                          int nSaturatedVal, int nNodataVal );
     224                 :            : 
     225                 :            :     virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
     226                 :            :                               void *, int, int, GDALDataType,
     227                 :            : #ifdef GDAL_DCAP_RASTER
     228                 :            :                               GSpacing nPixelSpace, GSpacing nLineSpace,
     229                 :            :                               GDALRasterIOExtraArg* psExtraArg
     230                 :            : #else
     231                 :            :                               int nPixelSpace, int nLineSpace
     232                 :            : #endif
     233                 :            :                               ) override;
     234                 :            : };
     235                 :            : 
     236                 :            : /************************************************************************/
     237                 :            : /*                         SENTINEL2AlphaBand()                         */
     238                 :            : /************************************************************************/
     239                 :            : 
     240                 :          1 : SENTINEL2AlphaBand::SENTINEL2AlphaBand( GDALDataset *poDSIn, int nBandIn,
     241                 :            :                                         GDALDataType eType,
     242                 :            :                                         int nXSize, int nYSize,
     243                 :            :                                         int nSaturatedVal, int nNodataVal ) :
     244                 :            :     VRTSourcedRasterBand(poDSIn, nBandIn, eType,
     245                 :            :                          nXSize, nYSize),
     246                 :            :     m_nSaturatedVal(nSaturatedVal),
     247                 :          1 :     m_nNodataVal(nNodataVal)
     248                 :          1 : {}
     249                 :            : 
     250                 :            : /************************************************************************/
     251                 :            : /*                             IRasterIO()                              */
     252                 :            : /************************************************************************/
     253                 :            : 
     254                 :          1 : CPLErr SENTINEL2AlphaBand::IRasterIO( GDALRWFlag eRWFlag,
     255                 :            :                                       int nXOff, int nYOff, int nXSize, int nYSize,
     256                 :            :                                       void * pData, int nBufXSize, int nBufYSize,
     257                 :            :                                       GDALDataType eBufType,
     258                 :            : #ifdef GDAL_DCAP_RASTER
     259                 :            :                                       GSpacing nPixelSpace, GSpacing nLineSpace,
     260                 :            :                                       GDALRasterIOExtraArg* psExtraArg
     261                 :            : #else
     262                 :            :                                       int nPixelSpace, int nLineSpace
     263                 :            : #endif
     264                 :            :                                       )
     265                 :            : {
     266                 :            :     // Query the first band. Quite arbitrary, but hopefully all bands have
     267                 :            :     // the same nodata/saturated pixels.
     268                 :            :     CPLErr eErr = poDS->GetRasterBand(1)->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     269                 :            :                                             pData, nBufXSize, nBufYSize,
     270                 :            :                                             eBufType, nPixelSpace, nLineSpace
     271                 :            : #ifdef GDAL_DCAP_RASTER
     272                 :            :                                             ,psExtraArg
     273                 :            : #endif
     274                 :          1 :                                             );
     275         [ +  - ]:          1 :     if( eErr == CE_None )
     276                 :            :     {
     277                 :          1 :         const char* pszNBITS = GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
     278         [ +  - ]:          1 :         const int nBits = (pszNBITS) ? atoi(pszNBITS) : 16;
     279                 :          1 :         const GUInt16 nMaxVal = (GUInt16)((1 << nBits) - 1);
     280                 :            : 
     281                 :            :         // Replace pixels matching m_nSaturatedVal and m_nNodataVal by 0
     282                 :            :         // and others by the maxVal.
     283         [ +  + ]:          1 :         for(int iY = 0; iY < nBufYSize; iY ++)
     284                 :            :         {
     285         [ +  + ]:          1 :             for(int iX = 0; iX < nBufXSize; iX ++)
     286                 :            :             {
     287                 :            :                 // Optimized path for likely most common case
     288         [ +  + ]:          1 :                 if( eBufType == GDT_UInt16 )
     289                 :            :                 {
     290                 :            :                     GUInt16* panPtr = (GUInt16*)
     291                 :          1 :                            ((GByte*)pData + iY * nLineSpace + iX * nPixelSpace);
     292 [ -  + ][ #  # ]:          1 :                     if( *panPtr == 0 ||
                 [ #  # ]
     293                 :            :                         *panPtr == m_nSaturatedVal || *panPtr == m_nNodataVal )
     294                 :            :                     {
     295                 :          1 :                         *panPtr = 0;
     296                 :            :                     }
     297                 :            :                     else
     298                 :          1 :                         *panPtr = nMaxVal;
     299                 :            :                 }
     300                 :            :                 // Generic path for other datatypes
     301                 :            :                 else
     302                 :            :                 {
     303                 :            :                     double dfVal;
     304                 :          1 :                     GDALCopyWords((GByte*)pData + iY * nLineSpace + iX * nPixelSpace,
     305                 :            :                                    eBufType, 0,
     306                 :            :                                    &dfVal, GDT_Float64, 0,
     307                 :          1 :                                    1);
     308 [ -  + ][ #  # ]:          1 :                     if( dfVal == 0.0 || dfVal == m_nSaturatedVal ||
                 [ #  # ]
     309                 :            :                         dfVal == m_nNodataVal )
     310                 :            :                     {
     311                 :          1 :                         dfVal = 0;
     312                 :            :                     }
     313                 :            :                     else
     314                 :          0 :                         dfVal = nMaxVal;
     315                 :            :                     GDALCopyWords(&dfVal, GDT_Float64, 0,
     316                 :          1 :                                   (GByte*)pData + iY * nLineSpace + iX * nPixelSpace,
     317                 :            :                                   eBufType, 0,
     318                 :          1 :                                   1);
     319                 :            :                 }
     320                 :            :             }
     321                 :            :         }
     322                 :            :     }
     323                 :            : 
     324                 :          1 :     return eErr;
     325                 :            : }
     326                 :            : 
     327                 :            : /************************************************************************/
     328                 :            : /*                          SENTINEL2Dataset()                          */
     329                 :            : /************************************************************************/
     330                 :            : 
     331                 :          1 : SENTINEL2Dataset::SENTINEL2Dataset( int nXSize, int nYSize ) :
     332         [ +  - ]:          1 :     VRTDataset(nXSize, nYSize)
     333                 :            : {
     334                 :          1 :     poDriver = NULL;
     335         [ +  - ]:          1 :     SetWritable(FALSE);
     336                 :          1 : }
     337                 :            : 
     338                 :            : /************************************************************************/
     339                 :            : /*                         ~SENTINEL2Dataset()                          */
     340                 :            : /************************************************************************/
     341                 :            : 
     342 [ +  - ][ -  + ]:          1 : SENTINEL2Dataset::~SENTINEL2Dataset() {}
     343                 :            : 
     344                 :            : /************************************************************************/
     345                 :            : /*                            GetFileList()                             */
     346                 :            : /************************************************************************/
     347                 :            : 
     348                 :          1 : char** SENTINEL2Dataset::GetFileList()
     349                 :            : {
     350                 :          1 :     CPLStringList aosList;
     351 [ +  - ][ +  + ]:          1 :     for(size_t i=0;i<aosNonJP2Files.size();i++)
     352 [ +  - ][ +  - ]:          1 :         aosList.AddString(aosNonJP2Files[i]);
                 [ +  - ]
     353         [ +  - ]:          1 :     char** papszFileList = VRTDataset::GetFileList();
     354 [ +  - ][ +  + ]:          1 :     for(char** papszIter = papszFileList; papszIter && *papszIter; ++papszIter)
                 [ +  + ]
     355         [ +  - ]:          1 :         aosList.AddString(*papszIter);
     356         [ +  - ]:          1 :     CSLDestroy(papszFileList);
     357         [ +  - ]:          1 :     return aosList.StealList();
     358                 :            : }
     359                 :            : 
     360                 :            : /************************************************************************/
     361                 :            : /*                             Identify()                               */
     362                 :            : /************************************************************************/
     363                 :            : 
     364                 :          1 : int SENTINEL2Dataset::Identify( GDALOpenInfo *poOpenInfo )
     365                 :            : {
     366         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1B:") )
     367                 :          1 :         return TRUE;
     368         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1C:") )
     369                 :          1 :         return TRUE;
     370         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1C_TILE:") )
     371                 :          1 :         return TRUE;
     372         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L2A:") )
     373                 :          1 :         return TRUE;
     374                 :            : 
     375                 :          1 :     const char* pszJustFilename = CPLGetFilename(poOpenInfo->pszFilename);
     376                 :            : 
     377                 :            :     // We don't handle direct tile access for L1C SafeCompact products
     378                 :            :     // We could, but this isn't just done yet.
     379         [ -  + ]:          1 :     if( EQUAL( pszJustFilename, "MTD_TL.xml") )
     380                 :          0 :         return FALSE;
     381                 :            : 
     382                 :            :     /* Accept directly .zip as provided by https://scihub.esa.int/ */
     383    [ + ][ +  + ]:          1 :     if( (STARTS_WITH_CI(pszJustFilename, "S2A_OPER_PRD_MSI") ||
         [ + ][ +  +  + ]
                 [ +  + ]
     384                 :          1 :          STARTS_WITH_CI(pszJustFilename, "S2B_OPER_PRD_MSI") ||
     385                 :          1 :          STARTS_WITH_CI(pszJustFilename, "S2A_USER_PRD_MSI") ||
     386                 :          1 :          STARTS_WITH_CI(pszJustFilename, "S2B_USER_PRD_MSI") ) &&
     387                 :          1 :          EQUAL(CPLGetExtension(pszJustFilename), "zip") )
     388                 :            :     {
     389                 :          1 :         return TRUE;
     390                 :            :     }
     391                 :            : 
     392         [ +  + ]:          1 :     if( poOpenInfo->nHeaderBytes < 100 )
     393                 :          1 :         return FALSE;
     394                 :            : 
     395                 :          1 :     const char* pszHeader = reinterpret_cast<const char*>(poOpenInfo->pabyHeader);
     396                 :            : 
     397   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1B_User_Product" ) != NULL &&
     398                 :          1 :         strstr(pszHeader, "User_Product_Level-1B.xsd" ) != NULL )
     399                 :          1 :         return TRUE;
     400                 :            : 
     401   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1B_Granule_ID" ) != NULL &&
     402                 :          1 :         strstr(pszHeader, "S2_PDI_Level-1B_Granule_Metadata.xsd" ) != NULL )
     403                 :          1 :         return TRUE;
     404                 :            : 
     405   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1C_User_Product" ) != NULL &&
     406                 :          1 :         strstr(pszHeader, "User_Product_Level-1C.xsd" ) != NULL )
     407                 :          1 :         return TRUE;
     408                 :            : 
     409   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1C_Tile_ID" ) != NULL &&
     410                 :          1 :         strstr(pszHeader, "S2_PDI_Level-1C_Tile_Metadata.xsd" ) != NULL )
     411                 :          1 :         return TRUE;
     412                 :            : 
     413   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-2A_User_Product" ) != NULL &&
     414                 :          1 :         strstr(pszHeader, "User_Product_Level-2A" ) != NULL )
     415                 :          1 :         return TRUE;
     416                 :            : 
     417                 :          1 :     return FALSE;
     418                 :            : }
     419                 :            : 
     420                 :            : /************************************************************************/
     421                 :            : /*                         SENTINEL2_CPLXMLNodeHolder                   */
     422                 :            : /************************************************************************/
     423                 :            : 
     424                 :            : class SENTINEL2_CPLXMLNodeHolder
     425                 :            : {
     426                 :            :     CPLXMLNode* m_psNode;
     427                 :            :     public:
     428                 :          1 :         explicit SENTINEL2_CPLXMLNodeHolder(CPLXMLNode* psNode) : m_psNode(psNode) {}
     429         [ +  + ]:          1 :        ~SENTINEL2_CPLXMLNodeHolder() { if(m_psNode) CPLDestroyXMLNode(m_psNode); }
     430                 :            : 
     431                 :          1 :        CPLXMLNode* Release() {
     432                 :          1 :            CPLXMLNode* psRet = m_psNode;
     433                 :          1 :            m_psNode = NULL;
     434                 :          1 :            return psRet;
     435                 :            :        }
     436                 :            : };
     437                 :            : 
     438                 :            : /************************************************************************/
     439                 :            : /*                                Open()                                */
     440                 :            : /************************************************************************/
     441                 :            : 
     442                 :          1 : GDALDataset *SENTINEL2Dataset::Open( GDALOpenInfo * poOpenInfo )
     443                 :            : {
     444         [ +  + ]:          1 :     if ( !Identify( poOpenInfo ) )
     445                 :            :     {
     446                 :          1 :         return NULL;
     447                 :            :     }
     448                 :            : 
     449         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1B:") )
     450                 :          1 :         return OpenL1BSubdataset(poOpenInfo);
     451                 :            : 
     452         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1C:") )
     453                 :          1 :         return OpenL1C_L2ASubdataset(poOpenInfo, SENTINEL2_L1C);
     454                 :            : 
     455         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1C_TILE:") )
     456                 :          1 :         return OpenL1CTileSubdataset(poOpenInfo);
     457                 :            : 
     458         [ +  + ]:          1 :     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L2A:") )
     459                 :          1 :         return OpenL1C_L2ASubdataset(poOpenInfo, SENTINEL2_L2A);
     460                 :            : 
     461                 :          1 :     const char* pszJustFilename = CPLGetFilename(poOpenInfo->pszFilename);
     462 [ +  + ][ +  - ]:          1 :     if( (STARTS_WITH_CI(pszJustFilename, "S2A_OPER_PRD_MSI") ||
                 [ +  - ]
           [ -  +  +  - ]
                 [ +  + ]
     463                 :          1 :          STARTS_WITH_CI(pszJustFilename, "S2B_OPER_PRD_MSI") ||
     464                 :          1 :          STARTS_WITH_CI(pszJustFilename, "S2A_USER_PRD_MSI") ||
     465                 :          1 :          STARTS_WITH_CI(pszJustFilename, "S2B_USER_PRD_MSI") ) &&
     466                 :          1 :          EQUAL(CPLGetExtension(pszJustFilename), "zip") )
     467                 :            :     {
     468                 :          1 :         CPLString osBasename(CPLGetBasename(pszJustFilename));
     469         [ +  - ]:          1 :         CPLString osFilename(poOpenInfo->pszFilename);
     470         [ +  - ]:          1 :         CPLString osMTD(osBasename);
     471         [ +  - ]:          1 :         osMTD[9] = 'M';
     472         [ +  - ]:          1 :         osMTD[10] = 'T';
     473         [ +  - ]:          1 :         osMTD[11] = 'D';
     474         [ +  - ]:          1 :         osMTD[13] = 'S';
     475         [ +  - ]:          1 :         osMTD[14] = 'A';
     476         [ +  - ]:          1 :         osMTD[15] = 'F';
     477 [ +  - ][ +  - ]:          1 :         CPLString osSAFE(CPLString(osBasename) + ".SAFE");
         [ +  - ][ +  - ]
                 [ +  - ]
     478 [ +  - ][ +  - ]:          1 :         osFilename = osFilename + "/" + osSAFE +"/" + osMTD + ".xml";
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     479 [ +  - ][ +  - ]:          1 :         if( strncmp(osFilename, "/vsizip/", strlen("/vsizip/")) != 0 )
     480 [ +  - ][ +  - ]:          1 :             osFilename = "/vsizip/" + osFilename;
         [ +  - ][ +  - ]
                 [ +  - ]
     481 [ +  - ][ +  - ]:          1 :         CPLDebug("SENTINEL2", "Trying %s", osFilename.c_str());
     482 [ +  - ][ +  - ]:          1 :         GDALOpenInfo oOpenInfo(osFilename, GA_ReadOnly);
     483 [ +  - ][ +  - ]:          1 :         return Open(&oOpenInfo);
         [ +  - ][ +  - ]
                 [ +  - ]
     484                 :            :     }
     485                 :            : 
     486                 :          1 :     const char* pszHeader = reinterpret_cast<const char*>(poOpenInfo->pabyHeader);
     487                 :            : 
     488   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1B_User_Product" ) != NULL &&
     489                 :          1 :         strstr(pszHeader, "User_Product_Level-1B.xsd" ) != NULL )
     490                 :            :     {
     491                 :          1 :         return OpenL1BUserProduct(poOpenInfo);
     492                 :            :     }
     493                 :            : 
     494   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1B_Granule_ID" ) != NULL &&
     495                 :          1 :         strstr(pszHeader, "S2_PDI_Level-1B_Granule_Metadata.xsd" ) != NULL )
     496                 :            :     {
     497                 :          1 :         return OpenL1BGranule(poOpenInfo->pszFilename);
     498                 :            :     }
     499                 :            : 
     500   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1C_User_Product" ) != NULL &&
     501                 :          1 :         strstr(pszHeader, "User_Product_Level-1C.xsd" ) != NULL )
     502                 :            :     {
     503                 :          1 :         return OpenL1C_L2A(poOpenInfo->pszFilename, SENTINEL2_L1C);
     504                 :            :     }
     505                 :            : 
     506   [ +  +  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-1C_Tile_ID" ) != NULL &&
     507                 :          1 :         strstr(pszHeader, "S2_PDI_Level-1C_Tile_Metadata.xsd" ) != NULL )
     508                 :          1 :         return OpenL1CTile(poOpenInfo->pszFilename);
     509                 :            : 
     510   [ +  -  +  - ]:          1 :     if( strstr(pszHeader,  "<n1:Level-2A_User_Product" ) != NULL &&
     511                 :          1 :         strstr(pszHeader, "User_Product_Level-2A" ) != NULL )
     512                 :          1 :         return OpenL1C_L2A(poOpenInfo->pszFilename, SENTINEL2_L2A);
     513                 :            : 
     514                 :          1 :     return NULL;
     515                 :            : }
     516                 :            : 
     517                 :            : /************************************************************************/
     518                 :            : /*                        SENTINEL2GetBandDesc()                        */
     519                 :            : /************************************************************************/
     520                 :            : 
     521                 :          1 : static const SENTINEL2BandDescription* SENTINEL2GetBandDesc(const char* pszBandName)
     522                 :            : {
     523         [ +  + ]:          1 :     for(size_t i=0; i < NB_BANDS; i++)
     524                 :            :     {
     525         [ +  + ]:          1 :         if( EQUAL(asBandDesc[i].pszBandName, pszBandName) )
     526                 :          1 :             return &(asBandDesc[i]);
     527                 :            :     }
     528                 :          1 :     return NULL;
     529                 :            : }
     530                 :            : 
     531                 :            : /************************************************************************/
     532                 :            : /*                       SENTINEL2GetL2ABandDesc()                      */
     533                 :            : /************************************************************************/
     534                 :            : 
     535                 :          1 : static const SENTINEL2_L2A_BandDescription* SENTINEL2GetL2ABandDesc(const char* pszBandName)
     536                 :            : {
     537         [ +  + ]:          1 :     for(size_t i=0; i < NB_L2A_BANDS; i++)
     538                 :            :     {
     539         [ +  + ]:          1 :         if( EQUAL(asL2ABandDesc[i].pszBandName, pszBandName) )
     540                 :          1 :             return &(asL2ABandDesc[i]);
     541                 :            :     }
     542                 :          1 :     return NULL;
     543                 :            : }
     544                 :            : 
     545                 :            : /************************************************************************/
     546                 :            : /*                        SENTINEL2GetGranuleInfo()                     */
     547                 :            : /************************************************************************/
     548                 :            : 
     549                 :          1 : static bool SENTINEL2GetGranuleInfo(SENTINEL2Level eLevel,
     550                 :            :                                     const CPLString& osGranuleMTDPath,
     551                 :            :                                     int nDesiredResolution,
     552                 :            :                                     int* pnEPSGCode = NULL,
     553                 :            :                                     double* pdfULX = NULL,
     554                 :            :                                     double* pdfULY = NULL,
     555                 :            :                                     int* pnResolution = NULL,
     556                 :            :                                     int* pnWidth = NULL,
     557                 :            :                                     int* pnHeight = NULL)
     558                 :            : {
     559                 :            :     static bool bTryOptimization = true;
     560                 :          1 :     CPLXMLNode *psRoot = NULL;
     561                 :            : 
     562         [ +  - ]:          1 :     if( bTryOptimization )
     563                 :            :     {
     564                 :            :         /* Small optimization: in practice the interesting info are in the */
     565                 :            :         /* first bytes of the Granule MTD, which can be very long sometimes */
     566                 :            :         /* so only read them, and hack the buffer a bit to form a valid XML */
     567                 :            :         char szBuffer[3072];
     568                 :          1 :         VSILFILE* fp = VSIFOpenL( osGranuleMTDPath, "rb" );
     569                 :          1 :         size_t nRead = 0;
     570 [ +  + ][ -  + ]:          1 :         if( fp == NULL ||
                 [ +  + ]
     571                 :            :             (nRead = VSIFReadL( szBuffer, 1, sizeof(szBuffer)-1, fp )) == 0 )
     572                 :            :         {
     573         [ -  + ]:          1 :             if( fp )
     574                 :          0 :                 VSIFCloseL(fp);
     575                 :            :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s",
     576                 :          1 :                      osGranuleMTDPath.c_str());
     577                 :          1 :             return false;
     578                 :            :         }
     579                 :          1 :         szBuffer[nRead] = 0;
     580                 :          1 :         VSIFCloseL(fp);
     581                 :          1 :         char* pszTileGeocoding = strstr(szBuffer, "</Tile_Geocoding>");
     582 [ +  + ][ +  -  :          1 :         if( eLevel == SENTINEL2_L1C &&
             +  -  +  - ]
                 [ +  - ]
     583                 :            :             pszTileGeocoding != NULL &&
     584                 :          1 :             strstr(szBuffer, "<n1:Level-1C_Tile_ID") != NULL &&
     585                 :          1 :             strstr(szBuffer, "<n1:Geometric_Info") != NULL &&
     586                 :            :             static_cast<size_t>(pszTileGeocoding - szBuffer) <
     587                 :            :                 sizeof(szBuffer) - strlen("</Tile_Geocoding></n1:Geometric_Info></n1:Level-1C_Tile_ID>") - 1 )
     588                 :            :         {
     589                 :            :             strcpy(pszTileGeocoding,
     590                 :          1 :                 "</Tile_Geocoding></n1:Geometric_Info></n1:Level-1C_Tile_ID>");
     591                 :          1 :             psRoot = CPLParseXMLString( szBuffer );
     592                 :            :         }
     593 [ +  - ][ +  -  :          1 :         else if( eLevel == SENTINEL2_L2A &&
             +  -  +  - ]
                 [ +  - ]
     594                 :            :             pszTileGeocoding != NULL &&
     595                 :          1 :             strstr(szBuffer, "<n1:Level-2A_Tile_ID") != NULL &&
     596                 :          1 :             strstr(szBuffer, "<n1:Geometric_Info") != NULL &&
     597                 :            :             static_cast<size_t>(pszTileGeocoding - szBuffer) <
     598                 :            :                 sizeof(szBuffer) - strlen("</Tile_Geocoding></n1:Geometric_Info></n1:Level-2A_Tile_ID>") - 1 )
     599                 :            :         {
     600                 :            :             strcpy(pszTileGeocoding,
     601                 :          1 :                 "</Tile_Geocoding></n1:Geometric_Info></n1:Level-2A_Tile_ID>");
     602                 :          1 :             psRoot = CPLParseXMLString( szBuffer );
     603                 :            :         }
     604                 :            :         else
     605                 :          1 :             bTryOptimization = false;
     606                 :            :     }
     607                 :            : 
     608                 :            :     // If the above doesn't work, then read the whole file...
     609         [ -  + ]:          1 :     if( psRoot == NULL )
     610                 :          0 :         psRoot = CPLParseXMLFile( osGranuleMTDPath );
     611         [ -  + ]:          1 :     if( psRoot == NULL )
     612                 :            :     {
     613                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot XML parse %s",
     614                 :          0 :                  osGranuleMTDPath.c_str());
     615                 :          0 :         return false;
     616                 :            :     }
     617                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
     618         [ +  - ]:          1 :     CPLStripXMLNamespace(psRoot, NULL, TRUE);
     619                 :            : 
     620                 :            :     const char* pszNodePath =
     621                 :            :         (eLevel == SENTINEL2_L1C ) ?
     622                 :            :              "=Level-1C_Tile_ID.Geometric_Info.Tile_Geocoding" :
     623         [ +  + ]:          1 :              "=Level-2A_Tile_ID.Geometric_Info.Tile_Geocoding";
     624         [ +  - ]:          1 :     CPLXMLNode* psTileGeocoding = CPLGetXMLNode(psRoot, pszNodePath);
     625         [ -  + ]:          1 :     if( psTileGeocoding == NULL )
     626                 :            :     {
     627                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s in %s",
     628                 :            :                  pszNodePath,
     629 [ #  # ][ #  # ]:          0 :                  osGranuleMTDPath.c_str());
     630                 :          0 :         return false;
     631                 :            :     }
     632                 :            : 
     633         [ +  - ]:          1 :     const char* pszCSCode = CPLGetXMLValue(psTileGeocoding, "HORIZONTAL_CS_CODE", NULL);
     634         [ -  + ]:          1 :     if( pszCSCode == NULL )
     635                 :            :     {
     636                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s in %s",
     637                 :            :                  "HORIZONTAL_CS_CODE",
     638 [ #  # ][ #  # ]:          0 :                  osGranuleMTDPath.c_str());
     639                 :          0 :         return false;
     640                 :            :     }
     641         [ -  + ]:          1 :     if( !STARTS_WITH_CI(pszCSCode, "EPSG:") )
     642                 :            :     {
     643                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid CS code (%s) for %s",
     644                 :            :                  pszCSCode,
     645 [ #  # ][ #  # ]:          0 :                  osGranuleMTDPath.c_str());
     646                 :          0 :         return false;
     647                 :            :     }
     648                 :          1 :     int nEPSGCode = atoi(pszCSCode + strlen("EPSG:"));
     649         [ +  - ]:          1 :     if( pnEPSGCode != NULL )
     650                 :          1 :         *pnEPSGCode = nEPSGCode;
     651                 :            : 
     652         [ +  + ]:          1 :     for(CPLXMLNode* psIter = psTileGeocoding->psChild; psIter != NULL;
     653                 :            :                                                        psIter = psIter->psNext)
     654                 :            :     {
     655         [ +  + ]:          1 :         if( psIter->eType != CXT_Element )
     656                 :          1 :             continue;
     657         [ +  + ]:          1 :         if( EQUAL(psIter->pszValue, "Size") &&
           [ +  +  +  + ]
                 [ +  + ]
     658                 :            :             (nDesiredResolution == 0 ||
     659         [ +  - ]:          1 :              atoi(CPLGetXMLValue(psIter, "resolution", "")) == nDesiredResolution) )
     660                 :            :         {
     661         [ +  - ]:          1 :             nDesiredResolution = atoi(CPLGetXMLValue(psIter, "resolution", ""));
     662         [ +  - ]:          1 :             const char* pszRows = CPLGetXMLValue(psIter, "NROWS", NULL);
     663         [ -  + ]:          1 :             if( pszRows == NULL )
     664                 :            :             {
     665                 :            :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s in %s",
     666                 :            :                         "NROWS",
     667 [ #  # ][ #  # ]:          0 :                         osGranuleMTDPath.c_str());
     668                 :          0 :                 return false;
     669                 :            :             }
     670         [ +  - ]:          1 :             const char* pszCols = CPLGetXMLValue(psIter, "NCOLS", NULL);
     671         [ -  + ]:          1 :             if( pszCols == NULL )
     672                 :            :             {
     673                 :            :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s in %s",
     674                 :            :                         "NCOLS",
     675 [ #  # ][ #  # ]:          0 :                         osGranuleMTDPath.c_str());
     676                 :          0 :                 return false;
     677                 :            :             }
     678         [ +  + ]:          1 :             if( pnResolution )
     679                 :          1 :                 *pnResolution = nDesiredResolution;
     680         [ +  + ]:          1 :             if( pnWidth )
     681                 :          1 :                 *pnWidth = atoi(pszCols);
     682         [ +  + ]:          1 :             if( pnHeight )
     683                 :          1 :                 *pnHeight = atoi(pszRows);
     684                 :            :         }
     685         [ +  + ]:          1 :         else if( EQUAL(psIter->pszValue, "Geoposition") &&
           [ +  -  +  + ]
                 [ +  + ]
     686                 :            :                  (nDesiredResolution == 0 ||
     687         [ +  - ]:          1 :                   atoi(CPLGetXMLValue(psIter, "resolution", "")) == nDesiredResolution) )
     688                 :            :         {
     689         [ +  - ]:          1 :             nDesiredResolution = atoi(CPLGetXMLValue(psIter, "resolution", ""));
     690         [ +  - ]:          1 :             const char* pszULX = CPLGetXMLValue(psIter, "ULX", NULL);
     691         [ -  + ]:          1 :             if( pszULX == NULL )
     692                 :            :             {
     693                 :            :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s in %s",
     694                 :            :                         "ULX",
     695 [ #  # ][ #  # ]:          0 :                         osGranuleMTDPath.c_str());
     696                 :          0 :                 return false;
     697                 :            :             }
     698         [ +  - ]:          1 :             const char* pszULY = CPLGetXMLValue(psIter, "ULY", NULL);
     699         [ -  + ]:          1 :             if( pszULY == NULL )
     700                 :            :             {
     701                 :            :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s in %s",
     702                 :            :                         "ULY",
     703 [ #  # ][ #  # ]:          0 :                         osGranuleMTDPath.c_str());
     704                 :          0 :                 return false;
     705                 :            :             }
     706         [ +  + ]:          1 :             if( pnResolution )
     707                 :          1 :                 *pnResolution = nDesiredResolution;
     708         [ +  + ]:          1 :             if( pdfULX )
     709         [ +  - ]:          1 :                 *pdfULX = CPLAtof(pszULX);
     710         [ +  + ]:          1 :             if( pdfULY )
     711         [ +  - ]:          1 :                 *pdfULY = CPLAtof(pszULY);
     712                 :            :         }
     713                 :            :     }
     714                 :            : 
     715                 :          1 :     return true;
     716                 :            : }
     717                 :            : 
     718                 :            : /************************************************************************/
     719                 :            : /*                      SENTINEL2GetPathSeparator()                     */
     720                 :            : /************************************************************************/
     721                 :            : 
     722                 :            : // For the sake of simplifying our unit tests, we limit the use of \\ to when
     723                 :            : // it is strictly necessary. Otherwise we could use CPLFormFilename()...
     724                 :          1 : static char SENTINEL2GetPathSeparator(const char* pszBasename)
     725                 :            : {
     726         [ -  + ]:          1 :     if( STARTS_WITH_CI(pszBasename, "\\\\?\\") )
     727                 :          0 :         return '\\';
     728                 :            :     else
     729                 :          1 :         return '/';
     730                 :            : }
     731                 :            : 
     732                 :            : /************************************************************************/
     733                 :            : /*                      SENTINEL2GetGranuleList()                       */
     734                 :            : /************************************************************************/
     735                 :            : 
     736                 :          1 : static bool SENTINEL2GetGranuleList(CPLXMLNode* psMainMTD,
     737                 :            :                                     SENTINEL2Level eLevel,
     738                 :            :                                     const char* pszFilename,
     739                 :            :                                     std::vector<CPLString>& osList,
     740                 :            :                                     std::set<int>* poSetResolutions = NULL,
     741                 :            :                                     std::map<int, std::set<CPLString> >*
     742                 :            :                                                 poMapResolutionsToBands = NULL)
     743                 :            : {
     744                 :            :     const char* pszNodePath =
     745                 :            :         (eLevel == SENTINEL2_L1B ) ? "Level-1B_User_Product" :
     746                 :            :         (eLevel == SENTINEL2_L1C ) ? "Level-1C_User_Product" :
     747 [ +  + ][ +  + ]:          1 :                                      "Level-2A_User_Product";
     748                 :            : 
     749                 :            :     CPLXMLNode* psRoot =  CPLGetXMLNode(psMainMTD,
     750                 :          1 :                                         CPLSPrintf("=%s", pszNodePath));
     751         [ -  + ]:          1 :     if( psRoot == NULL )
     752                 :            :     {
     753                 :          0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find =%s", pszNodePath);
     754                 :          0 :         return false;
     755                 :            :     }
     756                 :            :     pszNodePath = (eLevel == SENTINEL2_L2A) ?
     757         [ +  + ]:          1 :             "General_Info.L2A_Product_Info" : "General_Info.Product_Info";
     758                 :          1 :     CPLXMLNode* psProductInfo = CPLGetXMLNode(psRoot, pszNodePath);
     759         [ +  + ]:          1 :     if( psProductInfo == NULL )
     760                 :            :     {
     761                 :          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", pszNodePath);
     762                 :          1 :         return false;
     763                 :            :     }
     764                 :            : 
     765                 :            :     pszNodePath = (eLevel == SENTINEL2_L2A) ?
     766         [ +  + ]:          1 :             "L2A_Product_Organisation" : "Product_Organisation";
     767                 :            :     CPLXMLNode* psProductOrganisation =
     768                 :          1 :                         CPLGetXMLNode(psProductInfo, pszNodePath);
     769         [ +  + ]:          1 :     if( psProductOrganisation == NULL )
     770                 :            :     {
     771                 :          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", pszNodePath);
     772                 :          1 :         return false;
     773                 :            :     }
     774                 :            : 
     775                 :          1 :     CPLString osDirname( CPLGetDirname(pszFilename) );
     776                 :            : #ifdef HAVE_READLINK
     777                 :            :     char szPointerFilename[2048];
     778                 :            :     int nBytes = static_cast<int>(readlink(pszFilename, szPointerFilename,
     779                 :          1 :                                            sizeof(szPointerFilename)));
     780         [ -  + ]:          1 :     if (nBytes != -1)
     781                 :            :     {
     782                 :            :         const int nOffset =
     783         [ #  # ]:          0 :             std::min(nBytes, static_cast<int>(sizeof(szPointerFilename)-1));
     784                 :          0 :         szPointerFilename[nOffset] = '\0';
     785 [ #  # ][ #  # ]:          0 :         osDirname = CPLGetDirname(szPointerFilename);
         [ #  # ][ #  # ]
     786                 :            :     }
     787                 :            : #endif
     788                 :            : 
     789         [ +  - ]:          1 :     std::set<CPLString> aoSetGranuleId;
     790         [ +  + ]:          1 :     for(CPLXMLNode* psIter = psProductOrganisation->psChild; psIter != NULL;
     791                 :            :                                                     psIter = psIter->psNext )
     792                 :            :     {
     793 [ +  - ][ -  + ]:          1 :         if( psIter->eType != CXT_Element ||
     794                 :          1 :             !EQUAL(psIter->pszValue, "Granule_List") )
     795                 :            :         {
     796                 :          0 :             continue;
     797                 :            :         }
     798         [ +  + ]:          1 :         for(CPLXMLNode* psIter2 = psIter->psChild; psIter2 != NULL;
     799                 :            :                                                      psIter2 = psIter2->psNext )
     800                 :            :         {
     801 [ +  + ][ -  + ]:          1 :             if( psIter2->eType != CXT_Element ||
     802                 :          1 :                 !EQUAL(psIter2->pszValue, "Granules") )
     803                 :            :             {
     804                 :          1 :                 continue;
     805                 :            :             }
     806         [ +  - ]:          1 :             const char* pszGranuleId = CPLGetXMLValue(psIter2, "granuleIdentifier", NULL);
     807         [ +  + ]:          1 :             if( pszGranuleId == NULL )
     808                 :            :             {
     809         [ +  - ]:          1 :                 CPLDebug("SENTINEL2", "Missing granuleIdentifier attribute");
     810                 :          1 :                 continue;
     811                 :            :             }
     812                 :            : 
     813         [ +  + ]:          1 :             if( eLevel == SENTINEL2_L2A )
     814                 :            :             {
     815         [ +  + ]:          1 :                 for(CPLXMLNode* psIter3 = psIter2->psChild; psIter3 != NULL;
     816                 :            :                                                      psIter3 = psIter3->psNext )
     817                 :            :                 {
     818 [ +  + ][ -  + ]:          1 :                     if( psIter3->eType != CXT_Element ||
     819                 :          1 :                         !EQUAL(psIter3->pszValue, "IMAGE_ID_2A") )
     820                 :            :                     {
     821                 :          1 :                         continue;
     822                 :            :                     }
     823         [ +  - ]:          1 :                     const char* pszTileName = CPLGetXMLValue(psIter3, NULL, "");
     824                 :          1 :                     size_t nLen = strlen(pszTileName);
     825 [ +  - ][ +  - ]:          1 :                     if( nLen > 4 && pszTileName[nLen-4] == '_' &&
                 [ +  - ]
     826                 :          1 :                         pszTileName[nLen-1] == 'm' )
     827                 :            :                     {
     828                 :          1 :                         int nResolution = atoi(pszTileName + nLen - 3);
     829         [ +  + ]:          1 :                         if( poSetResolutions != NULL )
     830         [ +  - ]:          1 :                             (*poSetResolutions).insert(nResolution);
     831         [ +  - ]:          1 :                         if( poMapResolutionsToBands != NULL )
     832                 :            :                         {
     833                 :          1 :                             nLen -= 4;
     834 [ +  - ][ +  + ]:          1 :                             if( nLen > 4 && pszTileName[nLen-4] == '_' &&
                 [ +  - ]
     835                 :          1 :                                 pszTileName[nLen-3] == 'B' )
     836                 :            :                             {
     837         [ +  - ]:          1 :                                 (*poMapResolutionsToBands)[nResolution].
     838 [ +  - ][ +  - ]:          1 :                                     insert(CPLString(pszTileName).substr(nLen-2,2));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     839                 :            :                             }
     840 [ +  - ][ +  - ]:          1 :                             else if ( nLen > strlen("S2A_USER_MSI_") &&
         [ +  - ][ +  - ]
     841                 :          1 :                                       pszTileName[8] == '_' &&
     842                 :          1 :                                       pszTileName[12] == '_' &&
     843                 :          1 :                                       !EQUALN(pszTileName+9, "MSI", 3) )
     844                 :            :                             {
     845         [ +  - ]:          1 :                                 (*poMapResolutionsToBands)[nResolution].
     846 [ +  - ][ +  - ]:          1 :                                     insert(CPLString(pszTileName).substr(9,3));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     847                 :            :                             }
     848                 :            :                         }
     849                 :            :                     }
     850                 :            :                 }
     851                 :            :             }
     852                 :            : 
     853                 :            :             // For L2A we can have several time the same granuleIdentifier
     854                 :            :             // for the different resolutions
     855 [ +  - ][ +  - ]:          1 :             if( aoSetGranuleId.find(pszGranuleId) != aoSetGranuleId.end() )
         [ +  - ][ +  - ]
         [ +  - ][ -  + ]
     856                 :          0 :                 continue;
     857 [ +  - ][ +  - ]:          1 :             aoSetGranuleId.insert(pszGranuleId);
                 [ +  - ]
     858                 :            : 
     859                 :            :             /* S2A_OPER_MSI_L1C_TL_SGS__20151024T023555_A001758_T53JLJ_N01.04 --> */
     860                 :            :             /* S2A_OPER_MTD_L1C_TL_SGS__20151024T023555_A001758_T53JLJ */
     861         [ +  - ]:          1 :             CPLString osGranuleMTD = pszGranuleId;
     862 [ +  - ][ +  + ]:          1 :             if( osGranuleMTD.size() > strlen("S2A_OPER_MSI_") &&
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  + ]
     863 [ +  - ][ +  - ]:          1 :                 osGranuleMTD[8] == '_' && osGranuleMTD[12] == '_' &&
     864 [ +  - ][ +  - ]:          1 :                 osGranuleMTD[osGranuleMTD.size()-7] == '_' &&
     865 [ +  - ][ +  - ]:          1 :                 osGranuleMTD[osGranuleMTD.size()-6] == 'N' )
     866                 :            :             {
     867         [ +  - ]:          1 :                 osGranuleMTD[9] = 'M';
     868         [ +  - ]:          1 :                 osGranuleMTD[10] = 'T';
     869         [ +  - ]:          1 :                 osGranuleMTD[11] = 'D';
     870 [ +  - ][ +  - ]:          1 :                 osGranuleMTD.resize(osGranuleMTD.size()-7);
     871                 :            :             }
     872                 :            :             else
     873                 :            :             {
     874         [ +  - ]:          1 :                 CPLDebug("SENTINEL2", "Invalid granule ID: %s", pszGranuleId);
     875                 :          1 :                 continue;
     876                 :            :             }
     877         [ +  - ]:          1 :             osGranuleMTD += ".xml";
     878                 :            : 
     879         [ +  - ]:          1 :             const char chSeparator = SENTINEL2GetPathSeparator(osDirname);
     880         [ +  - ]:          1 :             CPLString osGranuleMTDPath = osDirname;
     881         [ +  - ]:          1 :             osGranuleMTDPath += chSeparator;
     882         [ +  - ]:          1 :             osGranuleMTDPath += "GRANULE";
     883         [ +  - ]:          1 :             osGranuleMTDPath += chSeparator;
     884         [ +  - ]:          1 :             osGranuleMTDPath += pszGranuleId;
     885         [ +  - ]:          1 :             osGranuleMTDPath += chSeparator;
     886         [ +  - ]:          1 :             osGranuleMTDPath += osGranuleMTD;
     887         [ +  - ]:          1 :             osList.push_back(osGranuleMTDPath);
     888 [ +  - ][ +  - ]:          1 :         }
     889                 :            :     }
     890                 :            : 
     891         [ +  - ]:          1 :     return true;
     892                 :            : }
     893                 :            : 
     894                 :            : /************************************************************************/
     895                 :            : /*                     SENTINEL2GetUserProductMetadata()                */
     896                 :            : /************************************************************************/
     897                 :            : 
     898                 :            : static
     899                 :          1 : char** SENTINEL2GetUserProductMetadata( CPLXMLNode* psMainMTD,
     900                 :            :                                     const char* pszRootNode )
     901                 :            : {
     902                 :          1 :     CPLStringList aosList;
     903                 :            : 
     904                 :            :     CPLXMLNode* psRoot =  CPLGetXMLNode(psMainMTD,
     905 [ +  - ][ +  - ]:          1 :                                         CPLSPrintf("=%s", pszRootNode));
     906         [ -  + ]:          1 :     if( psRoot == NULL )
     907                 :            :     {
     908         [ #  # ]:          0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find =%s", pszRootNode);
     909                 :          0 :         return NULL;
     910                 :            :     }
     911                 :            :     CPLXMLNode* psProductInfo = CPLGetXMLNode(psRoot,
     912                 :          1 :         EQUAL(pszRootNode, "Level-2A_User_Product") ?
     913 [ +  + ][ +  - ]:          1 :             "General_Info.L2A_Product_Info" : "General_Info.Product_Info");
     914                 :          1 :     int nDataTakeCounter = 1;
     915 [ +  - ][ +  + ]:          1 :     for( CPLXMLNode* psIter = (psProductInfo ? psProductInfo->psChild : NULL);
     916                 :            :                      psIter != NULL;
     917                 :            :                      psIter = psIter->psNext )
     918                 :            :     {
     919         [ -  + ]:          1 :         if( psIter->eType != CXT_Element )
     920                 :          0 :             continue;
     921 [ +  + ][ +  + ]:          1 :         if( psIter->psChild != NULL && psIter->psChild->eType == CXT_Text )
     922                 :            :         {
     923                 :            :             aosList.AddNameValue( psIter->pszValue,
     924         [ +  - ]:          1 :                                   psIter->psChild->pszValue );
     925                 :            :         }
     926         [ +  + ]:          1 :         else if( EQUAL(psIter->pszValue, "Datatake") )
     927                 :            :         {
     928 [ +  - ][ +  - ]:          1 :             CPLString osPrefix(CPLSPrintf("DATATAKE_%d_", nDataTakeCounter));
     929                 :          1 :             nDataTakeCounter ++;
     930         [ +  - ]:          1 :             const char* pszId = CPLGetXMLValue(psIter, "datatakeIdentifier", NULL);
     931         [ +  - ]:          1 :             if( pszId )
     932 [ +  - ][ +  - ]:          1 :                 aosList.AddNameValue( (osPrefix + "ID").c_str(), pszId );
         [ +  - ][ +  - ]
     933         [ +  + ]:          1 :             for( CPLXMLNode* psIter2 = psIter->psChild;
     934                 :            :                      psIter2 != NULL;
     935                 :            :                      psIter2 = psIter2->psNext )
     936                 :            :             {
     937         [ +  + ]:          1 :                 if( psIter2->eType != CXT_Element )
     938                 :          1 :                     continue;
     939 [ +  - ][ +  - ]:          1 :                 if( psIter2->psChild != NULL && psIter2->psChild->eType == CXT_Text )
     940                 :            :                 {
     941                 :            :                     aosList.AddNameValue( (osPrefix + psIter2->pszValue).c_str(),
     942 [ +  - ][ +  - ]:          1 :                                           psIter2->psChild->pszValue );
         [ +  - ][ +  - ]
     943                 :            :                 }
     944         [ +  - ]:          1 :             }
     945                 :            :         }
     946                 :            :     }
     947                 :            : 
     948                 :            :     CPLXMLNode* psIC = CPLGetXMLNode(psRoot,
     949                 :          1 :             EQUAL(pszRootNode, "Level-2A_User_Product") ?
     950                 :            :                 "General_Info.L2A_Product_Image_Characteristics" :
     951 [ +  + ][ +  - ]:          1 :                 "General_Info.Product_Image_Characteristics");
     952         [ +  + ]:          1 :     if( psIC != NULL )
     953                 :            :     {
     954         [ +  + ]:          1 :         for( CPLXMLNode* psIter = psIC->psChild; psIter != NULL;
     955                 :            :                                                  psIter = psIter->psNext )
     956                 :            :         {
     957 [ +  + ][ +  + ]:          1 :             if( psIter->eType != CXT_Element ||
     958                 :          1 :                 !EQUAL(psIter->pszValue, "Special_Values") )
     959                 :            :             {
     960                 :          1 :                 continue;
     961                 :            :             }
     962         [ +  - ]:          1 :             const char* pszText = CPLGetXMLValue(psIter, "SPECIAL_VALUE_TEXT", NULL);
     963         [ +  - ]:          1 :             const char* pszIndex = CPLGetXMLValue(psIter, "SPECIAL_VALUE_INDEX", NULL);
     964 [ +  - ][ +  - ]:          1 :             if( pszText && pszIndex )
     965                 :            :             {
     966                 :            :                 aosList.AddNameValue( (CPLString("SPECIAL_VALUE_") + pszText).c_str(),
     967 [ +  - ][ +  - ]:          1 :                                        pszIndex );
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     968                 :            :             }
     969                 :            :         }
     970                 :            : 
     971                 :            :         const char* pszQuantValue =
     972         [ +  - ]:          1 :             CPLGetXMLValue(psIC, "QUANTIFICATION_VALUE", NULL);
     973         [ +  + ]:          1 :         if( pszQuantValue != NULL )
     974         [ +  - ]:          1 :             aosList.AddNameValue("QUANTIFICATION_VALUE", pszQuantValue);
     975                 :            : 
     976                 :            :         const char* pszRCU =
     977         [ +  - ]:          1 :             CPLGetXMLValue(psIC, "Reflectance_Conversion.U", NULL);
     978         [ +  + ]:          1 :         if( pszRCU != NULL )
     979         [ +  - ]:          1 :             aosList.AddNameValue("REFLECTANCE_CONVERSION_U", pszRCU);
     980                 :            : 
     981                 :            :         // L2A specific
     982         [ +  - ]:          1 :         CPLXMLNode* psQVL = CPLGetXMLNode(psIC, "L1C_L2A_Quantification_Values_List");
     983 [ +  + ][ +  + ]:          1 :         for( CPLXMLNode* psIter = psQVL ? psQVL->psChild : NULL; psIter != NULL;
     984                 :            :                                                  psIter = psIter->psNext )
     985                 :            :         {
     986         [ -  + ]:          1 :             if( psIter->eType != CXT_Element )
     987                 :            :             {
     988                 :          0 :                 continue;
     989                 :            :             }
     990                 :            :             aosList.AddNameValue( psIter->pszValue,
     991 [ +  - ][ +  - ]:          1 :                                   CPLGetXMLValue(psIter, NULL, NULL));
     992         [ +  - ]:          1 :             const char* pszUnit = CPLGetXMLValue(psIter, "unit", NULL);
     993         [ +  - ]:          1 :             if( pszUnit )
     994 [ +  - ][ +  - ]:          1 :                 aosList.AddNameValue( CPLSPrintf("%s_UNIT", psIter->pszValue), pszUnit);
     995                 :            :         }
     996                 :            : 
     997                 :            :         const char* pszRefBand =
     998         [ +  - ]:          1 :             CPLGetXMLValue(psIC, "REFERENCE_BAND", NULL);
     999         [ +  + ]:          1 :         if( pszRefBand != NULL )
    1000                 :            :         {
    1001                 :          1 :             int nIdx = atoi(pszRefBand);
    1002 [ +  - ][ +  - ]:          1 :             if( nIdx >= 0 && nIdx < (int)NB_BANDS )
    1003         [ +  - ]:          1 :                 aosList.AddNameValue("REFERENCE_BAND", asBandDesc[nIdx].pszBandName );
    1004                 :            :         }
    1005                 :            :     }
    1006                 :            : 
    1007         [ +  - ]:          1 :     CPLXMLNode* psQII = CPLGetXMLNode(psRoot, "Quality_Indicators_Info");
    1008         [ +  + ]:          1 :     if( psQII != NULL )
    1009                 :            :     {
    1010         [ +  - ]:          1 :         const char* pszCC = CPLGetXMLValue(psQII, "Cloud_Coverage_Assessment", NULL);
    1011         [ +  - ]:          1 :         if( pszCC )
    1012                 :            :             aosList.AddNameValue("CLOUD_COVERAGE_ASSESSMENT",
    1013         [ +  - ]:          1 :                                  pszCC);
    1014                 :            : 
    1015                 :            :         const char* pszDegradedAnc = CPLGetXMLValue(psQII,
    1016         [ +  - ]:          1 :             "Technical_Quality_Assessment.DEGRADED_ANC_DATA_PERCENTAGE", NULL);
    1017         [ +  - ]:          1 :         if( pszDegradedAnc )
    1018         [ +  - ]:          1 :             aosList.AddNameValue("DEGRADED_ANC_DATA_PERCENTAGE", pszDegradedAnc);
    1019                 :            : 
    1020                 :            :         const char* pszDegradedMSI = CPLGetXMLValue(psQII,
    1021         [ +  - ]:          1 :             "Technical_Quality_Assessment.DEGRADED_MSI_DATA_PERCENTAGE", NULL);
    1022         [ +  - ]:          1 :         if( pszDegradedMSI )
    1023         [ +  - ]:          1 :             aosList.AddNameValue("DEGRADED_MSI_DATA_PERCENTAGE", pszDegradedMSI);
    1024                 :            : 
    1025                 :            :         CPLXMLNode* psQualInspect = CPLGetXMLNode(psQII,
    1026         [ +  - ]:          1 :                             "Quality_Control_Checks.Quality_Inspections");
    1027 [ +  - ][ +  + ]:          1 :         for( CPLXMLNode* psIter = (psQualInspect ? psQualInspect->psChild : NULL);
    1028                 :            :                      psIter != NULL;
    1029                 :            :                      psIter = psIter->psNext )
    1030                 :            :         {
    1031         [ -  + ]:          1 :             if( psIter->eType != CXT_Element )
    1032                 :          0 :                 continue;
    1033 [ +  - ][ +  - ]:          1 :             if( psIter->psChild != NULL && psIter->psChild->eType == CXT_Text )
    1034                 :            :             {
    1035                 :            :                 aosList.AddNameValue( psIter->pszValue,
    1036         [ +  - ]:          1 :                                     psIter->psChild->pszValue );
    1037                 :            :             }
    1038                 :            :         }
    1039                 :            :     }
    1040                 :            : 
    1041         [ +  - ]:          1 :     CPLXMLNode* psL2A_QII = CPLGetXMLNode(psRoot, "L2A_Quality_Indicators_Info");
    1042         [ +  + ]:          1 :     if( psL2A_QII != NULL )
    1043                 :            :     {
    1044         [ +  - ]:          1 :         CPLXMLNode* psICCQI = CPLGetXMLNode(psL2A_QII, "Image_Content_QI");
    1045 [ +  - ][ +  + ]:          1 :         for( CPLXMLNode* psIter = (psICCQI ? psICCQI->psChild : NULL);
    1046                 :            :                     psIter != NULL;
    1047                 :            :                     psIter = psIter->psNext )
    1048                 :            :         {
    1049         [ -  + ]:          1 :             if( psIter->eType != CXT_Element )
    1050                 :          0 :                 continue;
    1051 [ +  - ][ +  - ]:          1 :             if( psIter->psChild != NULL && psIter->psChild->eType == CXT_Text )
    1052                 :            :             {
    1053                 :            :                 aosList.AddNameValue( psIter->pszValue,
    1054         [ +  - ]:          1 :                                     psIter->psChild->pszValue );
    1055                 :            :             }
    1056                 :            :         }
    1057                 :            :     }
    1058                 :            : 
    1059         [ +  - ]:          1 :     return aosList.StealList();
    1060                 :            : }
    1061                 :            : 
    1062                 :            : /************************************************************************/
    1063                 :            : /*                        SENTINEL2GetResolutionSet()                   */
    1064                 :            : /************************************************************************/
    1065                 :            : 
    1066                 :          1 : static bool SENTINEL2GetResolutionSet(CPLXMLNode* psProductInfo,
    1067                 :            :                                       std::set<int>& oSetResolutions,
    1068                 :            :                                       std::map<int, std::set<CPLString> >&
    1069                 :            :                                                         oMapResolutionsToBands)
    1070                 :            : {
    1071                 :            : 
    1072                 :            :     CPLXMLNode* psBandList = CPLGetXMLNode(psProductInfo,
    1073                 :          1 :                                            "Query_Options.Band_List");
    1074         [ +  + ]:          1 :     if( psBandList == NULL )
    1075                 :            :     {
    1076                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    1077                 :          1 :                  "Query_Options.Band_List");
    1078                 :          1 :         return false;
    1079                 :            :     }
    1080                 :            : 
    1081         [ +  + ]:          1 :     for(CPLXMLNode* psIter = psBandList->psChild; psIter != NULL;
    1082                 :            :                                                   psIter = psIter->psNext )
    1083                 :            :     {
    1084 [ +  - ][ -  + ]:          1 :         if( psIter->eType != CXT_Element ||
    1085                 :          1 :             !EQUAL(psIter->pszValue, "BAND_NAME") )
    1086                 :            :         {
    1087                 :          0 :             continue;
    1088                 :            :         }
    1089                 :          1 :         const char* pszBandName = CPLGetXMLValue(psIter, NULL, "");
    1090                 :            :         const SENTINEL2BandDescription* psBandDesc =
    1091                 :          1 :                                         SENTINEL2GetBandDesc(pszBandName);
    1092         [ +  + ]:          1 :         if( psBandDesc == NULL )
    1093                 :            :         {
    1094                 :          1 :             CPLDebug("SENTINEL2", "Unknown band name %s", pszBandName);
    1095                 :          1 :             continue;
    1096                 :            :         }
    1097                 :          1 :         oSetResolutions.insert( psBandDesc->nResolution );
    1098                 :          1 :         CPLString osName = psBandDesc->pszBandName + 1; /* skip B character */
    1099 [ +  - ][ +  + ]:          1 :         if( atoi(osName) < 10 )
    1100 [ +  - ][ +  - ]:          1 :             osName = "0" + osName;
         [ +  - ][ +  - ]
                 [ +  - ]
    1101 [ +  - ][ +  - ]:          1 :         oMapResolutionsToBands[psBandDesc->nResolution].insert(osName);
    1102                 :          1 :     }
    1103         [ +  + ]:          1 :     if( oSetResolutions.empty() )
    1104                 :            :     {
    1105                 :          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find any band");
    1106                 :          1 :         return false;
    1107                 :            :     }
    1108                 :          1 :     return true;
    1109                 :            : }
    1110                 :            : 
    1111                 :            : /************************************************************************/
    1112                 :            : /*                  SENTINEL2GetPolygonWKTFromPosList()                 */
    1113                 :            : /************************************************************************/
    1114                 :            : 
    1115                 :          1 : static CPLString SENTINEL2GetPolygonWKTFromPosList(const char* pszPosList)
    1116                 :            : {
    1117                 :          1 :     CPLString osPolygon;
    1118         [ +  - ]:          1 :     char** papszTokens = CSLTokenizeString(pszPosList);
    1119         [ +  - ]:          1 :     int nTokens = CSLCount(papszTokens);
    1120                 :          1 :     int nDim = 2;
    1121 [ +  + ][ +  - ]:          1 :     if( (nTokens % 3) == 0 && nTokens >= 3 * 4 &&
         [ +  - ][ +  - ]
                 [ +  - ]
    1122                 :          1 :         EQUAL(papszTokens[0], papszTokens[nTokens-3]) &&
    1123                 :          1 :         EQUAL(papszTokens[1], papszTokens[nTokens-2]) &&
    1124                 :          1 :         EQUAL(papszTokens[2], papszTokens[nTokens-1]) )
    1125                 :            :     {
    1126                 :          1 :         nDim = 3;
    1127                 :            :     }
    1128         [ +  - ]:          1 :     if( (nTokens % nDim) == 0 )
    1129                 :            :     {
    1130 [ +  - ][ +  - ]:          1 :         osPolygon = "POLYGON((";
                 [ +  - ]
    1131         [ +  + ]:          1 :         for(char** papszIter = papszTokens; *papszIter; papszIter += nDim )
    1132                 :            :         {
    1133         [ +  + ]:          1 :             if( papszIter != papszTokens )
    1134         [ +  - ]:          1 :                 osPolygon += ", ";
    1135         [ +  - ]:          1 :             osPolygon += papszIter[1];
    1136         [ +  - ]:          1 :             osPolygon += " ";
    1137         [ +  - ]:          1 :             osPolygon += papszIter[0];
    1138         [ +  + ]:          1 :             if( nDim == 3 )
    1139                 :            :             {
    1140         [ +  - ]:          1 :                 osPolygon += " ";
    1141         [ +  - ]:          1 :                 osPolygon += papszIter[2];
    1142                 :            :             }
    1143                 :            :         }
    1144         [ +  - ]:          1 :         osPolygon += "))";
    1145                 :            :     }
    1146         [ +  - ]:          1 :     CSLDestroy(papszTokens);
    1147                 :          1 :     return osPolygon;
    1148                 :            : }
    1149                 :            : 
    1150                 :            : /************************************************************************/
    1151                 :            : /*                    SENTINEL2GetBandListForResolution()               */
    1152                 :            : /************************************************************************/
    1153                 :            : 
    1154                 :          1 : static CPLString SENTINEL2GetBandListForResolution(
    1155                 :            :                                         const std::set<CPLString>& oBandnames)
    1156                 :            : {
    1157                 :          1 :     CPLString osBandNames;
    1158 [ +  - ][ +  - ]:          1 :     for(std::set<CPLString>::const_iterator oIterBandnames = oBandnames.begin();
         [ +  - ][ +  + ]
    1159         [ +  - ]:          1 :                                             oIterBandnames != oBandnames.end();
    1160                 :            :                                         ++oIterBandnames)
    1161                 :            :     {
    1162 [ +  - ][ +  + ]:          1 :         if( !osBandNames.empty() )
    1163         [ +  - ]:          1 :             osBandNames += ", ";
    1164 [ +  - ][ +  - ]:          1 :         const char* pszName = *oIterBandnames;
    1165         [ +  + ]:          1 :         if( *pszName == '0' )
    1166                 :          1 :             pszName ++;
    1167         [ +  + ]:          1 :         if( atoi(pszName) > 0 )
    1168 [ +  - ][ +  - ]:          1 :             osBandNames += "B" + CPLString(pszName);
         [ +  - ][ +  - ]
                 [ +  - ]
    1169                 :            :         else
    1170         [ +  - ]:          1 :             osBandNames += pszName;
    1171                 :            :     }
    1172                 :          1 :     return osBandNames;
    1173                 :            : }
    1174                 :            : 
    1175                 :            : /************************************************************************/
    1176                 :            : /*                         OpenL1BUserProduct()                         */
    1177                 :            : /************************************************************************/
    1178                 :            : 
    1179                 :          1 : GDALDataset *SENTINEL2Dataset::OpenL1BUserProduct( GDALOpenInfo * poOpenInfo )
    1180                 :            : {
    1181                 :          1 :     CPLXMLNode *psRoot = CPLParseXMLFile( poOpenInfo->pszFilename );
    1182         [ +  + ]:          1 :     if( psRoot == NULL )
    1183                 :          1 :         return NULL;
    1184                 :            : 
    1185                 :          1 :     char* pszOriginalXML = CPLSerializeXMLTree(psRoot);
    1186                 :          1 :     CPLString osOriginalXML;
    1187         [ +  - ]:          1 :     if( pszOriginalXML )
    1188 [ +  - ][ +  - ]:          1 :         osOriginalXML = pszOriginalXML;
                 [ +  - ]
    1189         [ +  - ]:          1 :     CPLFree(pszOriginalXML);
    1190                 :            : 
    1191                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
    1192         [ +  - ]:          1 :     CPLStripXMLNamespace(psRoot, NULL, TRUE);
    1193                 :            : 
    1194                 :            :     CPLXMLNode* psProductInfo = CPLGetXMLNode(psRoot,
    1195         [ +  - ]:          1 :                             "=Level-1B_User_Product.General_Info.Product_Info");
    1196         [ +  + ]:          1 :     if( psProductInfo == NULL )
    1197                 :            :     {
    1198                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    1199         [ +  - ]:          1 :                  "=Level-1B_User_Product.General_Info.Product_Info");
    1200                 :          1 :         return NULL;
    1201                 :            :     }
    1202                 :            : 
    1203         [ +  - ]:          1 :     std::set<int> oSetResolutions;
    1204         [ +  - ]:          1 :     std::map<int, std::set<CPLString> > oMapResolutionsToBands;
    1205         [ +  + ]:          1 :     if( !SENTINEL2GetResolutionSet(psProductInfo,
    1206                 :            :                                    oSetResolutions,
    1207         [ +  - ]:          1 :                                    oMapResolutionsToBands) )
    1208                 :            :     {
    1209                 :          1 :         return NULL;
    1210                 :            :     }
    1211                 :            : 
    1212         [ +  - ]:          1 :     std::vector<CPLString> aosGranuleList;
    1213         [ -  + ]:          1 :     if( !SENTINEL2GetGranuleList(psRoot,
    1214                 :            :                                  SENTINEL2_L1B,
    1215                 :            :                                  poOpenInfo->pszFilename,
    1216         [ +  - ]:          1 :                                  aosGranuleList) )
    1217                 :            :     {
    1218                 :          0 :         return NULL;
    1219                 :            :     }
    1220                 :            : 
    1221 [ +  - ][ +  - ]:          1 :     SENTINEL2DatasetContainer* poDS = new SENTINEL2DatasetContainer();
    1222                 :            :     char** papszMD = SENTINEL2GetUserProductMetadata(psRoot,
    1223         [ +  - ]:          1 :                                                  "Level-1B_User_Product");
    1224         [ +  - ]:          1 :     poDS->GDALDataset::SetMetadata(papszMD);
    1225         [ +  - ]:          1 :     CSLDestroy(papszMD);
    1226                 :            : 
    1227 [ +  - ][ +  - ]:          1 :     if( !osOriginalXML.empty() )
    1228                 :            :     {
    1229                 :          1 :         char* apszXMLMD[2] = { NULL };
    1230         [ +  - ]:          1 :         apszXMLMD[0] = const_cast<char*>(osOriginalXML.c_str());
    1231                 :          1 :         apszXMLMD[1] = NULL;
    1232         [ +  - ]:          1 :         poDS->GDALDataset::SetMetadata(apszXMLMD, "xml:SENTINEL2");
    1233                 :            :     }
    1234                 :            : 
    1235                 :            :     /* Create subdatsets per granules and resolution (10, 20, 60m) */
    1236                 :          1 :     int iSubDSNum = 1;
    1237 [ +  - ][ +  + ]:          1 :     for(size_t i = 0; i < aosGranuleList.size(); i++ )
    1238                 :            :     {
    1239 [ +  - ][ +  - ]:          1 :         for(std::set<int>::const_iterator oIterRes = oSetResolutions.begin();
         [ +  - ][ +  + ]
    1240         [ +  - ]:          1 :                                     oIterRes != oSetResolutions.end();
    1241                 :            :                                 ++oIterRes )
    1242                 :            :         {
    1243         [ +  - ]:          1 :             const int nResolution = *oIterRes;
    1244                 :            : 
    1245                 :            :             poDS->GDALDataset::SetMetadataItem(
    1246                 :            :                 CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    1247                 :            :                 CPLSPrintf("SENTINEL2_L1B:%s:%dm",
    1248         [ +  - ]:          1 :                            aosGranuleList[i].c_str(),
    1249                 :            :                            nResolution),
    1250 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
         [ +  - ][ +  - ]
    1251                 :            : 
    1252                 :            :             CPLString osBandNames = SENTINEL2GetBandListForResolution(
    1253 [ +  - ][ +  - ]:          1 :                                             oMapResolutionsToBands[nResolution]);
    1254                 :            : 
    1255                 :            :             CPLString osDesc(CPLSPrintf("Bands %s of granule %s with %dm resolution",
    1256                 :            :                                         osBandNames.c_str(),
    1257         [ +  - ]:          1 :                                         CPLGetFilename(aosGranuleList[i]),
    1258 [ +  - ][ +  - ]:          1 :                                         nResolution));
         [ +  - ][ +  - ]
                 [ +  - ]
    1259                 :            :             poDS->GDALDataset::SetMetadataItem(
    1260                 :            :                 CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    1261                 :            :                 osDesc.c_str(),
    1262 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    1263                 :            : 
    1264                 :          1 :             iSubDSNum ++;
    1265 [ +  - ][ +  - ]:          1 :         }
    1266                 :            :     }
    1267                 :            : 
    1268                 :            :     const char* pszPosList = CPLGetXMLValue(psRoot,
    1269                 :            :         "=Level-1B_User_Product.Geometric_Info.Product_Footprint."
    1270         [ +  - ]:          1 :         "Product_Footprint.Global_Footprint.EXT_POS_LIST", NULL);
    1271         [ +  - ]:          1 :     if( pszPosList != NULL )
    1272                 :            :     {
    1273         [ +  - ]:          1 :         CPLString osPolygon = SENTINEL2GetPolygonWKTFromPosList(pszPosList);
    1274 [ +  - ][ +  - ]:          1 :         if( !osPolygon.empty() )
    1275 [ +  - ][ +  - ]:          1 :             poDS->GDALDataset::SetMetadataItem("FOOTPRINT", osPolygon.c_str());
                 [ +  - ]
    1276                 :            :     }
    1277                 :            : 
    1278 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
    1279                 :            : }
    1280                 :            : 
    1281                 :            : /************************************************************************/
    1282                 :            : /*                    SENTINEL2GetL1BGranuleMetadata()                  */
    1283                 :            : /************************************************************************/
    1284                 :            : 
    1285                 :            : static
    1286                 :          1 : char** SENTINEL2GetL1BGranuleMetadata( CPLXMLNode* psMainMTD )
    1287                 :            : {
    1288                 :          1 :     CPLStringList aosList;
    1289                 :            : 
    1290                 :            :     CPLXMLNode* psRoot =  CPLGetXMLNode(psMainMTD,
    1291         [ +  - ]:          1 :                                         "=Level-1B_Granule_ID");
    1292         [ +  + ]:          1 :     if( psRoot == NULL )
    1293                 :            :     {
    1294                 :            :         CPLError(CE_Failure, CPLE_AppDefined,
    1295         [ +  - ]:          1 :                  "Cannot find =Level-1B_Granule_ID");
    1296                 :          1 :         return NULL;
    1297                 :            :     }
    1298                 :            :     CPLXMLNode* psGeneralInfo = CPLGetXMLNode(psRoot,
    1299         [ +  - ]:          1 :                                               "General_Info");
    1300 [ +  - ][ +  + ]:          1 :     for( CPLXMLNode* psIter = (psGeneralInfo ? psGeneralInfo->psChild : NULL);
    1301                 :            :                      psIter != NULL;
    1302                 :            :                      psIter = psIter->psNext )
    1303                 :            :     {
    1304         [ -  + ]:          1 :         if( psIter->eType != CXT_Element )
    1305                 :          0 :             continue;
    1306         [ +  - ]:          1 :         const char* pszValue = CPLGetXMLValue(psIter, NULL, NULL);
    1307         [ +  + ]:          1 :         if( pszValue != NULL )
    1308                 :            :         {
    1309         [ +  - ]:          1 :             aosList.AddNameValue( psIter->pszValue, pszValue );
    1310                 :            :         }
    1311                 :            :     }
    1312                 :            : 
    1313                 :            :     CPLXMLNode* psGeometryHeader = CPLGetXMLNode(psRoot,
    1314         [ +  - ]:          1 :                         "Geometric_Info.Granule_Position.Geometric_Header");
    1315         [ +  + ]:          1 :     if( psGeometryHeader != NULL )
    1316                 :            :     {
    1317                 :            :         const char* pszVal = CPLGetXMLValue(psGeometryHeader,
    1318         [ +  - ]:          1 :                                             "Incidence_Angles.ZENITH_ANGLE", NULL);
    1319         [ +  - ]:          1 :         if( pszVal )
    1320         [ +  - ]:          1 :             aosList.AddNameValue( "INCIDENCE_ZENITH_ANGLE", pszVal );
    1321                 :            : 
    1322                 :            :         pszVal = CPLGetXMLValue(psGeometryHeader,
    1323         [ +  - ]:          1 :                                             "Incidence_Angles.AZIMUTH_ANGLE", NULL);
    1324         [ +  - ]:          1 :         if( pszVal )
    1325         [ +  - ]:          1 :             aosList.AddNameValue( "INCIDENCE_AZIMUTH_ANGLE", pszVal );
    1326                 :            : 
    1327                 :            :         pszVal = CPLGetXMLValue(psGeometryHeader,
    1328         [ +  - ]:          1 :                                             "Solar_Angles.ZENITH_ANGLE", NULL);
    1329         [ +  - ]:          1 :         if( pszVal )
    1330         [ +  - ]:          1 :             aosList.AddNameValue( "SOLAR_ZENITH_ANGLE", pszVal );
    1331                 :            : 
    1332                 :            :         pszVal = CPLGetXMLValue(psGeometryHeader,
    1333         [ +  - ]:          1 :                                             "Solar_Angles.AZIMUTH_ANGLE", NULL);
    1334         [ +  - ]:          1 :         if( pszVal )
    1335         [ +  - ]:          1 :             aosList.AddNameValue( "SOLAR_AZIMUTH_ANGLE", pszVal );
    1336                 :            :     }
    1337                 :            : 
    1338         [ +  - ]:          1 :     CPLXMLNode* psQII = CPLGetXMLNode(psRoot, "Quality_Indicators_Info");
    1339         [ +  + ]:          1 :     if( psQII != NULL )
    1340                 :            :     {
    1341         [ +  - ]:          1 :         CPLXMLNode* psICCQI = CPLGetXMLNode(psQII, "Image_Content_QI");
    1342 [ +  - ][ +  + ]:          1 :         for( CPLXMLNode* psIter = (psICCQI ? psICCQI->psChild : NULL);
    1343                 :            :                      psIter != NULL;
    1344                 :            :                      psIter = psIter->psNext )
    1345                 :            :         {
    1346         [ -  + ]:          1 :             if( psIter->eType != CXT_Element )
    1347                 :          0 :                 continue;
    1348 [ +  - ][ +  - ]:          1 :             if( psIter->psChild != NULL && psIter->psChild->eType == CXT_Text )
    1349                 :            :             {
    1350                 :            :                 aosList.AddNameValue( psIter->pszValue,
    1351         [ +  - ]:          1 :                                     psIter->psChild->pszValue );
    1352                 :            :             }
    1353                 :            :         }
    1354                 :            :     }
    1355                 :            : 
    1356         [ +  - ]:          1 :     return aosList.StealList();
    1357                 :            : }
    1358                 :            : 
    1359                 :            : /************************************************************************/
    1360                 :            : /*                        SENTINEL2GetTilename()                        */
    1361                 :            : /************************************************************************/
    1362                 :            : 
    1363                 :          1 : static CPLString SENTINEL2GetTilename(const CPLString& osGranulePath,
    1364                 :            :                                       const CPLString& osGranuleName,
    1365                 :            :                                       const CPLString& osBandName,
    1366                 :            :                                       bool bIsPreview = false,
    1367                 :            :                                       int nPrecisionL2A = 0)
    1368                 :            : {
    1369                 :          1 :     CPLString osJPEG2000Name(osGranuleName);
    1370 [ +  - ][ +  + ]:          1 :     if( osJPEG2000Name.size() > 7 &&
         [ +  + ][ +  + ]
                 [ +  + ]
    1371 [ +  - ][ +  - ]:          1 :         osJPEG2000Name[osJPEG2000Name.size()-7] == '_' &&
    1372 [ +  - ][ +  - ]:          1 :         osJPEG2000Name[osJPEG2000Name.size()-6] == 'N' )
    1373                 :            :     {
    1374 [ +  - ][ +  - ]:          1 :         osJPEG2000Name.resize(osJPEG2000Name.size()-7);
    1375                 :            :     }
    1376                 :            : 
    1377                 :            :     const SENTINEL2_L2A_BandDescription* psL2ABandDesc =
    1378 [ +  + ][ +  - ]:          1 :                     (nPrecisionL2A) ? SENTINEL2GetL2ABandDesc(osBandName): NULL;
    1379                 :            : 
    1380         [ +  - ]:          1 :     CPLString osTile(osGranulePath);
    1381         [ +  - ]:          1 :     const char chSeparator = SENTINEL2GetPathSeparator(osTile);
    1382 [ +  - ][ +  - ]:          1 :     if( !osTile.empty() )
    1383         [ +  - ]:          1 :         osTile += chSeparator;
    1384 [ +  + ][ +  + ]:          1 :     if( bIsPreview ||
                 [ +  + ]
    1385                 :            :         (psL2ABandDesc != NULL && psL2ABandDesc->eLocation == TL_QI_DATA ) )
    1386                 :            :     {
    1387         [ +  - ]:          1 :         osTile += "QI_DATA";
    1388         [ +  - ]:          1 :         osTile += chSeparator;
    1389 [ +  - ][ +  - ]:          1 :         if( osJPEG2000Name.size() > 12 &&
         [ +  - ][ +  - ]
                 [ +  - ]
    1390 [ +  - ][ +  - ]:          1 :             osJPEG2000Name[8] == '_' && osJPEG2000Name[12] == '_' )
    1391                 :            :         {
    1392 [ +  - ][ +  + ]:          1 :             if( atoi(osBandName) > 0 )
    1393                 :            :             {
    1394         [ +  - ]:          1 :                 osJPEG2000Name[9] = 'P';
    1395         [ +  - ]:          1 :                 osJPEG2000Name[10] = 'V';
    1396         [ +  - ]:          1 :                 osJPEG2000Name[11] = 'I';
    1397                 :            :             }
    1398 [ +  - ][ +  - ]:          1 :             else if( nPrecisionL2A && osBandName.size() == 3 )
         [ +  - ][ +  - ]
    1399                 :            :             {
    1400 [ +  - ][ +  - ]:          1 :                 osJPEG2000Name[9] = osBandName[0];
    1401 [ +  - ][ +  - ]:          1 :                 osJPEG2000Name[10] = osBandName[1];
    1402 [ +  - ][ +  - ]:          1 :                 osJPEG2000Name[11] = osBandName[2];
    1403                 :            :             }
    1404                 :            :         }
    1405                 :            :         else
    1406                 :            :         {
    1407                 :            :             CPLDebug("SENTINEL2", "Invalid granule path: %s",
    1408 [ #  # ][ #  # ]:          0 :                      osGranulePath.c_str());
    1409                 :            :         }
    1410         [ +  - ]:          1 :         osTile += osJPEG2000Name;
    1411 [ +  + ][ +  + ]:          1 :         if( nPrecisionL2A && !bIsPreview )
    1412 [ +  - ][ +  - ]:          1 :             osTile += CPLSPrintf("_%02dm", nPrecisionL2A);
    1413                 :            :     }
    1414                 :            :     else
    1415                 :            :     {
    1416         [ +  - ]:          1 :         osTile += "IMG_DATA";
    1417         [ +  - ]:          1 :         osTile += chSeparator;
    1418 [ +  + ][ +  + ]:          1 :         if( (psL2ABandDesc != NULL && psL2ABandDesc->eLocation == TL_IMG_DATA_Rxxm) ||
         [ +  + ][ +  + ]
    1419                 :            :             (psL2ABandDesc == NULL && nPrecisionL2A != 0) )
    1420                 :            :         {
    1421 [ +  - ][ +  - ]:          1 :             osTile += CPLSPrintf("R%02dm", nPrecisionL2A);
    1422         [ +  - ]:          1 :             osTile += chSeparator;
    1423                 :            :         }
    1424 [ +  - ][ +  + ]:          1 :         if( osJPEG2000Name.size() > 12 &&
         [ +  - ][ +  - ]
                 [ +  + ]
    1425 [ +  - ][ +  - ]:          1 :             osJPEG2000Name[8] == '_' && osJPEG2000Name[12] == '_' )
    1426                 :            :         {
    1427 [ +  - ][ +  + ]:          1 :             if( atoi(osBandName) > 0 )
    1428                 :            :             {
    1429         [ +  - ]:          1 :                 osJPEG2000Name[9] = 'M';
    1430         [ +  - ]:          1 :                 osJPEG2000Name[10] = 'S';
    1431         [ +  - ]:          1 :                 osJPEG2000Name[11] = 'I';
    1432                 :            :             }
    1433 [ +  - ][ +  - ]:          1 :             else if( nPrecisionL2A && osBandName.size() == 3 )
         [ +  - ][ +  - ]
    1434                 :            :             {
    1435 [ +  - ][ +  - ]:          1 :                 osJPEG2000Name[9] = osBandName[0];
    1436 [ +  - ][ +  - ]:          1 :                 osJPEG2000Name[10] = osBandName[1];
    1437 [ +  - ][ +  - ]:          1 :                 osJPEG2000Name[11] = osBandName[2];
    1438                 :            :             }
    1439                 :            :         }
    1440                 :            :         else
    1441                 :            :         {
    1442                 :            :             CPLDebug("SENTINEL2", "Invalid granule path: %s",
    1443 [ +  - ][ +  - ]:          1 :                      osGranulePath.c_str());
    1444                 :            :         }
    1445         [ +  - ]:          1 :         osTile += osJPEG2000Name;
    1446 [ +  - ][ +  + ]:          1 :         if( atoi(osBandName) > 0 )
    1447                 :            :         {
    1448         [ +  - ]:          1 :             osTile += "_B";
    1449 [ +  - ][ +  + ]:          1 :             if( osBandName.size() == 3 && osBandName[0] == '0' )
         [ +  - ][ +  - ]
                 [ +  + ]
    1450 [ +  - ][ +  - ]:          1 :                 osTile += osBandName.substr(1);
                 [ +  - ]
    1451                 :            :             else
    1452         [ +  - ]:          1 :                 osTile += osBandName;
    1453                 :            :         }
    1454         [ +  + ]:          1 :         if( nPrecisionL2A )
    1455 [ +  - ][ +  - ]:          1 :             osTile += CPLSPrintf("_%02dm", nPrecisionL2A);
    1456                 :            :     }
    1457         [ +  - ]:          1 :     osTile += ".jp2";
    1458                 :          1 :     return osTile;
    1459                 :            : }
    1460                 :            : 
    1461                 :            : /************************************************************************/
    1462                 :            : /*                 SENTINEL2GetMainMTDFilenameFromGranuleMTD()          */
    1463                 :            : /************************************************************************/
    1464                 :            : 
    1465                 :          1 : static CPLString SENTINEL2GetMainMTDFilenameFromGranuleMTD(const char* pszFilename)
    1466                 :            : {
    1467                 :            :     // Look for product MTD file
    1468                 :            :     CPLString osTopDir(CPLFormFilename(
    1469                 :            :         CPLFormFilename( CPLGetDirname(pszFilename), "..", NULL ),
    1470                 :          1 :         "..", NULL ));
    1471                 :            : 
    1472                 :            :     // Workaround to avoid long filenames on Windows
    1473 [ +  - ][ +  + ]:          1 :     if( CPLIsFilenameRelative(pszFilename) )
    1474                 :            :     {
    1475                 :            :         // GRANULE/bla/bla.xml
    1476         [ +  - ]:          1 :         const char* pszPath = CPLGetPath(pszFilename);
    1477 [ +  + ][ -  + ]:          1 :         if( strchr(pszPath, '/') || strchr(pszPath, '\\') )
    1478                 :            :         {
    1479 [ +  - ][ +  - ]:          1 :             osTopDir = CPLGetPath(CPLGetPath(pszPath));
         [ +  - ][ +  - ]
                 [ +  - ]
    1480 [ +  - ][ -  + ]:          1 :             if( osTopDir == "" )
    1481 [ #  # ][ #  # ]:          0 :                 osTopDir = ".";
                 [ #  # ]
    1482                 :            :         }
    1483                 :            :     }
    1484                 :            : 
    1485 [ +  - ][ +  - ]:          1 :     char** papszContents = VSIReadDir(osTopDir);
    1486         [ +  - ]:          1 :     CPLString osMainMTD;
    1487 [ +  + ][ +  + ]:          1 :     for(char** papszIter = papszContents; papszIter && *papszIter; ++papszIter)
                 [ +  + ]
    1488                 :            :     {
    1489 [ +  + ][ +  + ]:          1 :         if( strlen(*papszIter) >= strlen("S2A_XXXX_MTD") &&
         [ +  + ][ +  - ]
    1490                 :          1 :             (STARTS_WITH_CI(*papszIter, "S2A_") ||
    1491                 :          1 :              STARTS_WITH_CI(*papszIter, "S2B_")) &&
    1492                 :          1 :              EQUALN(*papszIter + strlen("S2A_XXXX"), "_MTD", 4) )
    1493                 :            :         {
    1494 [ +  - ][ +  - ]:          1 :             osMainMTD = CPLFormFilename(osTopDir, *papszIter, NULL);
         [ +  - ][ +  - ]
                 [ +  - ]
    1495                 :          1 :             break;
    1496                 :            :         }
    1497                 :            :     }
    1498         [ +  - ]:          1 :     CSLDestroy(papszContents);
    1499                 :          1 :     return osMainMTD;
    1500                 :            : }
    1501                 :            : 
    1502                 :            : /************************************************************************/
    1503                 :            : /*            SENTINEL2GetResolutionSetAndMainMDFromGranule()           */
    1504                 :            : /************************************************************************/
    1505                 :            : 
    1506                 :          1 : static void SENTINEL2GetResolutionSetAndMainMDFromGranule(
    1507                 :            :                     const char* pszFilename,
    1508                 :            :                     const char* pszRootPathWithoutEqual,
    1509                 :            :                     int nResolutionOfInterest,
    1510                 :            :                     std::set<int>& oSetResolutions,
    1511                 :            :                     std::map<int, std::set<CPLString> >& oMapResolutionsToBands,
    1512                 :            :                     char**& papszMD,
    1513                 :            :                     CPLXMLNode** ppsRootMainMTD)
    1514                 :            : {
    1515                 :          1 :     CPLString osMainMTD(SENTINEL2GetMainMTDFilenameFromGranuleMTD(pszFilename));
    1516                 :            : 
    1517                 :            :     // Parse product MTD if available
    1518                 :          1 :     papszMD = NULL;
    1519 [ +  - ][ +  + ]:          1 :     if( !osMainMTD.empty() &&
         [ +  + ][ +  + ]
    1520                 :            :         /* env var for debug only */
    1521 [ +  - ][ +  - ]:          1 :         CPLTestBool(CPLGetConfigOption("SENTINEL2_USE_MAIN_MTD", "YES")) )
    1522                 :            :     {
    1523 [ +  - ][ +  - ]:          1 :         CPLXMLNode *psRootMainMTD = CPLParseXMLFile( osMainMTD );
    1524         [ +  - ]:          1 :         if( psRootMainMTD != NULL )
    1525                 :            :         {
    1526         [ +  - ]:          1 :             CPLStripXMLNamespace(psRootMainMTD, NULL, TRUE);
    1527                 :            : 
    1528                 :            :             CPLXMLNode* psProductInfo = CPLGetXMLNode(psRootMainMTD,
    1529 [ +  - ][ +  - ]:          1 :                 CPLSPrintf("=%s.General_Info.Product_Info", pszRootPathWithoutEqual));
    1530         [ +  - ]:          1 :             if( psProductInfo != NULL )
    1531                 :            :             {
    1532                 :            :                 SENTINEL2GetResolutionSet(psProductInfo,
    1533                 :            :                                           oSetResolutions,
    1534         [ +  - ]:          1 :                                           oMapResolutionsToBands);
    1535                 :            :             }
    1536                 :            : 
    1537                 :            :             papszMD = SENTINEL2GetUserProductMetadata(psRootMainMTD,
    1538         [ +  - ]:          1 :                                                       pszRootPathWithoutEqual);
    1539         [ +  + ]:          1 :             if( ppsRootMainMTD != NULL )
    1540                 :          1 :                 *ppsRootMainMTD = psRootMainMTD;
    1541                 :            :             else
    1542         [ +  - ]:          1 :                 CPLDestroyXMLNode(psRootMainMTD);
    1543                 :            :         }
    1544                 :            :     }
    1545                 :            :     else
    1546                 :            :     {
    1547                 :            :         // If no main MTD file found, then probe all bands for resolution (of
    1548                 :            :         // interest if there's one, or all resolutions otherwise)
    1549         [ +  + ]:          1 :         for(size_t i=0;i<NB_BANDS;i++)
    1550                 :            :         {
    1551 [ +  + ][ +  + ]:          1 :             if( nResolutionOfInterest != 0 &&
    1552                 :            :                 asBandDesc[i].nResolution != nResolutionOfInterest )
    1553                 :            :             {
    1554                 :          1 :                 continue;
    1555                 :            :             }
    1556         [ +  - ]:          1 :             CPLString osBandName = asBandDesc[i].pszBandName + 1; /* skip B character */
    1557 [ +  - ][ +  + ]:          1 :             if( atoi(osBandName) < 10 )
    1558 [ +  - ][ +  - ]:          1 :                 osBandName = "0" + osBandName;
         [ +  - ][ +  - ]
                 [ +  - ]
    1559                 :            : 
    1560                 :            :             CPLString osTile(SENTINEL2GetTilename(CPLGetPath(pszFilename),
    1561                 :            :                                                   CPLGetBasename(pszFilename),
    1562 [ +  - ][ +  - ]:          1 :                                                   osBandName));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1563                 :            :             VSIStatBufL sStat;
    1564 [ +  - ][ +  - ]:          1 :             if( VSIStatExL(osTile, &sStat, VSI_STAT_EXISTS_FLAG) == 0 )
                 [ +  + ]
    1565                 :            :             {
    1566 [ +  - ][ +  - ]:          1 :                 oMapResolutionsToBands[asBandDesc[i].nResolution].insert(osBandName);
    1567         [ +  - ]:          1 :                 oSetResolutions.insert(asBandDesc[i].nResolution);
    1568                 :            :             }
    1569 [ +  - ][ +  - ]:          1 :         }
    1570                 :          1 :     }
    1571                 :          1 : }
    1572                 :            : 
    1573                 :            : /************************************************************************/
    1574                 :            : /*                           OpenL1BGranule()                           */
    1575                 :            : /************************************************************************/
    1576                 :            : 
    1577                 :          1 : GDALDataset *SENTINEL2Dataset::OpenL1BGranule( const char* pszFilename,
    1578                 :            :                                                CPLXMLNode** ppsRoot,
    1579                 :            :                                                int nResolutionOfInterest,
    1580                 :            :                                                std::set<CPLString> *poBandSet )
    1581                 :            : {
    1582                 :          1 :     CPLXMLNode *psRoot = CPLParseXMLFile( pszFilename );
    1583         [ +  + ]:          1 :     if( psRoot == NULL )
    1584                 :          1 :         return NULL;
    1585                 :            : 
    1586                 :          1 :     char* pszOriginalXML = CPLSerializeXMLTree(psRoot);
    1587                 :          1 :     CPLString osOriginalXML;
    1588         [ +  - ]:          1 :     if( pszOriginalXML )
    1589 [ +  - ][ +  - ]:          1 :         osOriginalXML = pszOriginalXML;
                 [ +  - ]
    1590         [ +  - ]:          1 :     CPLFree(pszOriginalXML);
    1591                 :            : 
    1592                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
    1593         [ +  - ]:          1 :     CPLStripXMLNamespace(psRoot, NULL, TRUE);
    1594                 :            : 
    1595         [ +  - ]:          1 :     CPLString osMainMTD(SENTINEL2GetMainMTDFilenameFromGranuleMTD(pszFilename));
    1596                 :            : 
    1597 [ +  - ][ +  - ]:          1 :     SENTINEL2DatasetContainer* poDS = new SENTINEL2DatasetContainer();
    1598                 :            : 
    1599 [ +  - ][ +  - ]:          1 :     if( !osOriginalXML.empty() )
    1600                 :            :     {
    1601                 :            :         char* apszXMLMD[2];
    1602         [ +  - ]:          1 :         apszXMLMD[0] = const_cast<char*>(osOriginalXML.c_str());
    1603                 :          1 :         apszXMLMD[1] = NULL;
    1604         [ +  - ]:          1 :         poDS->GDALDataset::SetMetadata(apszXMLMD, "xml:SENTINEL2");
    1605                 :            :     }
    1606                 :            : 
    1607         [ +  - ]:          1 :     std::set<int> oSetResolutions;
    1608         [ +  - ]:          1 :     std::map<int, std::set<CPLString> > oMapResolutionsToBands;
    1609                 :          1 :     char** papszMD = NULL;
    1610                 :            :     SENTINEL2GetResolutionSetAndMainMDFromGranule(pszFilename,
    1611                 :            :                                                   "Level-1B_User_Product",
    1612                 :            :                                                   nResolutionOfInterest,
    1613                 :            :                                                   oSetResolutions,
    1614                 :            :                                                   oMapResolutionsToBands,
    1615                 :            :                                                   papszMD,
    1616         [ +  - ]:          1 :                                                   NULL);
    1617         [ +  + ]:          1 :     if( poBandSet != NULL )
    1618 [ +  - ][ +  - ]:          1 :         *poBandSet = oMapResolutionsToBands[nResolutionOfInterest];
    1619                 :            : 
    1620         [ +  - ]:          1 :     char** papszGranuleMD = SENTINEL2GetL1BGranuleMetadata(psRoot);
    1621         [ +  - ]:          1 :     papszMD = CSLMerge(papszMD, papszGranuleMD);
    1622         [ +  - ]:          1 :     CSLDestroy(papszGranuleMD);
    1623                 :            : 
    1624                 :            :     // Remove CLOUD_COVERAGE_ASSESSMENT that comes from main metadata, if granule
    1625                 :            :     // CLOUDY_PIXEL_PERCENTAGE is present.
    1626 [ +  - ][ +  + ]:          1 :     if( CSLFetchNameValue(papszMD, "CLOUDY_PIXEL_PERCENTAGE") != NULL &&
         [ +  - ][ +  + ]
    1627         [ +  - ]:          1 :         CSLFetchNameValue(papszMD, "CLOUD_COVERAGE_ASSESSMENT") != NULL )
    1628                 :            :     {
    1629         [ +  - ]:          1 :         papszMD = CSLSetNameValue(papszMD, "CLOUD_COVERAGE_ASSESSMENT", NULL);
    1630                 :            :     }
    1631                 :            : 
    1632         [ +  - ]:          1 :     poDS->GDALDataset::SetMetadata(papszMD);
    1633         [ +  - ]:          1 :     CSLDestroy(papszMD);
    1634                 :            : 
    1635                 :            :     // Get the footprint
    1636                 :            :     const char* pszPosList = CPLGetXMLValue(psRoot,
    1637                 :            :         "=Level-1B_Granule_ID.Geometric_Info.Granule_Footprint."
    1638         [ +  - ]:          1 :         "Granule_Footprint.Footprint.EXT_POS_LIST", NULL);
    1639         [ +  + ]:          1 :     if( pszPosList != NULL )
    1640                 :            :     {
    1641         [ +  - ]:          1 :         CPLString osPolygon = SENTINEL2GetPolygonWKTFromPosList(pszPosList);
    1642 [ +  - ][ +  - ]:          1 :         if( !osPolygon.empty() )
    1643 [ +  - ][ +  - ]:          1 :             poDS->GDALDataset::SetMetadataItem("FOOTPRINT", osPolygon.c_str());
                 [ +  - ]
    1644                 :            :     }
    1645                 :            : 
    1646                 :            :     /* Create subdatsets per resolution (10, 20, 60m) */
    1647                 :          1 :     int iSubDSNum = 1;
    1648 [ +  - ][ +  - ]:          1 :     for(std::set<int>::const_iterator oIterRes = oSetResolutions.begin();
         [ +  - ][ +  + ]
    1649         [ +  - ]:          1 :                                 oIterRes != oSetResolutions.end();
    1650                 :            :                             ++oIterRes )
    1651                 :            :     {
    1652         [ +  - ]:          1 :         const int nResolution = *oIterRes;
    1653                 :            : 
    1654                 :            :         poDS->GDALDataset::SetMetadataItem(
    1655                 :            :             CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    1656                 :            :             CPLSPrintf("SENTINEL2_L1B:%s:%dm",
    1657                 :            :                         pszFilename,
    1658                 :            :                         nResolution),
    1659 [ +  - ][ +  - ]:          1 :             "SUBDATASETS");
                 [ +  - ]
    1660                 :            : 
    1661                 :            :         CPLString osBandNames = SENTINEL2GetBandListForResolution(
    1662 [ +  - ][ +  - ]:          1 :                                         oMapResolutionsToBands[nResolution]);
    1663                 :            : 
    1664                 :            :         CPLString osDesc(CPLSPrintf("Bands %s with %dm resolution",
    1665                 :            :                                     osBandNames.c_str(),
    1666 [ +  - ][ +  - ]:          1 :                                     nResolution));
                 [ +  - ]
    1667                 :            :         poDS->GDALDataset::SetMetadataItem(
    1668                 :            :             CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    1669                 :            :             osDesc.c_str(),
    1670 [ +  - ][ +  - ]:          1 :             "SUBDATASETS");
                 [ +  - ]
    1671                 :            : 
    1672                 :          1 :         iSubDSNum ++;
    1673 [ +  - ][ +  - ]:          1 :     }
    1674                 :            : 
    1675         [ +  + ]:          1 :     if( ppsRoot != NULL )
    1676                 :            :     {
    1677                 :          1 :         *ppsRoot = oXMLHolder.Release();
    1678                 :            :     }
    1679                 :            : 
    1680 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
    1681                 :            : }
    1682                 :            : 
    1683                 :            : /************************************************************************/
    1684                 :            : /*                     SENTINEL2SetBandMetadata()                       */
    1685                 :            : /************************************************************************/
    1686                 :            : 
    1687                 :          1 : static void SENTINEL2SetBandMetadata(GDALRasterBand* poBand,
    1688                 :            :                                      const CPLString& osBandName)
    1689                 :            : {
    1690                 :          1 :     CPLString osLookupBandName(osBandName);
    1691 [ +  - ][ +  + ]:          1 :     if( osLookupBandName[0] == '0' )
    1692 [ +  - ][ +  - ]:          1 :         osLookupBandName = osLookupBandName.substr(1);
         [ +  - ][ +  - ]
                 [ +  - ]
    1693 [ +  - ][ +  + ]:          1 :     if( atoi(osLookupBandName) > 0 )
    1694 [ +  - ][ +  - ]:          1 :         osLookupBandName = "B" + osLookupBandName;
         [ +  - ][ +  - ]
                 [ +  - ]
    1695                 :            : 
    1696         [ +  - ]:          1 :     CPLString osBandDesc(osLookupBandName);
    1697                 :            :     const SENTINEL2BandDescription* psBandDesc =
    1698         [ +  - ]:          1 :                             SENTINEL2GetBandDesc(osLookupBandName);
    1699         [ +  + ]:          1 :     if( psBandDesc != NULL )
    1700                 :            :     {
    1701                 :            :         osBandDesc += CPLSPrintf(", central wavelength %d nm",
    1702 [ +  - ][ +  - ]:          1 :                                     psBandDesc->nWaveLength);
    1703         [ +  - ]:          1 :         poBand->SetColorInterpretation(psBandDesc->eColorInterp);
    1704         [ +  - ]:          1 :         poBand->SetMetadataItem("BANDNAME", psBandDesc->pszBandName);
    1705                 :            :         poBand->SetMetadataItem("BANDWIDTH", CPLSPrintf("%d",
    1706 [ +  - ][ +  - ]:          1 :                                                 psBandDesc->nBandWidth));
    1707         [ +  - ]:          1 :         poBand->SetMetadataItem("BANDWIDTH_UNIT", "nm");
    1708                 :            :         poBand->SetMetadataItem("WAVELENGTH", CPLSPrintf("%d",
    1709 [ +  - ][ +  - ]:          1 :                                                 psBandDesc->nWaveLength));
    1710         [ +  - ]:          1 :         poBand->SetMetadataItem("WAVELENGTH_UNIT", "nm");
    1711                 :            :     }
    1712                 :            :     else
    1713                 :            :     {
    1714                 :            :         const SENTINEL2_L2A_BandDescription* psL2ABandDesc =
    1715         [ +  - ]:          1 :                                         SENTINEL2GetL2ABandDesc(osBandName);
    1716         [ +  - ]:          1 :         if(psL2ABandDesc != NULL )
    1717                 :            :         {
    1718         [ +  - ]:          1 :             osBandDesc += ", ";
    1719         [ +  - ]:          1 :             osBandDesc += psL2ABandDesc->pszBandDescription;
    1720                 :            :         }
    1721                 :            : 
    1722 [ +  - ][ +  - ]:          1 :         poBand->SetMetadataItem("BANDNAME", osBandName);
    1723                 :            :     }
    1724 [ +  - ][ +  - ]:          1 :     poBand->SetDescription(osBandDesc);
                 [ +  - ]
    1725                 :          1 : }
    1726                 :            : 
    1727                 :            : /************************************************************************/
    1728                 :            : /*                         OpenL1BSubdataset()                          */
    1729                 :            : /************************************************************************/
    1730                 :            : 
    1731                 :          1 : GDALDataset *SENTINEL2Dataset::OpenL1BSubdataset( GDALOpenInfo * poOpenInfo )
    1732                 :            : {
    1733                 :          1 :     CPLString osFilename;
    1734         [ -  + ]:          1 :     CPLAssert( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1B:") );
    1735 [ +  - ][ +  - ]:          1 :     osFilename = poOpenInfo->pszFilename + strlen("SENTINEL2_L1B:");
                 [ +  - ]
    1736         [ +  - ]:          1 :     const char* pszPrecision = strrchr(osFilename.c_str(), ':');
    1737 [ +  + ][ +  - ]:          1 :     if( pszPrecision == NULL || pszPrecision == osFilename.c_str() )
         [ -  + ][ +  + ]
    1738                 :            :     {
    1739         [ +  - ]:          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid syntax for SENTINEL2_L1B:");
    1740                 :          1 :         return NULL;
    1741                 :            :     }
    1742                 :          1 :     const int nSubDSPrecision = atoi(pszPrecision + 1);
    1743 [ +  + ][ +  + ]:          1 :     if( nSubDSPrecision != 10 && nSubDSPrecision != 20 && nSubDSPrecision != 60 )
                 [ +  + ]
    1744                 :            :     {
    1745                 :            :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported precision: %d",
    1746         [ +  - ]:          1 :                  nSubDSPrecision);
    1747                 :          1 :         return NULL;
    1748                 :            :     }
    1749 [ +  - ][ +  - ]:          1 :     osFilename.resize( pszPrecision - osFilename.c_str() );
    1750                 :            : 
    1751                 :          1 :     CPLXMLNode* psRoot = NULL;
    1752         [ +  - ]:          1 :     std::set<CPLString> oSetBands;
    1753                 :            :     GDALDataset* poTmpDS = OpenL1BGranule( osFilename, &psRoot,
    1754 [ +  - ][ +  - ]:          1 :                                            nSubDSPrecision, &oSetBands);
    1755         [ +  + ]:          1 :     if( poTmpDS == NULL )
    1756                 :          1 :         return NULL;
    1757                 :            : 
    1758                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
    1759                 :            : 
    1760         [ +  - ]:          1 :     std::vector<CPLString> aosBands;
    1761 [ +  - ][ +  - ]:          1 :     for(std::set<CPLString>::const_iterator oIterBandnames = oSetBands.begin();
         [ +  - ][ +  + ]
    1762         [ +  - ]:          1 :                                             oIterBandnames != oSetBands.end();
    1763                 :            :                                         ++oIterBandnames)
    1764                 :            :     {
    1765 [ +  - ][ +  - ]:          1 :         aosBands.push_back(*oIterBandnames);
    1766                 :            :     }
    1767                 :            :     /* Put 2=Blue, 3=Green, 4=Band bands in RGB order for conveniency */
    1768 [ +  - ][ +  + ]:          1 :     if( aosBands.size() >= 3 &&
         [ +  + ][ +  - ]
         [ +  - ][ +  + ]
    1769 [ +  - ][ +  - ]:          1 :         aosBands[0] == "02" &&
    1770 [ +  - ][ +  - ]:          1 :         aosBands[1] == "03" &&
    1771 [ +  - ][ +  - ]:          1 :         aosBands[2] == "04" )
    1772                 :            :     {
    1773 [ +  - ][ +  - ]:          1 :         aosBands[0] = "04";
         [ +  - ][ +  - ]
    1774 [ +  - ][ +  - ]:          1 :         aosBands[2] = "02";
         [ +  - ][ +  - ]
    1775                 :            :     }
    1776                 :            : 
    1777                 :          1 :     int nBits = 0; /* 0 = unknown yet*/
    1778                 :          1 :     int nValMax = 0; /* 0 = unknown yet*/
    1779                 :          1 :     int nRows = 0;
    1780                 :          1 :     int nCols = 0;
    1781                 :            :     CPLXMLNode* psGranuleDimensions =
    1782         [ +  - ]:          1 :         CPLGetXMLNode(psRoot, "=Level-1B_Granule_ID.Geometric_Info.Granule_Dimensions");
    1783         [ +  + ]:          1 :     if( psGranuleDimensions == NULL )
    1784                 :            :     {
    1785 [ +  - ][ +  + ]:          1 :         for( size_t i=0; i<aosBands.size(); i++ )
    1786                 :            :         {
    1787                 :            :             CPLString osTile(SENTINEL2GetTilename(CPLGetPath(osFilename),
    1788                 :            :                                                   CPLGetBasename(osFilename),
    1789 [ +  - ][ +  - ]:          1 :                                                   aosBands[i]));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1790 [ +  - ][ +  - ]:          1 :             if( SENTINEL2GetTileInfo(osTile, &nCols, &nRows, &nBits) )
                 [ +  - ]
    1791                 :            :             {
    1792         [ +  - ]:          1 :                 if( nBits <= 16 )
    1793                 :          1 :                     nValMax = (1 << nBits) - 1;
    1794                 :            :                 else
    1795                 :            :                 {
    1796         [ #  # ]:          0 :                     CPLDebug("SENTINEL2", "Unexpected bit depth %d", nBits);
    1797                 :          1 :                     nValMax = 65535;
    1798                 :            :                 }
    1799                 :            :                 break;
    1800                 :            :             }
    1801 [ +  - ][ -  + ]:          1 :         }
    1802                 :            :     }
    1803                 :            :     else
    1804                 :            :     {
    1805         [ +  + ]:          1 :         for(CPLXMLNode* psIter = psGranuleDimensions->psChild; psIter != NULL;
    1806                 :            :                                                         psIter = psIter->psNext)
    1807                 :            :         {
    1808         [ +  + ]:          1 :             if( psIter->eType != CXT_Element )
    1809                 :          1 :                 continue;
    1810   [ +  -  +  + ]:          1 :             if( EQUAL(psIter->pszValue, "Size") &&
                 [ +  + ]
    1811         [ +  - ]:          1 :                 atoi(CPLGetXMLValue(psIter, "resolution", "")) == nSubDSPrecision )
    1812                 :            :             {
    1813         [ +  - ]:          1 :                 const char* pszRows = CPLGetXMLValue(psIter, "NROWS", NULL);
    1814         [ +  + ]:          1 :                 if( pszRows == NULL )
    1815                 :            :                 {
    1816                 :            :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    1817         [ +  - ]:          1 :                             "NROWS");
    1818 [ +  - ][ +  - ]:          1 :                     delete poTmpDS;
    1819                 :          1 :                     return NULL;
    1820                 :            :                 }
    1821         [ +  - ]:          1 :                 const char* pszCols = CPLGetXMLValue(psIter, "NCOLS", NULL);
    1822         [ +  + ]:          1 :                 if( pszCols == NULL )
    1823                 :            :                 {
    1824                 :            :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    1825         [ +  - ]:          1 :                             "NCOLS");
    1826 [ +  - ][ +  - ]:          1 :                     delete poTmpDS;
    1827                 :          1 :                     return NULL;
    1828                 :            :                 }
    1829                 :          1 :                 nRows = atoi(pszRows);
    1830                 :          1 :                 nCols = atoi(pszCols);
    1831                 :          1 :                 break;
    1832                 :            :             }
    1833                 :            :         }
    1834                 :            :     }
    1835 [ +  + ][ -  + ]:          1 :     if( nRows <= 0 || nCols <= 0 )
    1836                 :            :     {
    1837         [ +  - ]:          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find granule dimension");
    1838 [ +  - ][ +  - ]:          1 :         delete poTmpDS;
    1839                 :          1 :         return NULL;
    1840                 :            :     }
    1841                 :            : 
    1842 [ +  - ][ +  - ]:          1 :     SENTINEL2Dataset* poDS = new SENTINEL2Dataset(nCols, nRows);
    1843         [ +  - ]:          1 :     poDS->aosNonJP2Files.push_back(osFilename);
    1844                 :            : 
    1845                 :            :     // Transfer metadata
    1846 [ +  - ][ +  - ]:          1 :     poDS->GDALDataset::SetMetadata( poTmpDS->GetMetadata() );
    1847 [ +  - ][ +  - ]:          1 :     poDS->GDALDataset::SetMetadata( poTmpDS->GetMetadata("xml:SENTINEL2"), "xml:SENTINEL2" );
    1848                 :            : 
    1849 [ +  - ][ +  - ]:          1 :     delete poTmpDS;
    1850                 :            : 
    1851                 :            : /* -------------------------------------------------------------------- */
    1852                 :            : /*      Initialize bands.                                               */
    1853                 :            : /* -------------------------------------------------------------------- */
    1854                 :            : 
    1855 [ +  - ][ +  - ]:          1 :     int nSaturatedVal = atoi(CSLFetchNameValueDef(poDS->GetMetadata(), "SPECIAL_VALUE_SATURATED", "-1"));
    1856 [ +  - ][ +  - ]:          1 :     int nNodataVal = atoi(CSLFetchNameValueDef(poDS->GetMetadata(), "SPECIAL_VALUE_NODATA", "-1"));
    1857                 :            : 
    1858                 :            :     const bool bAlpha =
    1859 [ +  - ][ +  - ]:          1 :         CPLTestBool(SENTINEL2GetOption(poOpenInfo, "ALPHA", "FALSE"));
    1860 [ +  + ][ +  - ]:          1 :     const int nBands = ((bAlpha) ? 1 : 0) + static_cast<int>(aosBands.size());
    1861         [ +  + ]:          1 :     const int nAlphaBand = (!bAlpha) ? 0 : nBands;
    1862                 :          1 :     const GDALDataType eDT = GDT_UInt16;
    1863                 :            : 
    1864         [ +  + ]:          1 :     for(int nBand=1;nBand<=nBands;nBand++)
    1865                 :            :     {
    1866                 :          1 :         VRTSourcedRasterBand* poBand = NULL;
    1867                 :            : 
    1868         [ +  + ]:          1 :         if( nBand != nAlphaBand )
    1869                 :            :         {
    1870                 :            :             poBand = new VRTSourcedRasterBand( poDS, nBand, eDT,
    1871                 :            :                                                poDS->nRasterXSize,
    1872 [ +  - ][ +  - ]:          1 :                                                poDS->nRasterYSize );
    1873                 :            :         }
    1874                 :            :         else
    1875                 :            :         {
    1876                 :            :             poBand = new SENTINEL2AlphaBand ( poDS, nBand, eDT,
    1877                 :            :                                               poDS->nRasterXSize,
    1878                 :            :                                               poDS->nRasterYSize,
    1879                 :            :                                               nSaturatedVal,
    1880 [ +  - ][ +  - ]:          1 :                                               nNodataVal );
    1881                 :            :         }
    1882                 :            : 
    1883         [ +  - ]:          1 :         poDS->SetBand(nBand, poBand);
    1884         [ +  + ]:          1 :         if( nBand == nAlphaBand )
    1885         [ +  - ]:          1 :             poBand->SetColorInterpretation(GCI_AlphaBand);
    1886                 :            : 
    1887         [ +  - ]:          1 :         CPLString osBandName;
    1888         [ +  + ]:          1 :         if( nBand != nAlphaBand )
    1889                 :            :         {
    1890 [ +  - ][ +  - ]:          1 :             osBandName = aosBands[nBand-1];
    1891         [ +  - ]:          1 :             SENTINEL2SetBandMetadata( poBand, osBandName);
    1892                 :            :         }
    1893                 :            :         else
    1894 [ +  - ][ +  - ]:          1 :             osBandName = aosBands[0];
    1895                 :            : 
    1896                 :            :         CPLString osTile(SENTINEL2GetTilename(CPLGetPath(osFilename),
    1897                 :            :                                               CPLGetBasename(osFilename),
    1898 [ +  - ][ +  - ]:          1 :                                               osBandName));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1899                 :            : 
    1900                 :          1 :         bool bTileFound = false;
    1901         [ +  + ]:          1 :         if( nValMax == 0 )
    1902                 :            :         {
    1903                 :            :             /* It is supposed to be 12 bits, but some products have 15 bits */
    1904 [ +  - ][ +  - ]:          1 :             if( SENTINEL2GetTileInfo(osTile, NULL, NULL, &nBits) )
                 [ +  + ]
    1905                 :            :             {
    1906                 :          1 :                 bTileFound = true;
    1907         [ +  - ]:          1 :                 if( nBits <= 16 )
    1908                 :          1 :                     nValMax = (1 << nBits) - 1;
    1909                 :            :                 else
    1910                 :            :                 {
    1911         [ #  # ]:          0 :                     CPLDebug("SENTINEL2", "Unexpected bit depth %d", nBits);
    1912                 :          0 :                     nValMax = 65535;
    1913                 :            :                 }
    1914                 :            :             }
    1915                 :            :         }
    1916                 :            :         else
    1917                 :            :         {
    1918                 :            :             VSIStatBufL sStat;
    1919 [ +  - ][ +  - ]:          1 :             if( VSIStatExL(osTile, &sStat, VSI_STAT_EXISTS_FLAG) == 0 )
                 [ +  - ]
    1920                 :          1 :                 bTileFound = true;
    1921                 :            :         }
    1922         [ +  + ]:          1 :         if( !bTileFound )
    1923                 :            :         {
    1924                 :            :             CPLError(CE_Warning, CPLE_AppDefined,
    1925                 :            :                     "Tile %s not found on filesystem. Skipping it",
    1926 [ +  - ][ +  - ]:          1 :                     osTile.c_str());
    1927                 :          1 :             continue;
    1928                 :            :         }
    1929                 :            : 
    1930                 :            :         GDALProxyPoolDataset* proxyDS =
    1931                 :            :             new GDALProxyPoolDataset( osTile,
    1932                 :            :                                       poDS->nRasterXSize,
    1933                 :            :                                       poDS->nRasterYSize,
    1934                 :            :                                       GA_ReadOnly,
    1935 [ +  - ][ +  - ]:          1 :                                       TRUE );
                 [ +  - ]
    1936         [ +  - ]:          1 :         proxyDS->AddSrcBandDescription(eDT, 128, 128);
    1937                 :            : 
    1938         [ +  + ]:          1 :         if( nBand != nAlphaBand )
    1939                 :            :         {
    1940                 :            :             poBand->AddSimpleSource( proxyDS->GetRasterBand(1),
    1941                 :            :                                     0, 0,
    1942                 :            :                                     poDS->nRasterXSize,
    1943                 :            :                                     poDS->nRasterYSize,
    1944                 :            :                                     0,
    1945                 :            :                                     0,
    1946                 :            :                                     poDS->nRasterXSize,
    1947 [ +  - ][ +  - ]:          1 :                                     poDS->nRasterYSize);
    1948                 :            :         }
    1949                 :            :         else
    1950                 :            :         {
    1951                 :            :             poBand->AddComplexSource( proxyDS->GetRasterBand(1),
    1952                 :            :                                         0, 0,
    1953                 :            :                                         poDS->nRasterXSize,
    1954                 :            :                                         poDS->nRasterYSize,
    1955                 :            :                                         0,
    1956                 :            :                                         0,
    1957                 :            :                                         poDS->nRasterXSize,
    1958                 :            :                                         poDS->nRasterYSize,
    1959                 :            :                                         nValMax /* offset */,
    1960 [ +  - ][ +  - ]:          1 :                                         0 /* scale */);
    1961                 :            :         }
    1962                 :            : 
    1963         [ +  - ]:          1 :         proxyDS->Dereference();
    1964                 :            : 
    1965         [ +  - ]:          1 :         if( (nBits % 8) != 0 )
    1966                 :            :         {
    1967                 :            :             poBand->SetMetadataItem("NBITS",
    1968 [ +  - ][ +  - ]:          1 :                                 CPLSPrintf("%d", nBits), "IMAGE_STRUCTURE");
    1969                 :            :         }
    1970 [ +  - ][ +  + ]:          1 :     }
                 [ +  - ]
    1971                 :            : 
    1972                 :            : /* -------------------------------------------------------------------- */
    1973                 :            : /*      Add georeferencing.                                             */
    1974                 :            : /* -------------------------------------------------------------------- */
    1975                 :            :     //const char* pszDirection = poDS->GetMetadataItem("DATATAKE_1_SENSING_ORBIT_DIRECTION");
    1976         [ +  - ]:          1 :     const char* pszFootprint = poDS->GetMetadataItem("FOOTPRINT");
    1977         [ +  + ]:          1 :     if( pszFootprint != NULL )
    1978                 :            :     {
    1979                 :            :         // For descending orbits, we have observed that the order of points in
    1980                 :            :         // the polygon is UL, LL, LR, UR. That might not be true for ascending orbits
    1981                 :            :         // but let's assume it...
    1982                 :          1 :         char* pszFootprintC = const_cast<char*>(pszFootprint);
    1983                 :          1 :         OGRGeometry* poGeom = NULL;
    1984 [ +  - ][ +  - ]:          1 :         if( OGRGeometryFactory::createFromWkt( &pszFootprintC, NULL, &poGeom)
         [ +  - ][ +  - ]
                 [ +  - ]
    1985                 :            :                                                                 == OGRERR_NONE &&
    1986                 :            :             poGeom != NULL &&
    1987 [ +  - ][ +  - ]:          1 :             wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
    1988                 :            :         {
    1989                 :            :             OGRLinearRing* poRing =
    1990         [ +  - ]:          1 :                 reinterpret_cast<OGRPolygon*>(poGeom)->getExteriorRing();
    1991 [ +  - ][ +  - ]:          1 :             if( poRing != NULL && poRing->getNumPoints() == 5 )
         [ +  - ][ +  - ]
    1992                 :            :             {
    1993                 :            :                 GDAL_GCP    asGCPList[5];
    1994                 :          1 :                 memset( asGCPList, 0, sizeof(asGCPList) );
    1995         [ +  + ]:          1 :                 for(int i=0;i<4;i++)
    1996                 :            :                 {
    1997         [ +  - ]:          1 :                     asGCPList[i].dfGCPX = poRing->getX(i);
    1998         [ +  - ]:          1 :                     asGCPList[i].dfGCPY = poRing->getY(i);
    1999         [ +  - ]:          1 :                     asGCPList[i].dfGCPZ = poRing->getZ(i);
    2000                 :            :                 }
    2001                 :          1 :                 asGCPList[0].dfGCPPixel = 0;
    2002                 :          1 :                 asGCPList[0].dfGCPLine = 0;
    2003                 :          1 :                 asGCPList[1].dfGCPPixel = 0;
    2004                 :          1 :                 asGCPList[1].dfGCPLine = poDS->nRasterYSize;
    2005                 :          1 :                 asGCPList[2].dfGCPPixel = poDS->nRasterXSize;
    2006                 :          1 :                 asGCPList[2].dfGCPLine = poDS->nRasterYSize;
    2007                 :          1 :                 asGCPList[3].dfGCPPixel = poDS->nRasterXSize;
    2008                 :          1 :                 asGCPList[3].dfGCPLine = 0;
    2009                 :            : 
    2010                 :          1 :                 int nGCPCount = 4;
    2011                 :            :                 CPLXMLNode* psGeometryHeader =
    2012                 :            :                     CPLGetXMLNode(psRoot,
    2013                 :            :                                   "=Level-1B_Granule_ID.Geometric_Info."
    2014         [ +  - ]:          1 :                                   "Granule_Position.Geometric_Header");
    2015         [ +  - ]:          1 :                 if( psGeometryHeader != NULL )
    2016                 :            :                 {
    2017                 :            :                     const char* pszGC =
    2018         [ +  - ]:          1 :                         CPLGetXMLValue(psGeometryHeader, "GROUND_CENTER", NULL);
    2019                 :            :                     const char* pszQLCenter =
    2020         [ +  - ]:          1 :                         CPLGetXMLValue(psGeometryHeader, "QL_CENTER", NULL);
    2021 [ +  - ][ +  - ]:          1 :                     if( pszGC != NULL && pszQLCenter != NULL && EQUAL(pszQLCenter, "0 0") )
                 [ +  - ]
    2022                 :            :                     {
    2023         [ +  - ]:          1 :                         char** papszTokens = CSLTokenizeString(pszGC);
    2024 [ +  - ][ +  - ]:          1 :                         if( CSLCount(papszTokens) >= 2 )
    2025                 :            :                         {
    2026                 :          1 :                             nGCPCount = 5;
    2027         [ +  - ]:          1 :                             asGCPList[4].dfGCPX = CPLAtof(papszTokens[1]);
    2028         [ +  - ]:          1 :                             asGCPList[4].dfGCPY = CPLAtof(papszTokens[0]);
    2029 [ +  - ][ +  - ]:          1 :                             if( CSLCount(papszTokens) >= 3 )
    2030         [ +  - ]:          1 :                                 asGCPList[4].dfGCPZ = CPLAtof(papszTokens[2]);
    2031                 :          1 :                             asGCPList[4].dfGCPLine = poDS->nRasterYSize / 2.0;
    2032                 :          1 :                             asGCPList[4].dfGCPPixel = poDS->nRasterXSize / 2.0;
    2033                 :            :                         }
    2034         [ +  - ]:          1 :                         CSLDestroy(papszTokens);
    2035                 :            :                     }
    2036                 :            :                 }
    2037                 :            : 
    2038         [ +  - ]:          1 :                 poDS->SetGCPs( nGCPCount, asGCPList, SRS_WKT_WGS84 );
    2039         [ +  - ]:          1 :                 GDALDeinitGCPs( nGCPCount, asGCPList );
    2040                 :            :             }
    2041                 :            :         }
    2042 [ +  - ][ +  - ]:          1 :         delete poGeom;
    2043                 :            :     }
    2044                 :            : 
    2045                 :            : /* -------------------------------------------------------------------- */
    2046                 :            : /*      Initialize overview information.                                */
    2047                 :            : /* -------------------------------------------------------------------- */
    2048         [ +  - ]:          1 :     poDS->SetDescription( poOpenInfo->pszFilename );
    2049         [ +  - ]:          1 :     CPLString osOverviewFile;
    2050                 :            :     osOverviewFile = CPLSPrintf("%s_%dm.tif.ovr",
    2051 [ +  - ][ +  - ]:          1 :                                 osFilename.c_str(), nSubDSPrecision);
         [ +  - ][ +  - ]
                 [ +  - ]
    2052 [ +  - ][ +  - ]:          1 :     poDS->SetMetadataItem( "OVERVIEW_FILE", osOverviewFile, "OVERVIEWS" );
    2053         [ +  - ]:          1 :     poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );
    2054                 :            : 
    2055 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
    2056                 :            : }
    2057                 :            : 
    2058                 :            : /************************************************************************/
    2059                 :            : /*                 SENTINEL2GetGranuleList_L1CSafeCompact()             */
    2060                 :            : /************************************************************************/
    2061                 :            : 
    2062                 :          1 : static bool SENTINEL2GetGranuleList_L1CSafeCompact(CPLXMLNode* psMainMTD,
    2063                 :            :                                     const char* pszFilename,
    2064                 :            :                                     std::vector<L1CSafeCompatGranuleDescription>& osList)
    2065                 :            : {
    2066                 :            :     CPLXMLNode* psProductInfo = CPLGetXMLNode(psMainMTD,
    2067                 :          1 :                                 "=Level-1C_User_Product.General_Info.Product_Info");
    2068         [ -  + ]:          1 :     if( psProductInfo == NULL )
    2069                 :            :     {
    2070                 :            :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    2071                 :          0 :                         "=Level-1C_User_Product.General_Info.Product_Info");
    2072                 :          0 :         return false;
    2073                 :            :     }
    2074                 :            : 
    2075                 :            :     CPLXMLNode* psProductOrganisation =
    2076                 :          1 :                         CPLGetXMLNode(psProductInfo, "Product_Organisation");
    2077         [ -  + ]:          1 :     if( psProductOrganisation == NULL )
    2078                 :            :     {
    2079                 :          0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", "Product_Organisation");
    2080                 :          0 :         return false;
    2081                 :            :     }
    2082                 :            : 
    2083                 :          1 :     CPLString osDirname( CPLGetDirname(pszFilename) );
    2084                 :            : #ifdef HAVE_READLINK
    2085                 :            :     char szPointerFilename[2048];
    2086                 :            :     int nBytes = static_cast<int>(readlink(pszFilename, szPointerFilename,
    2087                 :          1 :                                            sizeof(szPointerFilename)));
    2088         [ -  + ]:          1 :     if (nBytes != -1)
    2089                 :            :     {
    2090                 :            :         const int nOffset =
    2091         [ #  # ]:          0 :             std::min(nBytes, static_cast<int>(sizeof(szPointerFilename)-1));
    2092                 :          0 :         szPointerFilename[nOffset] = '\0';
    2093 [ #  # ][ #  # ]:          0 :         osDirname = CPLGetDirname(szPointerFilename);
         [ #  # ][ #  # ]
    2094                 :            :     }
    2095                 :            : #endif
    2096                 :            : 
    2097         [ +  - ]:          1 :     const char chSeparator = SENTINEL2GetPathSeparator(osDirname);
    2098         [ +  + ]:          1 :     for(CPLXMLNode* psIter = psProductOrganisation->psChild; psIter != NULL;
    2099                 :            :                                                     psIter = psIter->psNext )
    2100                 :            :     {
    2101 [ +  - ][ -  + ]:          1 :         if( psIter->eType != CXT_Element ||
    2102                 :          1 :             !EQUAL(psIter->pszValue, "Granule_List") )
    2103                 :            :         {
    2104                 :          0 :             continue;
    2105                 :            :         }
    2106         [ +  + ]:          1 :         for(CPLXMLNode* psIter2 = psIter->psChild; psIter2 != NULL;
    2107                 :            :                                                      psIter2 = psIter2->psNext )
    2108                 :            :         {
    2109 [ +  - ][ -  + ]:          1 :             if( psIter2->eType != CXT_Element ||
    2110                 :          1 :                 !EQUAL(psIter2->pszValue, "Granule") )
    2111                 :            :             {
    2112                 :          0 :                 continue;
    2113                 :            :             }
    2114                 :            : 
    2115         [ +  - ]:          1 :             const char* pszImageFile = CPLGetXMLValue(psIter2, "IMAGE_FILE", NULL);
    2116 [ +  - ][ -  + ]:          1 :             if( pszImageFile == NULL || strlen(pszImageFile) < 3 )
    2117                 :            :             {
    2118         [ #  # ]:          0 :                 CPLDebug("SENTINEL2", "Missing IMAGE_FILE element");
    2119                 :          0 :                 continue;
    2120                 :            :             }
    2121         [ +  - ]:          1 :             L1CSafeCompatGranuleDescription oDesc;
    2122 [ +  - ][ +  - ]:          1 :             oDesc.osBandPrefixPath = osDirname + chSeparator + pszImageFile;
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    2123 [ +  - ][ +  - ]:          1 :             oDesc.osBandPrefixPath.resize( oDesc.osBandPrefixPath.size() - 3 ); // strip B12
    2124                 :            :             // GRANULE/L1C_T30TXT_A007999_20170102T111441/IMG_DATA/T30TXT_20170102T111442_B12 -->
    2125                 :            :             // GRANULE/L1C_T30TXT_A007999_20170102T111441/MTD_TL.xml
    2126                 :            :             oDesc.osMTDTLPath = osDirname + chSeparator +
    2127                 :            :                                 CPLGetDirname(CPLGetDirname(pszImageFile)) +
    2128 [ +  - ][ +  - ]:          1 :                                 chSeparator + "MTD_TL.xml";
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    2129         [ +  - ]:          1 :             osList.push_back(oDesc);
    2130         [ +  - ]:          1 :         }
    2131                 :            :     }
    2132                 :            : 
    2133                 :          1 :     return true;
    2134                 :            : }
    2135                 :            : 
    2136                 :            : /************************************************************************/
    2137                 :            : /*                           OpenL1C_L2A()                              */
    2138                 :            : /************************************************************************/
    2139                 :            : 
    2140                 :          1 : GDALDataset *SENTINEL2Dataset::OpenL1C_L2A( const char* pszFilename,
    2141                 :            :                                             SENTINEL2Level eLevel )
    2142                 :            : {
    2143                 :          1 :     CPLXMLNode *psRoot = CPLParseXMLFile( pszFilename );
    2144         [ +  + ]:          1 :     if( psRoot == NULL )
    2145                 :          1 :         return NULL;
    2146                 :            : 
    2147                 :          1 :     char* pszOriginalXML = CPLSerializeXMLTree(psRoot);
    2148                 :          1 :     CPLString osOriginalXML;
    2149         [ +  - ]:          1 :     if( pszOriginalXML )
    2150 [ +  - ][ +  - ]:          1 :         osOriginalXML = pszOriginalXML;
                 [ +  - ]
    2151         [ +  - ]:          1 :     CPLFree(pszOriginalXML);
    2152                 :            : 
    2153                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
    2154         [ +  - ]:          1 :     CPLStripXMLNamespace(psRoot, NULL, TRUE);
    2155                 :            : 
    2156                 :            :     const char* pszNodePath = (eLevel == SENTINEL2_L1C ) ?
    2157                 :            :                 "=Level-1C_User_Product.General_Info.Product_Info" :
    2158         [ +  + ]:          1 :                 "=Level-2A_User_Product.General_Info.L2A_Product_Info";
    2159         [ +  - ]:          1 :     CPLXMLNode* psProductInfo = CPLGetXMLNode(psRoot, pszNodePath);
    2160         [ +  + ]:          1 :     if( psProductInfo == NULL )
    2161                 :            :     {
    2162         [ +  - ]:          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", pszNodePath);
    2163                 :          1 :         return NULL;
    2164                 :            :     }
    2165                 :            : 
    2166                 :            :     const bool bIsSafeCompact = eLevel == SENTINEL2_L1C &&
    2167 [ +  + ][ +  - ]:          1 :         EQUAL(CPLGetXMLValue(psProductInfo, "Query_Options.PRODUCT_FORMAT", ""),
                 [ +  + ]
    2168                 :            :               "SAFE_COMPACT");
    2169                 :            : 
    2170         [ +  - ]:          1 :     std::set<int> oSetResolutions;
    2171         [ +  - ]:          1 :     std::map<int, std::set<CPLString> > oMapResolutionsToBands;
    2172         [ +  + ]:          1 :     if( bIsSafeCompact )
    2173                 :            :     {
    2174         [ +  + ]:          1 :         for(unsigned int i = 0; i < NB_BANDS; ++i)
    2175                 :            :         {
    2176                 :          1 :             const SENTINEL2BandDescription * psBandDesc = &asBandDesc[i];
    2177         [ +  - ]:          1 :             oSetResolutions.insert( psBandDesc->nResolution );
    2178         [ +  - ]:          1 :             CPLString osName = psBandDesc->pszBandName + 1; /* skip B character */
    2179 [ +  - ][ +  + ]:          1 :             if( atoi(osName) < 10 )
    2180 [ +  - ][ +  - ]:          1 :                 osName = "0" + osName;
         [ +  - ][ +  - ]
                 [ +  - ]
    2181 [ +  - ][ +  - ]:          1 :             oMapResolutionsToBands[psBandDesc->nResolution].insert(osName);
    2182         [ +  - ]:          1 :         }
    2183                 :            :     }
    2184 [ +  + ][ +  + ]:          1 :     else if( eLevel == SENTINEL2_L1C &&
                 [ +  + ]
    2185                 :            :         !SENTINEL2GetResolutionSet(psProductInfo,
    2186                 :            :                                    oSetResolutions,
    2187         [ +  - ]:          1 :                                    oMapResolutionsToBands) )
    2188                 :            :     {
    2189                 :          1 :         return NULL;
    2190                 :            :     }
    2191                 :            : 
    2192         [ +  - ]:          1 :     std::vector<CPLString> aosGranuleList;
    2193         [ +  + ]:          1 :     if( bIsSafeCompact )
    2194                 :            :     {
    2195         [ +  - ]:          1 :         std::vector<L1CSafeCompatGranuleDescription> aoL1CSafeCompactGranuleList;
    2196         [ -  + ]:          1 :         if( !SENTINEL2GetGranuleList_L1CSafeCompact(psRoot, pszFilename,
    2197         [ +  - ]:          1 :                                                     aoL1CSafeCompactGranuleList) )
    2198                 :            :         {
    2199                 :          0 :             return NULL;
    2200                 :            :         }
    2201 [ +  - ][ +  + ]:          1 :         for(size_t i=0;i<aoL1CSafeCompactGranuleList.size();++i)
    2202                 :            :         {
    2203                 :            :             aosGranuleList.push_back(
    2204 [ +  - ][ +  - ]:          1 :                 aoL1CSafeCompactGranuleList[i].osMTDTLPath);
    2205 [ +  - ][ +  - ]:          1 :         }
    2206                 :            :     }
    2207         [ -  + ]:          1 :     else if( !SENTINEL2GetGranuleList(psRoot,
    2208                 :            :                                  eLevel,
    2209                 :            :                                  pszFilename,
    2210                 :            :                                  aosGranuleList,
    2211                 :            :                                  (eLevel == SENTINEL2_L1C) ? NULL :
    2212                 :            :                                                     &oSetResolutions,
    2213                 :            :                                  (eLevel == SENTINEL2_L1C) ? NULL :
    2214 [ +  + ][ +  + ]:          1 :                                                     &oMapResolutionsToBands) )
                 [ +  - ]
    2215                 :            :     {
    2216                 :          0 :         return NULL;
    2217                 :            :     }
    2218                 :            : 
    2219         [ +  - ]:          1 :     std::set<int> oSetEPSGCodes;
    2220 [ +  - ][ +  + ]:          1 :     for(size_t i=0;i<aosGranuleList.size();i++)
    2221                 :            :     {
    2222                 :          1 :         int nEPSGCode = 0;
    2223 [ +  - ][ +  + ]:          1 :         if( SENTINEL2GetGranuleInfo(eLevel,
    2224         [ +  - ]:          1 :                                     aosGranuleList[i],
    2225 [ +  - ][ +  - ]:          1 :                                     *(oSetResolutions.begin()), &nEPSGCode) )
    2226                 :            :         {
    2227         [ +  - ]:          1 :             oSetEPSGCodes.insert(nEPSGCode);
    2228                 :            :         }
    2229                 :            :     }
    2230                 :            : 
    2231 [ +  - ][ +  - ]:          1 :     SENTINEL2DatasetContainer* poDS = new SENTINEL2DatasetContainer();
    2232                 :            :     char** papszMD = SENTINEL2GetUserProductMetadata(psRoot,
    2233                 :            :         (eLevel == SENTINEL2_L1C ) ? "Level-1C_User_Product" :
    2234 [ +  + ][ +  - ]:          1 :                                      "Level-2A_User_Product");
    2235         [ +  - ]:          1 :     poDS->GDALDataset::SetMetadata(papszMD);
    2236         [ +  - ]:          1 :     CSLDestroy(papszMD);
    2237                 :            : 
    2238 [ +  - ][ +  - ]:          1 :     if( !osOriginalXML.empty() )
    2239                 :            :     {
    2240                 :            :         char* apszXMLMD[2];
    2241         [ +  - ]:          1 :         apszXMLMD[0] = const_cast<char*>(osOriginalXML.c_str());
    2242                 :          1 :         apszXMLMD[1] = NULL;
    2243         [ +  - ]:          1 :         poDS->GDALDataset::SetMetadata(apszXMLMD, "xml:SENTINEL2");
    2244                 :            :     }
    2245                 :            : 
    2246                 :            :     const char* pszPrefix = (eLevel == SENTINEL2_L1C) ? "SENTINEL2_L1C" :
    2247         [ +  + ]:          1 :                                                         "SENTINEL2_L2A";
    2248                 :            : 
    2249                 :            :     /* Create subdatsets per resolution (10, 20, 60m) and EPSG codes */
    2250                 :          1 :     int iSubDSNum = 1;
    2251 [ +  - ][ +  - ]:          1 :     for(std::set<int>::const_iterator oIterRes = oSetResolutions.begin();
         [ +  - ][ +  + ]
    2252         [ +  - ]:          1 :                                 oIterRes != oSetResolutions.end();
    2253                 :            :                               ++oIterRes )
    2254                 :            :     {
    2255         [ +  - ]:          1 :         const int nResolution = *oIterRes;
    2256                 :            : 
    2257 [ +  - ][ +  - ]:          1 :         for(std::set<int>::const_iterator oIterEPSG = oSetEPSGCodes.begin();
         [ +  - ][ +  + ]
    2258         [ +  - ]:          1 :                                     oIterEPSG != oSetEPSGCodes.end();
    2259                 :            :                                   ++oIterEPSG )
    2260                 :            :         {
    2261         [ +  - ]:          1 :             const int nEPSGCode = *oIterEPSG;
    2262                 :            :             poDS->GDALDataset::SetMetadataItem(
    2263                 :            :                 CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    2264                 :            :                 CPLSPrintf("%s:%s:%dm:EPSG_%d",
    2265                 :            :                             pszPrefix, pszFilename, nResolution, nEPSGCode),
    2266 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    2267                 :            : 
    2268                 :            :             CPLString osBandNames = SENTINEL2GetBandListForResolution(
    2269 [ +  - ][ +  - ]:          1 :                                             oMapResolutionsToBands[nResolution]);
    2270                 :            : 
    2271                 :            :             CPLString osDesc(CPLSPrintf("Bands %s with %dm resolution",
    2272 [ +  - ][ +  - ]:          1 :                                         osBandNames.c_str(), nResolution));
                 [ +  - ]
    2273 [ +  - ][ +  - ]:          1 :             if( nEPSGCode >= 32601 && nEPSGCode <= 32660 )
    2274 [ +  - ][ +  - ]:          1 :                 osDesc += CPLSPrintf(", UTM %dN", nEPSGCode - 32600);
    2275 [ #  # ][ #  # ]:          0 :             else if( nEPSGCode >= 32701 && nEPSGCode <= 32760 )
    2276 [ #  # ][ #  # ]:          0 :                 osDesc += CPLSPrintf(", UTM %dS", nEPSGCode - 32700);
    2277                 :            :             else
    2278 [ #  # ][ #  # ]:          0 :                 osDesc += CPLSPrintf(", EPSG:%d", nEPSGCode);
    2279                 :            :             poDS->GDALDataset::SetMetadataItem(
    2280                 :            :                 CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    2281                 :            :                 osDesc.c_str(),
    2282 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    2283                 :            : 
    2284                 :          1 :             iSubDSNum ++;
    2285 [ +  - ][ +  - ]:          1 :         }
    2286                 :            :     }
    2287                 :            : 
    2288                 :            :     /* Expose TCI or PREVIEW subdatasets */
    2289         [ +  + ]:          1 :     if( bIsSafeCompact )
    2290                 :            :     {
    2291 [ +  - ][ +  - ]:          1 :         for(std::set<int>::const_iterator oIterEPSG = oSetEPSGCodes.begin();
         [ +  - ][ +  + ]
    2292         [ +  - ]:          1 :                                         oIterEPSG != oSetEPSGCodes.end();
    2293                 :            :                                     ++oIterEPSG )
    2294                 :            :         {
    2295         [ +  - ]:          1 :             const int nEPSGCode = *oIterEPSG;
    2296                 :            :             poDS->GDALDataset::SetMetadataItem(
    2297                 :            :                 CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    2298                 :            :                 CPLSPrintf("%s:%s:TCI:EPSG_%d",
    2299                 :            :                             pszPrefix, pszFilename, nEPSGCode),
    2300 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    2301                 :            : 
    2302         [ +  - ]:          1 :             CPLString osDesc("True color image");
    2303 [ +  - ][ +  - ]:          1 :             if( nEPSGCode >= 32601 && nEPSGCode <= 32660 )
    2304 [ +  - ][ +  - ]:          1 :                 osDesc += CPLSPrintf(", UTM %dN", nEPSGCode - 32600);
    2305 [ #  # ][ #  # ]:          0 :             else if( nEPSGCode >= 32701 && nEPSGCode <= 32760 )
    2306 [ #  # ][ #  # ]:          0 :                 osDesc += CPLSPrintf(", UTM %dS", nEPSGCode - 32700);
    2307                 :            :             else
    2308 [ #  # ][ #  # ]:          0 :                 osDesc += CPLSPrintf(", EPSG:%d", nEPSGCode);
    2309                 :            :             poDS->GDALDataset::SetMetadataItem(
    2310                 :            :                 CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    2311                 :            :                 osDesc.c_str(),
    2312 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    2313                 :            : 
    2314                 :          1 :             iSubDSNum ++;
    2315         [ +  - ]:          1 :         }
    2316                 :            :     }
    2317                 :            :     else
    2318                 :            :     {
    2319 [ +  - ][ +  - ]:          1 :         for(std::set<int>::const_iterator oIterEPSG = oSetEPSGCodes.begin();
         [ +  - ][ +  + ]
    2320         [ +  - ]:          1 :                                         oIterEPSG != oSetEPSGCodes.end();
    2321                 :            :                                     ++oIterEPSG )
    2322                 :            :         {
    2323         [ +  - ]:          1 :             const int nEPSGCode = *oIterEPSG;
    2324                 :            :             poDS->GDALDataset::SetMetadataItem(
    2325                 :            :                 CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    2326                 :            :                 CPLSPrintf("%s:%s:PREVIEW:EPSG_%d",
    2327                 :            :                             pszPrefix, pszFilename, nEPSGCode),
    2328 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    2329                 :            : 
    2330         [ +  - ]:          1 :             CPLString osDesc("RGB preview");
    2331 [ +  - ][ +  - ]:          1 :             if( nEPSGCode >= 32601 && nEPSGCode <= 32660 )
    2332 [ +  - ][ +  - ]:          1 :                 osDesc += CPLSPrintf(", UTM %dN", nEPSGCode - 32600);
    2333 [ #  # ][ #  # ]:          0 :             else if( nEPSGCode >= 32701 && nEPSGCode <= 32760 )
    2334 [ #  # ][ #  # ]:          0 :                 osDesc += CPLSPrintf(", UTM %dS", nEPSGCode - 32700);
    2335                 :            :             else
    2336 [ #  # ][ #  # ]:          0 :                 osDesc += CPLSPrintf(", EPSG:%d", nEPSGCode);
    2337                 :            :             poDS->GDALDataset::SetMetadataItem(
    2338                 :            :                 CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    2339                 :            :                 osDesc.c_str(),
    2340 [ +  - ][ +  - ]:          1 :                 "SUBDATASETS");
                 [ +  - ]
    2341                 :            : 
    2342                 :          1 :             iSubDSNum ++;
    2343         [ +  - ]:          1 :         }
    2344                 :            :     }
    2345                 :            : 
    2346                 :            :     pszNodePath = (eLevel == SENTINEL2_L1C ) ?
    2347                 :            :         "=Level-1C_User_Product.Geometric_Info.Product_Footprint."
    2348                 :            :         "Product_Footprint.Global_Footprint.EXT_POS_LIST" :
    2349                 :            :         "=Level-2A_User_Product.Geometric_Info.Product_Footprint."
    2350         [ +  + ]:          1 :         "Product_Footprint.Global_Footprint.EXT_POS_LIST";
    2351         [ +  - ]:          1 :     const char* pszPosList = CPLGetXMLValue(psRoot, pszNodePath, NULL);
    2352         [ +  + ]:          1 :     if( pszPosList != NULL )
    2353                 :            :     {
    2354         [ +  - ]:          1 :         CPLString osPolygon = SENTINEL2GetPolygonWKTFromPosList(pszPosList);
    2355 [ +  - ][ +  - ]:          1 :         if( !osPolygon.empty() )
    2356 [ +  - ][ +  - ]:          1 :             poDS->GDALDataset::SetMetadataItem("FOOTPRINT", osPolygon.c_str());
                 [ +  - ]
    2357                 :            :     }
    2358                 :            : 
    2359 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
                 [ +  - ]
    2360                 :            : }
    2361                 :            : 
    2362                 :            : /************************************************************************/
    2363                 :            : /*                    SENTINEL2GetL1BCTileMetadata()                    */
    2364                 :            : /************************************************************************/
    2365                 :            : 
    2366                 :            : static
    2367                 :          1 : char** SENTINEL2GetL1BCTileMetadata( CPLXMLNode* psMainMTD )
    2368                 :            : {
    2369                 :          1 :     CPLStringList aosList;
    2370                 :            : 
    2371                 :            :     CPLXMLNode* psRoot =  CPLGetXMLNode(psMainMTD,
    2372         [ +  - ]:          1 :                                         "=Level-1C_Tile_ID");
    2373         [ -  + ]:          1 :     if( psRoot == NULL )
    2374                 :            :     {
    2375                 :            :         CPLError(CE_Failure, CPLE_AppDefined,
    2376         [ #  # ]:          0 :                  "Cannot find =Level-1C_Tile_ID");
    2377                 :          0 :         return NULL;
    2378                 :            :     }
    2379                 :            :     CPLXMLNode* psGeneralInfo = CPLGetXMLNode(psRoot,
    2380         [ +  - ]:          1 :                                               "General_Info");
    2381 [ +  - ][ +  + ]:          1 :     for( CPLXMLNode* psIter = (psGeneralInfo ? psGeneralInfo->psChild : NULL);
    2382                 :            :                      psIter != NULL;
    2383                 :            :                      psIter = psIter->psNext )
    2384                 :            :     {
    2385         [ -  + ]:          1 :         if( psIter->eType != CXT_Element )
    2386                 :          0 :             continue;
    2387         [ +  - ]:          1 :         const char* pszValue = CPLGetXMLValue(psIter, NULL, NULL);
    2388         [ +  + ]:          1 :         if( pszValue != NULL )
    2389                 :            :         {
    2390         [ +  - ]:          1 :             aosList.AddNameValue( psIter->pszValue, pszValue );
    2391                 :            :         }
    2392                 :            :     }
    2393                 :            : 
    2394         [ +  - ]:          1 :     CPLXMLNode* psQII = CPLGetXMLNode(psRoot, "Quality_Indicators_Info");
    2395         [ +  + ]:          1 :     if( psQII != NULL )
    2396                 :            :     {
    2397         [ +  - ]:          1 :         CPLXMLNode* psICCQI = CPLGetXMLNode(psQII, "Image_Content_QI");
    2398 [ +  - ][ +  + ]:          1 :         for( CPLXMLNode* psIter = (psICCQI ? psICCQI->psChild : NULL);
    2399                 :            :                      psIter != NULL;
    2400                 :            :                      psIter = psIter->psNext )
    2401                 :            :         {
    2402         [ -  + ]:          1 :             if( psIter->eType != CXT_Element )
    2403                 :          0 :                 continue;
    2404 [ +  - ][ +  - ]:          1 :             if( psIter->psChild != NULL && psIter->psChild->eType == CXT_Text )
    2405                 :            :             {
    2406                 :            :                 aosList.AddNameValue( psIter->pszValue,
    2407         [ +  - ]:          1 :                                     psIter->psChild->pszValue );
    2408                 :            :             }
    2409                 :            :         }
    2410                 :            :     }
    2411                 :            : 
    2412         [ +  - ]:          1 :     return aosList.StealList();
    2413                 :            : }
    2414                 :            : 
    2415                 :            : /************************************************************************/
    2416                 :            : /*                              OpenL1CTile()                           */
    2417                 :            : /************************************************************************/
    2418                 :            : 
    2419                 :          1 : GDALDataset *SENTINEL2Dataset::OpenL1CTile( const char* pszFilename,
    2420                 :            :                                             CPLXMLNode** ppsRootMainMTD,
    2421                 :            :                                             int nResolutionOfInterest,
    2422                 :            :                                             std::set<CPLString>* poBandSet )
    2423                 :            : {
    2424                 :          1 :     CPLXMLNode *psRoot = CPLParseXMLFile( pszFilename );
    2425         [ +  + ]:          1 :     if( psRoot == NULL )
    2426                 :          1 :         return NULL;
    2427                 :            : 
    2428                 :          1 :     char* pszOriginalXML = CPLSerializeXMLTree(psRoot);
    2429                 :          1 :     CPLString osOriginalXML;
    2430         [ +  - ]:          1 :     if( pszOriginalXML )
    2431 [ +  - ][ +  - ]:          1 :         osOriginalXML = pszOriginalXML;
                 [ +  - ]
    2432         [ +  - ]:          1 :     CPLFree(pszOriginalXML);
    2433                 :            : 
    2434                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
    2435         [ +  - ]:          1 :     CPLStripXMLNamespace(psRoot, NULL, TRUE);
    2436                 :            : 
    2437         [ +  - ]:          1 :     std::set<int> oSetResolutions;
    2438         [ +  - ]:          1 :     std::map<int, std::set<CPLString> > oMapResolutionsToBands;
    2439                 :          1 :     char** papszMD = NULL;
    2440                 :            :     SENTINEL2GetResolutionSetAndMainMDFromGranule(pszFilename,
    2441                 :            :                                                   "Level-1C_User_Product",
    2442                 :            :                                                   nResolutionOfInterest,
    2443                 :            :                                                   oSetResolutions,
    2444                 :            :                                                   oMapResolutionsToBands,
    2445                 :            :                                                   papszMD,
    2446         [ +  - ]:          1 :                                                   ppsRootMainMTD);
    2447         [ +  + ]:          1 :     if( poBandSet != NULL )
    2448 [ +  - ][ +  - ]:          1 :         *poBandSet = oMapResolutionsToBands[nResolutionOfInterest];
    2449                 :            : 
    2450 [ +  - ][ +  - ]:          1 :     SENTINEL2DatasetContainer* poDS = new SENTINEL2DatasetContainer();
    2451                 :            : 
    2452         [ +  - ]:          1 :     char** papszGranuleMD = SENTINEL2GetL1BCTileMetadata(psRoot);
    2453         [ +  - ]:          1 :     papszMD = CSLMerge(papszMD, papszGranuleMD);
    2454         [ +  - ]:          1 :     CSLDestroy(papszGranuleMD);
    2455                 :            : 
    2456                 :            :     // Remove CLOUD_COVERAGE_ASSESSMENT that comes from main metadata, if granule
    2457                 :            :     // CLOUDY_PIXEL_PERCENTAGE is present.
    2458 [ +  - ][ +  + ]:          1 :     if( CSLFetchNameValue(papszMD, "CLOUDY_PIXEL_PERCENTAGE") != NULL &&
         [ +  + ][ +  + ]
    2459         [ +  - ]:          1 :         CSLFetchNameValue(papszMD, "CLOUD_COVERAGE_ASSESSMENT") != NULL )
    2460                 :            :     {
    2461         [ +  - ]:          1 :         papszMD = CSLSetNameValue(papszMD, "CLOUD_COVERAGE_ASSESSMENT", NULL);
    2462                 :            :     }
    2463                 :            : 
    2464         [ +  - ]:          1 :     poDS->GDALDataset::SetMetadata(papszMD);
    2465         [ +  - ]:          1 :     CSLDestroy(papszMD);
    2466                 :            : 
    2467 [ +  - ][ +  - ]:          1 :     if( !osOriginalXML.empty() )
    2468                 :            :     {
    2469                 :            :         char* apszXMLMD[2];
    2470         [ +  - ]:          1 :         apszXMLMD[0] = const_cast<char*>(osOriginalXML.c_str());
    2471                 :          1 :         apszXMLMD[1] = NULL;
    2472         [ +  - ]:          1 :         poDS->GDALDataset::SetMetadata(apszXMLMD, "xml:SENTINEL2");
    2473                 :            :     }
    2474                 :            : 
    2475                 :            :     /* Create subdatsets per resolution (10, 20, 60m) */
    2476                 :          1 :     int iSubDSNum = 1;
    2477 [ +  - ][ +  - ]:          1 :     for(std::set<int>::const_iterator oIterRes = oSetResolutions.begin();
         [ +  - ][ +  + ]
    2478         [ +  - ]:          1 :                                 oIterRes != oSetResolutions.end();
    2479                 :            :                               ++oIterRes )
    2480                 :            :     {
    2481         [ +  - ]:          1 :         const int nResolution = *oIterRes;
    2482                 :            : 
    2483                 :            :         poDS->GDALDataset::SetMetadataItem(
    2484                 :            :             CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    2485                 :            :             CPLSPrintf("%s:%s:%dm",
    2486                 :            :                         "SENTINEL2_L1C_TILE", pszFilename, nResolution),
    2487 [ +  - ][ +  - ]:          1 :                         "SUBDATASETS");
                 [ +  - ]
    2488                 :            : 
    2489                 :            :         CPLString osBandNames = SENTINEL2GetBandListForResolution(
    2490 [ +  - ][ +  - ]:          1 :                                         oMapResolutionsToBands[nResolution]);
    2491                 :            : 
    2492                 :            :         CPLString osDesc(CPLSPrintf("Bands %s with %dm resolution",
    2493 [ +  - ][ +  - ]:          1 :                                     osBandNames.c_str(), nResolution));
                 [ +  - ]
    2494                 :            :         poDS->GDALDataset::SetMetadataItem(
    2495                 :            :             CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    2496                 :            :             osDesc.c_str(),
    2497 [ +  - ][ +  - ]:          1 :             "SUBDATASETS");
                 [ +  - ]
    2498                 :            : 
    2499                 :          1 :         iSubDSNum ++;
    2500 [ +  - ][ +  - ]:          1 :     }
    2501                 :            : 
    2502                 :            :     /* Expose PREVIEW subdataset */
    2503                 :            :     poDS->GDALDataset::SetMetadataItem(
    2504                 :            :         CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    2505                 :            :         CPLSPrintf("%s:%s:PREVIEW",
    2506                 :            :                     "SENTINEL2_L1C_TILE", pszFilename),
    2507 [ +  - ][ +  - ]:          1 :         "SUBDATASETS");
                 [ +  - ]
    2508                 :            : 
    2509         [ +  - ]:          1 :     CPLString osDesc("RGB preview");
    2510                 :            :     poDS->GDALDataset::SetMetadataItem(
    2511                 :            :         CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    2512                 :            :         osDesc.c_str(),
    2513 [ +  - ][ +  - ]:          1 :         "SUBDATASETS");
                 [ +  - ]
    2514                 :            : 
    2515 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
    2516                 :            : }
    2517                 :            : 
    2518                 :            : /************************************************************************/
    2519                 :            : /*                         SENTINEL2GetOption()                         */
    2520                 :            : /************************************************************************/
    2521                 :            : 
    2522                 :            : static
    2523                 :          1 : const char* SENTINEL2GetOption( GDALOpenInfo* poOpenInfo,
    2524                 :            :                                 const char* pszName,
    2525                 :            :                                 const char* pszDefaultVal )
    2526                 :            : {
    2527                 :            : #ifdef GDAL_DMD_OPENOPTIONLIST
    2528                 :          1 :     const char* pszVal = CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszName);
    2529         [ +  + ]:          1 :     if( pszVal != NULL )
    2530                 :          1 :         return pszVal;
    2531                 :            : #else
    2532                 :            :     (void) poOpenInfo;
    2533                 :            : #endif
    2534                 :          1 :     return CPLGetConfigOption( CPLSPrintf("SENTINEL2_%s", pszName), pszDefaultVal );
    2535                 :            : }
    2536                 :            : 
    2537                 :            : /************************************************************************/
    2538                 :            : /*                            LaunderUnit()                             */
    2539                 :            : /************************************************************************/
    2540                 :            : 
    2541                 :          1 : static CPLString LaunderUnit(const char* pszUnit)
    2542                 :            : {
    2543                 :          1 :     CPLString osUnit;
    2544         [ +  + ]:          1 :     for(int i=0; pszUnit[i] != '\0'; )
    2545                 :            :     {
    2546         [ +  + ]:          1 :         if( strncmp(pszUnit + i, "\xC2" "\xB2", 2) == 0 ) /* square / 2 */
    2547                 :            :         {
    2548                 :          1 :             i += 2;
    2549         [ +  - ]:          1 :             osUnit += "2";
    2550                 :            :         }
    2551         [ +  + ]:          1 :         else if( strncmp(pszUnit + i, "\xC2" "\xB5", 2) == 0 ) /* micro */
    2552                 :            :         {
    2553                 :          1 :             i += 2;
    2554         [ +  - ]:          1 :             osUnit += "u";
    2555                 :            :         }
    2556                 :            :         else
    2557                 :            :         {
    2558         [ +  - ]:          1 :             osUnit += pszUnit[i];
    2559                 :          1 :             i ++;
    2560                 :            :         }
    2561                 :            :     }
    2562                 :          1 :     return osUnit;
    2563                 :            : }
    2564                 :            : 
    2565                 :            : /************************************************************************/
    2566                 :            : /*                       SENTINEL2GetTileInfo()                         */
    2567                 :            : /************************************************************************/
    2568                 :            : 
    2569                 :          1 : static bool SENTINEL2GetTileInfo(const char* pszFilename,
    2570                 :            :                                 int* pnWidth, int* pnHeight, int *pnBits)
    2571                 :            : {
    2572                 :            :     static const unsigned char jp2_box_jp[] = {0x6a,0x50,0x20,0x20}; /* 'jP  ' */
    2573                 :          1 :     VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
    2574         [ +  + ]:          1 :     if( fp == NULL )
    2575                 :          1 :         return false;
    2576                 :            :     GByte abyHeader[8];
    2577         [ -  + ]:          1 :     if( VSIFReadL(abyHeader, 8, 1, fp) != 1 )
    2578                 :            :     {
    2579                 :          0 :         VSIFCloseL(fp);
    2580                 :          0 :         return false;
    2581                 :            :     }
    2582         [ +  + ]:          1 :     if( memcmp(abyHeader + 4, jp2_box_jp, 4) == 0 )
    2583                 :            :     {
    2584                 :          1 :         bool bRet = false;
    2585                 :            :         /* Just parse the ihdr box instead of doing a full dataset opening */
    2586                 :          1 :         GDALJP2Box oBox( fp );
    2587 [ +  - ][ +  - ]:          1 :         if( oBox.ReadFirst() )
    2588                 :            :         {
    2589 [ +  - ][ +  - ]:          1 :             while( strlen(oBox.GetType()) > 0 )
    2590                 :            :             {
    2591 [ +  - ][ +  + ]:          1 :                 if( EQUAL(oBox.GetType(),"jp2h") )
    2592                 :            :                 {
    2593         [ +  - ]:          1 :                     GDALJP2Box oChildBox( fp );
    2594 [ +  - ][ +  - ]:          1 :                     if( !oChildBox.ReadFirstChild( &oBox ) )
    2595                 :            :                         break;
    2596                 :            : 
    2597 [ +  - ][ +  - ]:          1 :                     while( strlen(oChildBox.GetType()) > 0 )
    2598                 :            :                     {
    2599 [ +  - ][ +  - ]:          1 :                         if( EQUAL(oChildBox.GetType(),"ihdr") )
    2600                 :            :                         {
    2601         [ +  - ]:          1 :                             GByte* pabyData = oChildBox.ReadBoxData();
    2602         [ +  - ]:          1 :                             GIntBig nLength = oChildBox.GetDataLength();
    2603 [ +  - ][ +  - ]:          1 :                             if( pabyData != NULL && nLength >= 4 + 4 + 2 + 1 )
    2604                 :            :                             {
    2605                 :          1 :                                 bRet = true;
    2606         [ +  + ]:          1 :                                 if( pnHeight )
    2607                 :            :                                 {
    2608                 :          1 :                                     memcpy(pnHeight, pabyData, 4);
    2609                 :          1 :                                     CPL_MSBPTR32(pnHeight);
    2610                 :            :                                 }
    2611         [ +  + ]:          1 :                                 if( pnWidth != NULL )
    2612                 :            :                                 {
    2613                 :            :                                     //cppcheck-suppress nullPointer
    2614                 :          1 :                                     memcpy(pnWidth, pabyData+4, 4);
    2615                 :          1 :                                     CPL_MSBPTR32(pnWidth);
    2616                 :            :                                 }
    2617         [ +  - ]:          1 :                                 if( pnBits )
    2618                 :            :                                 {
    2619                 :          1 :                                     GByte byPBC = pabyData[4+4+2];
    2620         [ +  - ]:          1 :                                     if( byPBC != 255 )
    2621                 :            :                                     {
    2622                 :          1 :                                         *pnBits = 1 + (byPBC & 0x7f);
    2623                 :            :                                     }
    2624                 :            :                                     else
    2625                 :          0 :                                         *pnBits = 0;
    2626                 :            :                                 }
    2627                 :            :                             }
    2628         [ +  - ]:          1 :                             CPLFree(pabyData);
    2629                 :          1 :                             break;
    2630                 :            :                         }
    2631 [ #  # ][ #  # ]:          0 :                         if( !oChildBox.ReadNextChild( &oBox ) )
    2632                 :          0 :                             break;
    2633                 :            :                     }
    2634         [ +  - ]:          1 :                     break;
    2635                 :            :                 }
    2636                 :            : 
    2637 [ +  - ][ -  + ]:          1 :                 if (!oBox.ReadNext())
    2638                 :          0 :                     break;
    2639                 :            :             }
    2640                 :            :         }
    2641         [ +  - ]:          1 :         VSIFCloseL(fp);
    2642                 :          1 :         return bRet;
    2643                 :            :     }
    2644                 :            :     else /* for unit tests, we use TIFF */
    2645                 :            :     {
    2646                 :          1 :         VSIFCloseL(fp);
    2647                 :          1 :         GDALDataset* poDS = (GDALDataset*)GDALOpen(pszFilename, GA_ReadOnly);
    2648                 :          1 :         bool bRet = false;
    2649         [ +  - ]:          1 :         if( poDS != NULL )
    2650                 :            :         {
    2651         [ +  - ]:          1 :             if( poDS->GetRasterCount() != 0 )
    2652                 :            :             {
    2653                 :          1 :                 bRet = true;
    2654         [ -  + ]:          1 :                 if( pnWidth )
    2655                 :          0 :                     *pnWidth = poDS->GetRasterXSize();
    2656         [ -  + ]:          1 :                 if( pnHeight )
    2657                 :          0 :                     *pnHeight = poDS->GetRasterYSize();
    2658         [ +  - ]:          1 :                 if( pnBits )
    2659                 :            :                 {
    2660                 :          1 :                     const char* pszNBits = poDS->GetRasterBand(1)->GetMetadataItem(
    2661                 :          1 :                                                             "NBITS", "IMAGE_STRUCTURE");
    2662         [ -  + ]:          1 :                     if( pszNBits == NULL )
    2663                 :            :                     {
    2664                 :          0 :                         GDALDataType eDT = poDS->GetRasterBand(1)->GetRasterDataType();
    2665                 :          0 :                         pszNBits = CPLSPrintf( "%d", GDALGetDataTypeSize(eDT) );
    2666                 :            :                     }
    2667                 :          1 :                     *pnBits = atoi(pszNBits);
    2668                 :            :                 }
    2669                 :            :             }
    2670                 :          1 :             GDALClose(poDS);
    2671                 :            :         }
    2672                 :          1 :         return bRet;
    2673                 :            :     }
    2674                 :            : }
    2675                 :            : 
    2676                 :            : /************************************************************************/
    2677                 :            : /*                         OpenL1C_L2ASubdataset()                      */
    2678                 :            : /************************************************************************/
    2679                 :            : 
    2680                 :          1 : GDALDataset *SENTINEL2Dataset::OpenL1C_L2ASubdataset( GDALOpenInfo * poOpenInfo,
    2681                 :            :                                                       SENTINEL2Level eLevel )
    2682                 :            : {
    2683                 :          1 :     CPLString osFilename;
    2684                 :            :     const char* pszPrefix = (eLevel == SENTINEL2_L1C) ? "SENTINEL2_L1C" :
    2685         [ +  + ]:          1 :                                                         "SENTINEL2_L2A";
    2686         [ -  + ]:          1 :     CPLAssert( STARTS_WITH_CI(poOpenInfo->pszFilename, pszPrefix) );
    2687 [ +  - ][ +  - ]:          1 :     osFilename = poOpenInfo->pszFilename + strlen(pszPrefix) + 1;
                 [ +  - ]
    2688         [ +  - ]:          1 :     const char* pszEPSGCode = strrchr(osFilename.c_str(), ':');
    2689 [ +  + ][ +  - ]:          1 :     if( pszEPSGCode == NULL || pszEPSGCode == osFilename.c_str() )
         [ -  + ][ +  + ]
    2690                 :            :     {
    2691                 :            :         CPLError(CE_Failure, CPLE_AppDefined,
    2692         [ +  - ]:          1 :                  "Invalid syntax for %s:", pszPrefix);
    2693                 :          1 :         return NULL;
    2694                 :            :     }
    2695 [ +  - ][ +  - ]:          1 :     osFilename[ (size_t)(pszEPSGCode - osFilename.c_str()) ] = '\0';
    2696         [ +  - ]:          1 :     const char* pszPrecision = strrchr(osFilename.c_str(), ':');
    2697         [ +  + ]:          1 :     if( pszPrecision == NULL )
    2698                 :            :     {
    2699                 :            :         CPLError(CE_Failure, CPLE_AppDefined,
    2700         [ +  - ]:          1 :                  "Invalid syntax for %s:", pszPrefix);
    2701                 :          1 :         return NULL;
    2702                 :            :     }
    2703                 :            : 
    2704         [ +  + ]:          1 :     if( !STARTS_WITH_CI(pszEPSGCode+1, "EPSG_") )
    2705                 :            :     {
    2706                 :            :         CPLError(CE_Failure, CPLE_AppDefined,
    2707         [ +  - ]:          1 :                  "Invalid syntax for %s:", pszPrefix);
    2708                 :          1 :         return NULL;
    2709                 :            :     }
    2710                 :            : 
    2711                 :          1 :     const int nSubDSEPSGCode = atoi(pszEPSGCode + 1 + strlen("EPSG_"));
    2712                 :          1 :     const bool bIsPreview = STARTS_WITH_CI(pszPrecision + 1, "PREVIEW");
    2713                 :          1 :     const bool bIsTCI = STARTS_WITH_CI(pszPrecision + 1, "TCI");
    2714 [ +  + ][ +  + ]:          1 :     const int nSubDSPrecision = (bIsPreview) ? 320 : (bIsTCI) ? 10 : atoi(pszPrecision + 1);
    2715 [ +  + ][ +  + ]:          1 :     if( !bIsTCI && !bIsPreview &&
         [ +  + ][ +  + ]
                 [ +  + ]
    2716                 :            :         nSubDSPrecision != 10 && nSubDSPrecision != 20 && nSubDSPrecision != 60 )
    2717                 :            :     {
    2718                 :            :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported precision: %d",
    2719         [ +  - ]:          1 :                  nSubDSPrecision);
    2720                 :          1 :         return NULL;
    2721                 :            :     }
    2722                 :            : 
    2723 [ +  - ][ +  - ]:          1 :     osFilename.resize( pszPrecision - osFilename.c_str() );
    2724         [ +  - ]:          1 :     std::vector<CPLString> aosNonJP2Files;
    2725         [ +  - ]:          1 :     aosNonJP2Files.push_back(osFilename);
    2726                 :            : 
    2727 [ +  - ][ +  - ]:          1 :     CPLXMLNode *psRoot = CPLParseXMLFile( osFilename );
    2728         [ +  + ]:          1 :     if( psRoot == NULL )
    2729                 :          1 :         return NULL;
    2730                 :            : 
    2731         [ +  - ]:          1 :     char* pszOriginalXML = CPLSerializeXMLTree(psRoot);
    2732         [ +  - ]:          1 :     CPLString osOriginalXML;
    2733         [ +  - ]:          1 :     if( pszOriginalXML )
    2734 [ +  - ][ +  - ]:          1 :         osOriginalXML = pszOriginalXML;
                 [ +  - ]
    2735         [ +  - ]:          1 :     CPLFree(pszOriginalXML);
    2736                 :            : 
    2737                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRoot);
    2738         [ +  - ]:          1 :     CPLStripXMLNamespace(psRoot, NULL, TRUE);
    2739                 :            : 
    2740                 :            :     const bool bIsSafeCompact = eLevel == SENTINEL2_L1C &&
    2741 [ +  + ][ +  - ]:          1 :         EQUAL(CPLGetXMLValue(psRoot,
                 [ +  + ]
    2742                 :            :                 "=Level-1C_User_Product.General_Info.Product_Info.Query_Options.PRODUCT_FORMAT", ""),
    2743                 :            :               "SAFE_COMPACT");
    2744                 :            : 
    2745         [ +  - ]:          1 :     std::vector<CPLString> aosGranuleList;
    2746         [ +  - ]:          1 :     std::map<int, std::set<CPLString> > oMapResolutionsToBands;
    2747         [ +  - ]:          1 :     std::vector<L1CSafeCompatGranuleDescription> aoL1CSafeCompactGranuleList;
    2748         [ +  + ]:          1 :     if( bIsSafeCompact )
    2749                 :            :     {
    2750         [ +  + ]:          1 :         for(unsigned int i = 0; i < NB_BANDS; ++i)
    2751                 :            :         {
    2752                 :          1 :             const SENTINEL2BandDescription * psBandDesc = &asBandDesc[i];
    2753         [ +  - ]:          1 :             CPLString osName = psBandDesc->pszBandName + 1; /* skip B character */
    2754 [ +  - ][ +  + ]:          1 :             if( atoi(osName) < 10 )
    2755 [ +  - ][ +  - ]:          1 :                 osName = "0" + osName;
         [ +  - ][ +  - ]
                 [ +  - ]
    2756 [ +  - ][ +  - ]:          1 :             oMapResolutionsToBands[psBandDesc->nResolution].insert(osName);
    2757         [ +  - ]:          1 :         }
    2758         [ -  + ]:          1 :         if( !SENTINEL2GetGranuleList_L1CSafeCompact(psRoot, osFilename,
    2759 [ +  - ][ +  - ]:          1 :                                                     aoL1CSafeCompactGranuleList) )
    2760                 :            :         {
    2761                 :          0 :             return NULL;
    2762                 :            :         }
    2763 [ +  - ][ +  + ]:          1 :         for(size_t i=0;i<aoL1CSafeCompactGranuleList.size();++i)
    2764                 :            :         {
    2765                 :            :             aosGranuleList.push_back(
    2766 [ +  - ][ +  - ]:          1 :                 aoL1CSafeCompactGranuleList[i].osMTDTLPath);
    2767                 :            :         }
    2768                 :            :     }
    2769         [ +  + ]:          1 :     else if( !SENTINEL2GetGranuleList(psRoot,
    2770                 :            :                                  eLevel,
    2771                 :            :                                  osFilename,
    2772                 :            :                                  aosGranuleList,
    2773                 :            :                                  NULL,
    2774                 :            :                                  (eLevel == SENTINEL2_L1C) ? NULL :
    2775 [ +  + ][ +  - ]:          1 :                                                     &oMapResolutionsToBands) )
                 [ +  - ]
    2776                 :            :     {
    2777                 :          1 :         return NULL;
    2778                 :            :     }
    2779                 :            : 
    2780         [ +  - ]:          1 :     std::vector<CPLString> aosBands;
    2781         [ +  - ]:          1 :     std::set<CPLString> oSetBands;
    2782 [ +  + ][ +  + ]:          1 :     if( bIsPreview || bIsTCI )
    2783                 :            :     {
    2784 [ +  - ][ +  - ]:          1 :         aosBands.push_back("04");
                 [ +  - ]
    2785 [ +  - ][ +  - ]:          1 :         aosBands.push_back("03");
                 [ +  - ]
    2786 [ +  - ][ +  - ]:          1 :         aosBands.push_back("02");
                 [ +  - ]
    2787                 :            :     }
    2788 [ +  + ][ +  + ]:          1 :     else if( eLevel == SENTINEL2_L1C && !bIsSafeCompact )
    2789                 :            :     {
    2790                 :            :         CPLXMLNode* psBandList = CPLGetXMLNode(psRoot,
    2791         [ +  - ]:          1 :             "=Level-1C_User_Product.General_Info.Product_Info.Query_Options.Band_List");
    2792         [ -  + ]:          1 :         if( psBandList == NULL )
    2793                 :            :         {
    2794                 :            :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    2795         [ #  # ]:          0 :                     "Query_Options.Band_List");
    2796                 :          0 :             return NULL;
    2797                 :            :         }
    2798                 :            : 
    2799         [ +  + ]:          1 :         for(CPLXMLNode* psIter = psBandList->psChild; psIter != NULL;
    2800                 :            :                                                       psIter = psIter->psNext )
    2801                 :            :         {
    2802 [ +  - ][ -  + ]:          1 :             if( psIter->eType != CXT_Element ||
    2803                 :          1 :                 !EQUAL(psIter->pszValue, "BAND_NAME") )
    2804                 :          0 :                 continue;
    2805         [ +  - ]:          1 :             const char* pszBandName = CPLGetXMLValue(psIter, NULL, "");
    2806                 :            :             const SENTINEL2BandDescription* psBandDesc =
    2807                 :          1 :                                 SENTINEL2GetBandDesc(pszBandName);
    2808         [ -  + ]:          1 :             if( psBandDesc == NULL )
    2809                 :            :             {
    2810         [ #  # ]:          0 :                 CPLDebug("SENTINEL2", "Unknown band name %s", pszBandName);
    2811                 :          0 :                 continue;
    2812                 :            :             }
    2813         [ +  + ]:          1 :             if( psBandDesc->nResolution != nSubDSPrecision )
    2814                 :          1 :                 continue;
    2815         [ +  - ]:          1 :             CPLString osName = psBandDesc->pszBandName + 1; /* skip B character */
    2816 [ +  - ][ +  + ]:          1 :             if( atoi(osName) < 10 )
    2817 [ +  - ][ +  - ]:          1 :                 osName = "0" + osName;
         [ +  - ][ +  - ]
                 [ +  - ]
    2818         [ +  - ]:          1 :             oSetBands.insert(osName);
    2819         [ +  - ]:          1 :         }
    2820 [ +  - ][ -  + ]:          1 :         if( oSetBands.empty() )
    2821                 :          1 :             return NULL;
    2822                 :            :     }
    2823                 :            :     else
    2824                 :            :     {
    2825 [ +  - ][ +  - ]:          1 :         oSetBands = oMapResolutionsToBands[nSubDSPrecision];
    2826                 :            :     }
    2827                 :            : 
    2828 [ +  - ][ +  + ]:          1 :     if( aosBands.empty() )
    2829                 :            :     {
    2830 [ +  - ][ +  - ]:          1 :         for(std::set<CPLString>::const_iterator oIterBandnames = oSetBands.begin();
         [ +  - ][ +  + ]
    2831         [ +  - ]:          1 :                                                 oIterBandnames != oSetBands.end();
    2832                 :            :                                             ++oIterBandnames)
    2833                 :            :         {
    2834 [ +  - ][ +  - ]:          1 :             aosBands.push_back(*oIterBandnames);
    2835                 :            :         }
    2836                 :            :         /* Put 2=Blue, 3=Green, 4=Band bands in RGB order for conveniency */
    2837 [ +  - ][ +  + ]:          1 :         if( aosBands.size() >= 3 &&
         [ +  + ][ +  - ]
         [ +  - ][ +  + ]
    2838 [ +  - ][ +  - ]:          1 :             aosBands[0] == "02" &&
    2839 [ +  - ][ +  - ]:          1 :             aosBands[1] == "03" &&
    2840 [ +  - ][ +  - ]:          1 :             aosBands[2] == "04" )
    2841                 :            :         {
    2842 [ +  - ][ +  - ]:          1 :             aosBands[0] = "04";
         [ +  - ][ +  - ]
    2843 [ +  - ][ +  - ]:          1 :             aosBands[2] = "02";
         [ +  - ][ +  - ]
    2844                 :            :         }
    2845                 :            :     }
    2846                 :            : 
    2847                 :            : /* -------------------------------------------------------------------- */
    2848                 :            : /*      Create dataset.                                                 */
    2849                 :            : /* -------------------------------------------------------------------- */
    2850                 :            : 
    2851                 :            :     char** papszMD = SENTINEL2GetUserProductMetadata(psRoot,
    2852 [ +  + ][ +  - ]:          1 :         (eLevel == SENTINEL2_L1C ) ? "Level-1C_User_Product" : "Level-2A_User_Product");
    2853                 :            : 
    2854                 :            :     const int nSaturatedVal = atoi(CSLFetchNameValueDef(papszMD,
    2855         [ +  - ]:          1 :                                                   "SPECIAL_VALUE_SATURATED", "-1"));
    2856                 :            :     const int nNodataVal = atoi(CSLFetchNameValueDef(papszMD,
    2857         [ +  - ]:          1 :                                                "SPECIAL_VALUE_NODATA", "-1"));
    2858                 :            : 
    2859                 :            :     const bool bAlpha =
    2860 [ +  - ][ +  - ]:          1 :         CPLTestBool(SENTINEL2GetOption(poOpenInfo, "ALPHA", "FALSE"));
    2861                 :            : 
    2862                 :            :     SENTINEL2Dataset* poDS = CreateL1CL2ADataset(eLevel,
    2863                 :            :                                                  bIsSafeCompact,
    2864                 :            :                                                  aosGranuleList,
    2865                 :            :                                                  aoL1CSafeCompactGranuleList,
    2866                 :            :                                                  aosNonJP2Files,
    2867                 :            :                                                  nSubDSPrecision,
    2868                 :            :                                                  bIsPreview,
    2869                 :            :                                                  bIsTCI,
    2870                 :            :                                                  nSubDSEPSGCode,
    2871                 :            :                                                  bAlpha,
    2872                 :            :                                                  aosBands,
    2873                 :            :                                                  nSaturatedVal,
    2874         [ +  - ]:          1 :                                                  nNodataVal);
    2875         [ +  + ]:          1 :     if( poDS == NULL )
    2876                 :            :     {
    2877         [ +  - ]:          1 :         CSLDestroy(papszMD);
    2878                 :          1 :         return NULL;
    2879                 :            :     }
    2880                 :            : 
    2881 [ +  - ][ +  - ]:          1 :     if( !osOriginalXML.empty() )
    2882                 :            :     {
    2883                 :          1 :         char* apszXMLMD[2] = { NULL };
    2884         [ +  - ]:          1 :         apszXMLMD[0] = const_cast<char*>(osOriginalXML.c_str());
    2885                 :          1 :         apszXMLMD[1] = NULL;
    2886         [ +  - ]:          1 :         poDS->GDALDataset::SetMetadata(apszXMLMD, "xml:SENTINEL2");
    2887                 :            :     }
    2888                 :            : 
    2889         [ +  - ]:          1 :     poDS->GDALDataset::SetMetadata(papszMD);
    2890         [ +  - ]:          1 :     CSLDestroy(papszMD);
    2891                 :            : 
    2892                 :            : /* -------------------------------------------------------------------- */
    2893                 :            : /*      Add extra band metadata.                                        */
    2894                 :            : /* -------------------------------------------------------------------- */
    2895         [ +  - ]:          1 :     poDS->AddL1CL2ABandMetadata(eLevel, psRoot, aosBands);
    2896                 :            : 
    2897                 :            : /* -------------------------------------------------------------------- */
    2898                 :            : /*      Initialize overview information.                                */
    2899                 :            : /* -------------------------------------------------------------------- */
    2900         [ +  - ]:          1 :     poDS->SetDescription( poOpenInfo->pszFilename );
    2901         [ +  - ]:          1 :     CPLString osOverviewFile;
    2902         [ +  + ]:          1 :     if( bIsPreview )
    2903                 :            :         osOverviewFile = CPLSPrintf("%s_PREVIEW_EPSG_%d.tif.ovr",
    2904 [ +  - ][ +  - ]:          1 :                                     osFilename.c_str(), nSubDSEPSGCode);
         [ +  - ][ +  - ]
                 [ +  - ]
    2905         [ +  + ]:          1 :     else if( bIsTCI )
    2906                 :            :         osOverviewFile = CPLSPrintf("%s_TCI_EPSG_%d.tif.ovr",
    2907 [ +  - ][ +  - ]:          1 :                                     osFilename.c_str(), nSubDSEPSGCode);
         [ +  - ][ +  - ]
                 [ +  - ]
    2908                 :            :     else
    2909                 :            :         osOverviewFile = CPLSPrintf("%s_%dm_EPSG_%d.tif.ovr",
    2910                 :            :                                     osFilename.c_str(), nSubDSPrecision,
    2911 [ +  - ][ +  - ]:          1 :                                     nSubDSEPSGCode);
         [ +  - ][ +  - ]
                 [ +  - ]
    2912 [ +  - ][ +  - ]:          1 :     poDS->SetMetadataItem( "OVERVIEW_FILE", osOverviewFile, "OVERVIEWS" );
    2913         [ +  - ]:          1 :     poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );
    2914                 :            : 
    2915 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    2916                 :            : }
    2917                 :            : 
    2918                 :            : /************************************************************************/
    2919                 :            : /*                         AddL1CL2ABandMetadata()                      */
    2920                 :            : /************************************************************************/
    2921                 :            : 
    2922                 :          1 : void SENTINEL2Dataset::AddL1CL2ABandMetadata(SENTINEL2Level eLevel,
    2923                 :            :                                              CPLXMLNode* psRoot,
    2924                 :            :                                              const std::vector<CPLString>& aosBands)
    2925                 :            : {
    2926                 :            :     CPLXMLNode* psIC = CPLGetXMLNode(psRoot,
    2927                 :            :         (eLevel == SENTINEL2_L1C) ?
    2928                 :            :             "=Level-1C_User_Product.General_Info.Product_Image_Characteristics" :
    2929         [ +  + ]:          1 :             "=Level-2A_User_Product.General_Info.L2A_Product_Image_Characteristics");
    2930         [ +  + ]:          1 :     if( psIC != NULL )
    2931                 :            :     {
    2932                 :            :         CPLXMLNode* psSIL = CPLGetXMLNode(psIC,
    2933                 :          1 :                             "Reflectance_Conversion.Solar_Irradiance_List");
    2934         [ +  - ]:          1 :         if( psSIL != NULL )
    2935                 :            :         {
    2936         [ +  + ]:          1 :             for(CPLXMLNode* psIter = psSIL->psChild; psIter != NULL;
    2937                 :            :                                                      psIter = psIter->psNext )
    2938                 :            :             {
    2939 [ +  - ][ -  + ]:          1 :                 if( psIter->eType != CXT_Element ||
    2940                 :          1 :                     !EQUAL(psIter->pszValue, "SOLAR_IRRADIANCE") )
    2941                 :            :                 {
    2942                 :          0 :                     continue;
    2943                 :            :                 }
    2944                 :          1 :                 const char* pszBandId = CPLGetXMLValue(psIter, "bandId", NULL);
    2945                 :          1 :                 const char* pszUnit = CPLGetXMLValue(psIter, "unit", NULL);
    2946                 :          1 :                 const char* pszValue = CPLGetXMLValue(psIter, NULL, NULL);
    2947 [ +  - ][ +  - ]:          1 :                 if( pszBandId != NULL && pszUnit != NULL && pszValue != NULL )
                 [ +  - ]
    2948                 :            :                 {
    2949                 :          1 :                     int nIdx = atoi(pszBandId);
    2950 [ +  - ][ +  - ]:          1 :                     if( nIdx >= 0 && nIdx < (int)NB_BANDS )
    2951                 :            :                     {
    2952         [ +  + ]:          1 :                         for(int i=0;i<nBands;i++)
    2953                 :            :                         {
    2954                 :          1 :                             GDALRasterBand* poBand = GetRasterBand(i+1);
    2955                 :            :                             const char* pszBandName =
    2956                 :          1 :                                 poBand->GetMetadataItem("BANDNAME");
    2957 [ +  + ][ +  + ]:          1 :                             if( pszBandName != NULL &&
    2958                 :          1 :                                 EQUAL(asBandDesc[nIdx].pszBandName, pszBandName) )
    2959                 :            :                             {
    2960                 :            :                                 poBand->GDALRasterBand::SetMetadataItem(
    2961                 :          1 :                                     "SOLAR_IRRADIANCE", pszValue);
    2962                 :            :                                 poBand->GDALRasterBand::SetMetadataItem(
    2963                 :            :                                     "SOLAR_IRRADIANCE_UNIT",
    2964 [ +  - ][ +  - ]:          1 :                                      LaunderUnit(pszUnit));
    2965                 :          1 :                                 break;
    2966                 :            :                             }
    2967                 :            :                         }
    2968                 :            :                     }
    2969                 :            :                 }
    2970                 :            :             }
    2971                 :            :         }
    2972                 :            :     }
    2973                 :            : 
    2974                 :            : /* -------------------------------------------------------------------- */
    2975                 :            : /*      Add Scene Classification category values (L2A)                  */
    2976                 :            : /* -------------------------------------------------------------------- */
    2977                 :            :     CPLXMLNode* psSCL = CPLGetXMLNode(psRoot,
    2978                 :            :             "=Level-2A_User_Product.General_Info."
    2979                 :          1 :             "L2A_Product_Image_Characteristics.L2A_Scene_Classification_List");
    2980                 :          1 :     int nSCLBand = 0;
    2981         [ +  + ]:          1 :     for(int nBand=1;nBand<=static_cast<int>(aosBands.size());nBand++)
    2982                 :            :     {
    2983         [ +  + ]:          1 :         if( EQUAL(aosBands[nBand-1], "SCL") )
    2984                 :            :         {
    2985                 :          1 :             nSCLBand = nBand;
    2986                 :          1 :             break;
    2987                 :            :         }
    2988                 :            :     }
    2989 [ +  + ][ +  + ]:          1 :     if( psSCL != NULL && nSCLBand > 0 )
    2990                 :            :     {
    2991                 :          1 :         std::vector<CPLString> osCategories;
    2992         [ +  + ]:          1 :         for(CPLXMLNode* psIter = psSCL->psChild; psIter != NULL;
    2993                 :            :                                                      psIter = psIter->psNext )
    2994                 :            :         {
    2995 [ +  - ][ -  + ]:          1 :             if( psIter->eType != CXT_Element ||
    2996                 :          1 :                 !EQUAL(psIter->pszValue, "L2A_Scene_Classification_ID") )
    2997                 :            :             {
    2998                 :          0 :                 continue;
    2999                 :            :             }
    3000                 :            :             const char* pszText = CPLGetXMLValue(psIter,
    3001         [ +  - ]:          1 :                                         "L2A_SCENE_CLASSIFICATION_TEXT", NULL);
    3002                 :            :             const char* pszIdx = CPLGetXMLValue(psIter,
    3003         [ +  - ]:          1 :                                         "L2A_SCENE_CLASSIFICATION_INDEX", NULL);
    3004 [ +  - ][ +  - ]:          1 :             if( pszText && pszIdx && atoi(pszIdx) >= 0 && atoi(pszIdx) < 100 )
         [ +  - ][ +  - ]
    3005                 :            :             {
    3006                 :          1 :                 int nIdx = atoi(pszIdx);
    3007 [ +  - ][ +  - ]:          1 :                 if( nIdx >= (int)osCategories.size() )
    3008 [ +  - ][ +  - ]:          1 :                     osCategories.resize(nIdx + 1);
                 [ +  - ]
    3009         [ +  - ]:          1 :                 if( STARTS_WITH_CI(pszText, "SC_") )
    3010 [ +  - ][ +  - ]:          1 :                     osCategories[nIdx] = pszText + 3;
         [ +  - ][ +  - ]
    3011                 :            :                 else
    3012 [ #  # ][ #  # ]:          0 :                     osCategories[nIdx] = pszText;
         [ #  # ][ #  # ]
    3013                 :            :             }
    3014                 :            :         }
    3015                 :            :         char** papszCategories =
    3016 [ +  - ][ +  - ]:          1 :                     (char**)CPLCalloc(osCategories.size()+1,sizeof(char*));
    3017 [ +  - ][ +  + ]:          1 :         for(size_t i=0;i<osCategories.size();i++)
    3018 [ +  - ][ +  - ]:          1 :             papszCategories[i] = CPLStrdup(osCategories[i]);
                 [ +  - ]
    3019 [ +  - ][ +  - ]:          1 :         GetRasterBand(nSCLBand)->SetCategoryNames(papszCategories);
    3020         [ +  - ]:          1 :         CSLDestroy(papszCategories);
    3021                 :            :     }
    3022                 :          1 : }
    3023                 :            : 
    3024                 :            : /************************************************************************/
    3025                 :            : /*                         CreateL1CL2ADataset()                        */
    3026                 :            : /************************************************************************/
    3027                 :            : 
    3028                 :          1 : SENTINEL2Dataset* SENTINEL2Dataset::CreateL1CL2ADataset(
    3029                 :            :                 SENTINEL2Level eLevel,
    3030                 :            :                 bool bIsSafeCompact,
    3031                 :            :                 const std::vector<CPLString>& aosGranuleList,
    3032                 :            :                 const std::vector<L1CSafeCompatGranuleDescription>& aoL1CSafeCompactGranuleList,
    3033                 :            :                 std::vector<CPLString>& aosNonJP2Files,
    3034                 :            :                 int nSubDSPrecision,
    3035                 :            :                 bool bIsPreview,
    3036                 :            :                 bool bIsTCI,
    3037                 :            :                 int nSubDSEPSGCode, /* or -1 if not known at this point */
    3038                 :            :                 bool bAlpha,
    3039                 :            :                 const std::vector<CPLString>& aosBands,
    3040                 :            :                 int nSaturatedVal,
    3041                 :            :                 int nNodataVal)
    3042                 :            : {
    3043                 :            : 
    3044                 :            :     /* Iterate over granule metadata to know the layer extent */
    3045                 :            :     /* and the location of each granule */
    3046                 :          1 :     double dfMinX = 1.0e20;
    3047                 :          1 :     double dfMinY = 1.0e20;
    3048                 :          1 :     double dfMaxX = -1.0e20;
    3049                 :          1 :     double dfMaxY = -1.0e20;
    3050                 :          1 :     std::vector<SENTINEL2GranuleInfo> aosGranuleInfoList;
    3051 [ +  + ][ +  + ]:          1 :     const int nDesiredResolution = (bIsPreview || bIsTCI) ? 0 : nSubDSPrecision;
    3052                 :            : 
    3053         [ +  + ]:          1 :     if( bIsSafeCompact )
    3054                 :            :     {
    3055 [ +  - ][ +  - ]:          1 :         CPLAssert( aosGranuleList.size() ==
    3056         [ -  + ]:          1 :                    aoL1CSafeCompactGranuleList.size() );
    3057                 :            :     }
    3058                 :            : 
    3059 [ +  - ][ +  + ]:          1 :     for(size_t i=0;i<aosGranuleList.size();i++)
    3060                 :            :     {
    3061                 :          1 :         int nEPSGCode = 0;
    3062                 :          1 :         double dfULX = 0.0;
    3063                 :          1 :         double dfULY = 0.0;
    3064                 :          1 :         int nResolution = 0;
    3065                 :          1 :         int nWidth = 0;
    3066                 :          1 :         int nHeight = 0;
    3067 [ +  + ][ +  + ]:          1 :         if( SENTINEL2GetGranuleInfo(eLevel,
         [ +  + ][ +  + ]
                 [ +  + ]
    3068         [ +  - ]:          1 :                                     aosGranuleList[i],
    3069                 :            :                                     nDesiredResolution,
    3070                 :            :                                     &nEPSGCode,
    3071                 :            :                                     &dfULX, &dfULY,
    3072                 :            :                                     &nResolution,
    3073         [ +  - ]:          1 :                                     &nWidth, &nHeight) &&
    3074                 :            :             (nSubDSEPSGCode == nEPSGCode || nSubDSEPSGCode < 0) &&
    3075                 :            :             nResolution != 0 )
    3076                 :            :         {
    3077                 :          1 :             nSubDSEPSGCode = nEPSGCode;
    3078 [ +  - ][ +  - ]:          1 :             aosNonJP2Files.push_back(aosGranuleList[i]);
    3079                 :            : 
    3080         [ +  + ]:          1 :             if( dfULX < dfMinX ) dfMinX = dfULX;
    3081         [ +  + ]:          1 :             if( dfULY > dfMaxY ) dfMaxY = dfULY;
    3082                 :          1 :             const double dfLRX = dfULX + nResolution * nWidth;
    3083                 :          1 :             const double dfLRY = dfULY - nResolution * nHeight;
    3084         [ +  - ]:          1 :             if( dfLRX > dfMaxX ) dfMaxX = dfLRX;
    3085         [ +  - ]:          1 :             if( dfLRY < dfMinY ) dfMinY = dfLRY;
    3086                 :            : 
    3087         [ +  - ]:          1 :             SENTINEL2GranuleInfo oGranuleInfo;
    3088 [ +  - ][ +  - ]:          1 :             oGranuleInfo.osPath = CPLGetPath(aosGranuleList[i]);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    3089         [ +  + ]:          1 :             if( bIsSafeCompact )
    3090                 :            :             {
    3091                 :            :                 oGranuleInfo.osBandPrefixPath =
    3092 [ +  - ][ +  - ]:          1 :                             aoL1CSafeCompactGranuleList[i].osBandPrefixPath;
    3093                 :            :             }
    3094                 :          1 :             oGranuleInfo.dfMinX = dfULX;
    3095                 :          1 :             oGranuleInfo.dfMinY = dfLRY;
    3096                 :          1 :             oGranuleInfo.dfMaxX = dfLRX;
    3097                 :          1 :             oGranuleInfo.dfMaxY = dfULY;
    3098                 :          1 :             oGranuleInfo.nWidth = nWidth / (nSubDSPrecision / nResolution);
    3099                 :          1 :             oGranuleInfo.nHeight = nHeight / (nSubDSPrecision / nResolution);
    3100 [ +  - ][ +  - ]:          1 :             aosGranuleInfoList.push_back(oGranuleInfo);
    3101                 :            :         }
    3102                 :            :     }
    3103         [ +  + ]:          1 :     if( dfMinX > dfMaxX )
    3104                 :            :     {
    3105                 :            :         CPLError(CE_Failure, CPLE_NotSupported,
    3106                 :            :                  "No granule found for EPSG code %d",
    3107         [ +  - ]:          1 :                  nSubDSEPSGCode);
    3108                 :          1 :         return NULL;
    3109                 :            :     }
    3110                 :            : 
    3111                 :            :     const int nRasterXSize = static_cast<int>
    3112                 :          1 :                                     ((dfMaxX - dfMinX) / nSubDSPrecision + 0.5);
    3113                 :            :     const int nRasterYSize = static_cast<int>
    3114                 :          1 :                                     ((dfMaxY - dfMinY) / nSubDSPrecision + 0.5);
    3115 [ +  - ][ +  - ]:          1 :     SENTINEL2Dataset* poDS = new SENTINEL2Dataset(nRasterXSize, nRasterYSize);
    3116                 :            : 
    3117         [ +  - ]:          1 :     poDS->aosNonJP2Files = aosNonJP2Files;
    3118                 :            : 
    3119         [ +  - ]:          1 :     OGRSpatialReference oSRS;
    3120                 :          1 :     char* pszProjection = NULL;
    3121 [ +  - ][ +  - ]:          1 :     if( oSRS.importFromEPSG(nSubDSEPSGCode) == OGRERR_NONE &&
         [ +  - ][ +  - ]
    3122         [ +  - ]:          1 :         oSRS.exportToWkt(&pszProjection) == OGRERR_NONE )
    3123                 :            :     {
    3124         [ +  - ]:          1 :         poDS->SetProjection(pszProjection);
    3125         [ +  - ]:          1 :         CPLFree(pszProjection);
    3126                 :            :     }
    3127                 :            :     else
    3128                 :            :     {
    3129         [ #  # ]:          0 :         CPLDebug("SENTINEL2", "Invalid EPSG code %d", nSubDSEPSGCode);
    3130                 :            :     }
    3131                 :            : 
    3132                 :          1 :     double adfGeoTransform[6] = { 0 };
    3133                 :          1 :     adfGeoTransform[0] = dfMinX;
    3134                 :          1 :     adfGeoTransform[1] = nSubDSPrecision;
    3135                 :          1 :     adfGeoTransform[2] = 0;
    3136                 :          1 :     adfGeoTransform[3] = dfMaxY;
    3137                 :          1 :     adfGeoTransform[4] = 0;
    3138                 :          1 :     adfGeoTransform[5] = -nSubDSPrecision;
    3139         [ +  - ]:          1 :     poDS->SetGeoTransform(adfGeoTransform);
    3140         [ +  - ]:          1 :     poDS->GDALDataset::SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
    3141 [ +  + ][ +  + ]:          1 :     if( bIsPreview || bIsTCI )
    3142         [ +  - ]:          1 :         poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    3143                 :            : 
    3144 [ +  + ][ +  + ]:          1 :     int nBits = (bIsPreview || bIsTCI) ? 8 : 0 /* 0 = unknown yet*/;
    3145 [ +  + ][ +  + ]:          1 :     int nValMax = (bIsPreview || bIsTCI) ? 255 : 0 /* 0 = unknown yet*/;
    3146 [ +  + ][ +  + ]:          1 :     const int nBands = (bIsPreview || bIsTCI) ? 3 : ((bAlpha) ? 1 : 0) + static_cast<int>(aosBands.size());
         [ +  + ][ +  - ]
    3147 [ +  + ][ +  + ]:          1 :     const int nAlphaBand = (bIsPreview || bIsTCI || !bAlpha) ? 0 : nBands;
                 [ +  + ]
    3148 [ +  + ][ +  + ]:          1 :     const GDALDataType eDT = (bIsPreview || bIsTCI) ? GDT_Byte: GDT_UInt16;
    3149                 :            : 
    3150         [ +  - ]:          1 :     std::map<CPLString, GDALProxyPoolDataset*> oMapPVITile;
    3151                 :            : 
    3152         [ +  + ]:          1 :     for( int nBand = 1; nBand <= nBands; nBand++ )
    3153                 :            :     {
    3154                 :          1 :         VRTSourcedRasterBand* poBand = NULL;
    3155                 :            : 
    3156         [ +  + ]:          1 :         if( nBand != nAlphaBand )
    3157                 :            :         {
    3158                 :            :             poBand = new VRTSourcedRasterBand( poDS, nBand, eDT,
    3159                 :            :                                                poDS->nRasterXSize,
    3160 [ +  - ][ +  - ]:          1 :                                                poDS->nRasterYSize );
    3161                 :            :         }
    3162                 :            :         else
    3163                 :            :         {
    3164                 :            :             poBand = new SENTINEL2AlphaBand ( poDS, nBand, eDT,
    3165                 :            :                                               poDS->nRasterXSize,
    3166                 :            :                                               poDS->nRasterYSize,
    3167                 :            :                                               nSaturatedVal,
    3168 [ +  - ][ +  - ]:          1 :                                               nNodataVal );
    3169                 :            :         }
    3170                 :            : 
    3171         [ +  - ]:          1 :         poDS->SetBand(nBand, poBand);
    3172         [ +  + ]:          1 :         if( nBand == nAlphaBand )
    3173         [ +  - ]:          1 :             poBand->SetColorInterpretation(GCI_AlphaBand);
    3174                 :            : 
    3175         [ +  - ]:          1 :         CPLString osBandName;
    3176         [ +  + ]:          1 :         if( nBand != nAlphaBand )
    3177                 :            :         {
    3178 [ +  - ][ +  - ]:          1 :             osBandName = aosBands[nBand-1];
    3179         [ +  - ]:          1 :             SENTINEL2SetBandMetadata( poBand, osBandName);
    3180                 :            :         }
    3181                 :            :         else
    3182 [ +  - ][ +  - ]:          1 :             osBandName = aosBands[0];
    3183                 :            : 
    3184 [ +  - ][ +  + ]:          1 :         for(size_t iSrc=0;iSrc<aosGranuleInfoList.size();iSrc++)
    3185                 :            :         {
    3186         [ +  - ]:          1 :             const SENTINEL2GranuleInfo& oGranuleInfo = aosGranuleInfoList[iSrc];
    3187         [ +  - ]:          1 :             CPLString osTile;
    3188                 :            : 
    3189         [ +  + ]:          1 :             if( bIsSafeCompact )
    3190                 :            :             {
    3191         [ +  + ]:          1 :                 if( bIsTCI )
    3192                 :            :                 {
    3193 [ +  - ][ +  - ]:          1 :                     osTile = oGranuleInfo.osBandPrefixPath + "TCI.jp2";
         [ +  - ][ +  - ]
                 [ +  - ]
    3194                 :            :                 }
    3195                 :            :                 else
    3196                 :            :                 {
    3197 [ +  - ][ +  - ]:          1 :                     osTile = oGranuleInfo.osBandPrefixPath + "B";
         [ +  - ][ +  - ]
                 [ +  - ]
    3198 [ +  - ][ -  + ]:          1 :                     if( osBandName.size() == 1 )
    3199 [ #  # ][ #  # ]:          0 :                         osTile += "0" + osBandName;
                 [ #  # ]
    3200 [ +  - ][ +  + ]:          1 :                     else if( osBandName.size() == 3 )
    3201 [ +  - ][ +  - ]:          1 :                         osTile += osBandName.substr(1);
                 [ +  - ]
    3202                 :            :                     else
    3203         [ +  - ]:          1 :                         osTile += osBandName;
    3204         [ +  - ]:          1 :                     osTile += ".jp2";
    3205                 :            :                 }
    3206                 :            :             }
    3207                 :            :             else
    3208                 :            :             {
    3209                 :            :                 osTile = SENTINEL2GetTilename(
    3210                 :            :                         oGranuleInfo.osPath,
    3211                 :            :                         CPLGetFilename(oGranuleInfo.osPath),
    3212                 :            :                         osBandName,
    3213                 :            :                         bIsPreview,
    3214 [ +  + ][ +  - ]:          1 :                         (eLevel == SENTINEL2_L1C) ? 0 : nSubDSPrecision);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    3215                 :            :             }
    3216                 :            : 
    3217                 :          1 :             bool bTileFound = false;
    3218         [ +  + ]:          1 :             if( nValMax == 0 )
    3219                 :            :             {
    3220                 :            :                 /* It is supposed to be 12 bits, but some products have 15 bits */
    3221 [ +  - ][ +  - ]:          1 :                 if( SENTINEL2GetTileInfo(osTile, NULL, NULL, &nBits) )
                 [ +  + ]
    3222                 :            :                 {
    3223                 :          1 :                     bTileFound = true;
    3224         [ +  - ]:          1 :                     if( nBits <= 16 )
    3225                 :          1 :                         nValMax = (1 << nBits) - 1;
    3226                 :            :                     else
    3227                 :            :                     {
    3228         [ #  # ]:          0 :                         CPLDebug("SENTINEL2", "Unexpected bit depth %d", nBits);
    3229                 :          0 :                         nValMax = 65535;
    3230                 :            :                     }
    3231                 :            :                 }
    3232                 :            :             }
    3233                 :            :             else
    3234                 :            :             {
    3235                 :            :                 VSIStatBufL sStat;
    3236 [ +  - ][ +  - ]:          1 :                 if( VSIStatExL(osTile, &sStat, VSI_STAT_EXISTS_FLAG) == 0 )
                 [ +  - ]
    3237                 :          1 :                     bTileFound = true;
    3238                 :            :             }
    3239         [ +  + ]:          1 :             if( !bTileFound )
    3240                 :            :             {
    3241                 :            :                 CPLError(CE_Warning, CPLE_AppDefined,
    3242                 :            :                         "Tile %s not found on filesystem. Skipping it",
    3243 [ +  - ][ +  - ]:          1 :                         osTile.c_str());
    3244                 :          1 :                 continue;
    3245                 :            :             }
    3246                 :            : 
    3247                 :          1 :             GDALProxyPoolDataset* proxyDS = NULL;
    3248         [ +  + ]:          1 :             if( bIsPreview )
    3249                 :            :             {
    3250         [ +  - ]:          1 :                 proxyDS = oMapPVITile[osTile];
    3251         [ +  + ]:          1 :                 if( proxyDS == NULL )
    3252                 :            :                 {
    3253                 :            :                     proxyDS = new GDALProxyPoolDataset( osTile,
    3254                 :            :                                                         oGranuleInfo.nWidth,
    3255                 :            :                                                         oGranuleInfo.nHeight,
    3256                 :            :                                                         GA_ReadOnly,
    3257 [ +  - ][ +  - ]:          1 :                                                         TRUE);
                 [ +  - ]
    3258         [ +  + ]:          1 :                     for(int j=0;j<nBands;j++)
    3259         [ +  - ]:          1 :                         proxyDS->AddSrcBandDescription(eDT, 128, 128);
    3260         [ +  - ]:          1 :                     oMapPVITile[osTile] = proxyDS;
    3261                 :            :                 }
    3262                 :            :                 else
    3263         [ +  - ]:          1 :                     proxyDS->Reference();
    3264                 :            :             }
    3265                 :            :             else
    3266                 :            :             {
    3267                 :            :                 proxyDS = new GDALProxyPoolDataset( osTile,
    3268                 :            :                                                     oGranuleInfo.nWidth,
    3269                 :            :                                                     oGranuleInfo.nHeight,
    3270                 :            :                                                     GA_ReadOnly,
    3271 [ +  - ][ +  - ]:          1 :                                                     TRUE);
                 [ +  - ]
    3272         [ +  - ]:          1 :                 proxyDS->AddSrcBandDescription(eDT, 128, 128);
    3273                 :            :             }
    3274                 :            : 
    3275                 :            :             const int nDstXOff = static_cast<int>(
    3276                 :          1 :                     (oGranuleInfo.dfMinX - dfMinX) / nSubDSPrecision + 0.5);
    3277                 :            :             const int nDstYOff = static_cast<int>(
    3278                 :          1 :                     (dfMaxY - oGranuleInfo.dfMaxY) / nSubDSPrecision + 0.5);
    3279                 :            : 
    3280         [ +  + ]:          1 :             if( nBand != nAlphaBand )
    3281                 :            :             {
    3282                 :            :                 poBand->AddSimpleSource( proxyDS->GetRasterBand((bIsPreview) ? nBand : 1),
    3283                 :            :                                         0, 0,
    3284                 :            :                                         oGranuleInfo.nWidth,
    3285                 :            :                                         oGranuleInfo.nHeight,
    3286                 :            :                                         nDstXOff,
    3287                 :            :                                         nDstYOff,
    3288                 :            :                                         oGranuleInfo.nWidth,
    3289 [ +  + ][ +  - ]:          1 :                                         oGranuleInfo.nHeight);
                 [ +  - ]
    3290                 :            :             }
    3291                 :            :             else
    3292                 :            :             {
    3293                 :            :                 poBand->AddComplexSource( proxyDS->GetRasterBand(1),
    3294                 :            :                                           0, 0,
    3295                 :            :                                           oGranuleInfo.nWidth,
    3296                 :            :                                           oGranuleInfo.nHeight,
    3297                 :            :                                           nDstXOff,
    3298                 :            :                                           nDstYOff,
    3299                 :            :                                           oGranuleInfo.nWidth,
    3300                 :            :                                           oGranuleInfo.nHeight,
    3301                 :            :                                           nValMax /* offset */,
    3302 [ +  - ][ +  - ]:          1 :                                           0 /* scale */);
    3303                 :            :             }
    3304                 :            : 
    3305         [ +  - ]:          1 :             proxyDS->Dereference();
    3306         [ +  - ]:          1 :         }
    3307                 :            : 
    3308         [ +  + ]:          1 :         if( (nBits % 8) != 0 )
    3309                 :            :         {
    3310                 :            :             poBand->SetMetadataItem("NBITS",
    3311 [ +  - ][ +  - ]:          1 :                                 CPLSPrintf("%d", nBits), "IMAGE_STRUCTURE");
    3312                 :            :         }
    3313         [ +  - ]:          1 :     }
    3314                 :            : 
    3315 [ +  - ][ +  - ]:          1 :     return poDS;
    3316                 :            : }
    3317                 :            : 
    3318                 :            : /************************************************************************/
    3319                 :            : /*                      OpenL1CTileSubdataset()                         */
    3320                 :            : /************************************************************************/
    3321                 :            : 
    3322                 :          1 : GDALDataset* SENTINEL2Dataset::OpenL1CTileSubdataset( GDALOpenInfo * poOpenInfo )
    3323                 :            : {
    3324                 :          1 :     CPLString osFilename;
    3325         [ -  + ]:          1 :     CPLAssert( STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL2_L1C_TILE:") );
    3326 [ +  - ][ +  - ]:          1 :     osFilename = poOpenInfo->pszFilename + strlen("SENTINEL2_L1C_TILE:");
                 [ +  - ]
    3327         [ +  - ]:          1 :     const char* pszPrecision = strrchr(osFilename.c_str(), ':');
    3328 [ +  + ][ +  - ]:          1 :     if( pszPrecision == NULL || pszPrecision == osFilename.c_str() )
         [ -  + ][ +  + ]
    3329                 :            :     {
    3330         [ +  - ]:          1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid syntax for SENTINEL2_L1C_TILE:");
    3331                 :          1 :         return NULL;
    3332                 :            :     }
    3333                 :          1 :     const bool bIsPreview = STARTS_WITH_CI(pszPrecision + 1, "PREVIEW");
    3334         [ +  + ]:          1 :     const int nSubDSPrecision = (bIsPreview) ? 320 : atoi(pszPrecision + 1);
    3335 [ +  + ][ +  + ]:          1 :     if( !bIsPreview && nSubDSPrecision != 10 && nSubDSPrecision != 20 && nSubDSPrecision != 60 )
         [ +  + ][ +  + ]
    3336                 :            :     {
    3337                 :            :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported precision: %d",
    3338         [ +  - ]:          1 :                  nSubDSPrecision);
    3339                 :          1 :         return NULL;
    3340                 :            :     }
    3341 [ +  - ][ +  - ]:          1 :     osFilename.resize( pszPrecision - osFilename.c_str() );
    3342                 :            : 
    3343         [ +  - ]:          1 :     std::set<CPLString> oSetBands;
    3344                 :          1 :     CPLXMLNode* psRootMainMTD = NULL;
    3345 [ +  - ][ +  - ]:          1 :     GDALDataset* poTmpDS = OpenL1CTile( osFilename, &psRootMainMTD, nSubDSPrecision, &oSetBands);
    3346                 :          1 :     SENTINEL2_CPLXMLNodeHolder oXMLHolder(psRootMainMTD);
    3347         [ +  + ]:          1 :     if( poTmpDS == NULL )
    3348                 :          1 :         return NULL;
    3349                 :            : 
    3350         [ +  - ]:          1 :     std::vector<CPLString> aosBands;
    3351         [ +  + ]:          1 :     if( bIsPreview )
    3352                 :            :     {
    3353 [ +  - ][ +  - ]:          1 :         aosBands.push_back("04");
                 [ +  - ]
    3354 [ +  - ][ +  - ]:          1 :         aosBands.push_back("03");
                 [ +  - ]
    3355 [ +  - ][ +  - ]:          1 :         aosBands.push_back("02");
                 [ +  - ]
    3356                 :            :     }
    3357                 :            :     else
    3358                 :            :     {
    3359 [ +  - ][ +  - ]:          1 :         for(std::set<CPLString>::const_iterator oIterBandnames = oSetBands.begin();
         [ +  - ][ +  + ]
    3360         [ +  - ]:          1 :                                                 oIterBandnames != oSetBands.end();
    3361                 :            :                                             ++oIterBandnames)
    3362                 :            :         {
    3363 [ +  - ][ +  - ]:          1 :             aosBands.push_back(*oIterBandnames);
    3364                 :            :         }
    3365                 :            :         /* Put 2=Blue, 3=Green, 4=Band bands in RGB order for conveniency */
    3366 [ +  - ][ +  + ]:          1 :         if( aosBands.size() >= 3 &&
         [ +  + ][ +  - ]
         [ +  - ][ +  + ]
    3367 [ +  - ][ +  - ]:          1 :             aosBands[0] == "02" &&
    3368 [ +  - ][ +  - ]:          1 :             aosBands[1] == "03" &&
    3369 [ +  - ][ +  - ]:          1 :             aosBands[2] == "04" )
    3370                 :            :         {
    3371 [ +  - ][ +  - ]:          1 :             aosBands[0] = "04";
         [ +  - ][ +  - ]
    3372 [ +  - ][ +  - ]:          1 :             aosBands[2] = "02";
         [ +  - ][ +  - ]
    3373                 :            :         }
    3374                 :            :     }
    3375                 :            : 
    3376                 :            : /* -------------------------------------------------------------------- */
    3377                 :            : /*      Create dataset.                                                 */
    3378                 :            : /* -------------------------------------------------------------------- */
    3379                 :            : 
    3380         [ +  - ]:          1 :     std::vector<CPLString> aosGranuleList;
    3381         [ +  - ]:          1 :     aosGranuleList.push_back(osFilename);
    3382                 :            : 
    3383         [ +  - ]:          1 :     const int nSaturatedVal = atoi(CSLFetchNameValueDef(poTmpDS->GetMetadata(),
    3384         [ +  - ]:          1 :                                                   "SPECIAL_VALUE_SATURATED", "-1"));
    3385         [ +  - ]:          1 :     const int nNodataVal = atoi(CSLFetchNameValueDef(poTmpDS->GetMetadata(),
    3386         [ +  - ]:          1 :                                                "SPECIAL_VALUE_NODATA", "-1"));
    3387                 :            : 
    3388                 :            :     const bool bAlpha =
    3389 [ +  - ][ +  - ]:          1 :         CPLTestBool(SENTINEL2GetOption(poOpenInfo, "ALPHA", "FALSE"));
    3390                 :            : 
    3391         [ +  - ]:          1 :     std::vector<CPLString> aosNonJP2Files;
    3392                 :            :     SENTINEL2Dataset* poDS = CreateL1CL2ADataset(SENTINEL2_L1C,
    3393                 :            :                                                  false, // bIsSafeCompact
    3394                 :            :                                                  aosGranuleList,
    3395                 :            :                                                  std::vector<L1CSafeCompatGranuleDescription>(),
    3396                 :            :                                                  aosNonJP2Files,
    3397                 :            :                                                  nSubDSPrecision,
    3398                 :            :                                                  bIsPreview,
    3399                 :            :                                                  false, // bIsTCI
    3400                 :            :                                                  -1 /*nSubDSEPSGCode*/,
    3401                 :            :                                                  bAlpha,
    3402                 :            :                                                  aosBands,
    3403                 :            :                                                  nSaturatedVal,
    3404 [ +  - ][ +  - ]:          1 :                                                  nNodataVal);
                 [ +  - ]
    3405         [ +  + ]:          1 :     if( poDS == NULL )
    3406                 :            :     {
    3407 [ +  - ][ +  - ]:          1 :         delete poTmpDS;
    3408                 :          1 :         return NULL;
    3409                 :            :     }
    3410                 :            : 
    3411                 :            :     // Transfer metadata
    3412 [ +  - ][ +  - ]:          1 :     poDS->GDALDataset::SetMetadata( poTmpDS->GetMetadata() );
    3413 [ +  - ][ +  - ]:          1 :     poDS->GDALDataset::SetMetadata( poTmpDS->GetMetadata("xml:SENTINEL2"), "xml:SENTINEL2" );
    3414                 :            : 
    3415 [ +  - ][ +  - ]:          1 :     delete poTmpDS;
    3416                 :            : 
    3417                 :            : /* -------------------------------------------------------------------- */
    3418                 :            : /*      Add extra band metadata.                                        */
    3419                 :            : /* -------------------------------------------------------------------- */
    3420         [ +  + ]:          1 :     if( psRootMainMTD != NULL )
    3421         [ +  - ]:          1 :         poDS->AddL1CL2ABandMetadata(SENTINEL2_L1C, psRootMainMTD, aosBands);
    3422                 :            : 
    3423                 :            : /* -------------------------------------------------------------------- */
    3424                 :            : /*      Initialize overview information.                                */
    3425                 :            : /* -------------------------------------------------------------------- */
    3426         [ +  - ]:          1 :     poDS->SetDescription( poOpenInfo->pszFilename );
    3427         [ +  - ]:          1 :     CPLString osOverviewFile;
    3428         [ +  + ]:          1 :     if( bIsPreview )
    3429                 :            :         osOverviewFile = CPLSPrintf("%s_PREVIEW.tif.ovr",
    3430 [ +  - ][ +  - ]:          1 :                                     osFilename.c_str());
         [ +  - ][ +  - ]
                 [ +  - ]
    3431                 :            :     else
    3432                 :            :         osOverviewFile = CPLSPrintf("%s_%dm.tif.ovr",
    3433 [ +  - ][ +  - ]:          1 :                                     osFilename.c_str(), nSubDSPrecision);
         [ +  - ][ +  - ]
                 [ +  - ]
    3434 [ +  - ][ +  - ]:          1 :     poDS->SetMetadataItem( "OVERVIEW_FILE", osOverviewFile, "OVERVIEWS" );
    3435         [ +  - ]:          1 :     poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );
    3436                 :            : 
    3437 [ +  - ][ +  - ]:          1 :     return poDS;
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    3438                 :            : }
    3439                 :            : 
    3440                 :            : /************************************************************************/
    3441                 :            : /*                      GDALRegister_SENTINEL2()                        */
    3442                 :            : /************************************************************************/
    3443                 :            : 
    3444                 :          1 : void GDALRegister_SENTINEL2()
    3445                 :            : {
    3446         [ +  + ]:          1 :     if( GDALGetDriverByName( "SENTINEL2" ) != NULL )
    3447                 :          1 :         return;
    3448                 :            : 
    3449         [ +  - ]:          1 :     GDALDriver *poDriver = new GDALDriver();
    3450                 :            : 
    3451                 :          1 :     poDriver->SetDescription( "SENTINEL2" );
    3452                 :            : #ifdef GDAL_DCAP_RASTER
    3453                 :          1 :     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
    3454                 :            : #endif
    3455                 :          1 :     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
    3456                 :          1 :     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "Sentinel 2" );
    3457                 :          1 :     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_sentinel2.html" );
    3458                 :          1 :     poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
    3459                 :            : 
    3460                 :            : #ifdef GDAL_DMD_OPENOPTIONLIST
    3461                 :            :     poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST,
    3462                 :            : "<OpenOptionList>"
    3463                 :            : "  <Option name='ALPHA' type='boolean' description='Whether to expose an alpha band' default='NO'/>"
    3464                 :          1 : "</OpenOptionList>" );
    3465                 :            : #endif
    3466                 :            : 
    3467                 :          1 :     poDriver->pfnOpen = SENTINEL2Dataset::Open;
    3468                 :          1 :     poDriver->pfnIdentify = SENTINEL2Dataset::Identify;
    3469                 :            : 
    3470                 :          1 :     GetGDALDriverManager()->RegisterDriver( poDriver );
    3471                 :            : }

Generated by: LCOV version 1.9