LCOV - code coverage report
Current view: top level - frmts/sentinel2 - sentinel2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1461 1563 93.5 %
Date: XXXX-XX-XX Functions: 51 53 96.2 %
Branches: 2161 3806 56.8 %

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

Generated by: LCOV version 1.9