Example #1
0
    def postProcessing(self):
        ''' update the user product and product metadata,
            copy the logfile to QI data as a report,
            Check if at target product is present.

        '''

        self.updateProductMetadata()
        xp = L3_XmlParser(self.config, 'UP03')
        auxdata = xp.getTree('L3_Auxiliary_Data_Info', 'Aux_Data')
        auxdata.clear()
        dirname, basename = os.path.split(self.config.L3_TILE_MTD_XML)
        fn1r = basename.replace('_MTD_', '_GIP_')
        fn2r = fn1r.replace('.xml', '')
        gippFn = etree.Element('GIPP_FILENAME',
                               type='GIP_Level-3p',
                               version=self.config.processorVersion)
        gippFn.text = fn2r
        gippList = objectify.Element('L3_GIPP_LIST')
        gippList.append(gippFn)
        auxdata.append(gippList)
        xp.export()

        dirname, basename = os.path.split(self.config.L3_TILE_MTD_XML)
        report = basename.replace('.xml', '_Report.xml')
        report = os.path.join(dirname, 'QI_DATA', report)

        if ((os.path.isfile(self.config.fnLog)) == False):
            self.logger.fatal('Missing file: ' + self.config.fnLog)
            self.config.exitError()

        f = open(self.config.fnLog, 'a')
        f.write('</Sen2Three_Level-3_Report_File>')
        f.flush()
        f.close()
        copy_file(self.config.fnLog, report)

        return
Example #2
0
#!/usr/bin/env python
Example #3
0
    def exportBandList(self, productLevel):
        ''' Export all bands of current tile.
            converts all bands from hdf5 to JPEG-2000.
            
            :param productLevel: [ L2A | L3].
            :type productLevel: str
            :return: false if error occurred during export.
            :rtype: boolean           

        '''
        bandDir = self._L3_bandDir
        # converts all bands from hdf5 to JPEG 2000
        if (os.path.exists(bandDir) == False):
            self.config.logger.fatal('missing directory %s:' % bandDir)
            self.config.exitError()
            return False

        os.chdir(bandDir)
        self.config.timestamp(productLevel + '_Tables: start export')
        if (self._resolution == 10):
            bandIndex = [1, 2, 3, 7, 14, 34]

        elif (self._resolution == 20):
            bandIndex = [1, 2, 3, 4, 5, 6, 8, 11, 12, 14, 34]

        elif (self._resolution == 60):
            bandIndex = [0, 1, 2, 3, 4, 5, 6, 8, 9, 11, 12, 14, 34]

        xp = L3_XmlParser(self.config, 'UP2A')
        if self.config.productVersion < 14.5:
            pi = xp.getTree('General_Info', 'L2A_Product_Info')
            try:  # 14.2:
                gr2a = pi.L2A_Product_Organisation.Granule_List.Granule
            except:  # 13.1:
                gr2a = pi.L2A_Product_Organisation.Granule_List.Granules
        else:  # 14.5:
            pi = xp.getTree('General_Info', 'Product_Info')
            gr2a = pi.Product_Organisation.Granule_List.Granule

        gi2a = gr2a.attrib['granuleIdentifier']
        ds2a = gr2a.attrib['datastripIdentifier']
        l3TileId_split = self.config.L3_TILE_ID.split('_')
        gi3 = gi2a.replace('_L2A_', '_L03_')
        ds3 = ds2a.replace('_L2A_', '_L03_')
        if self.config.productVersion < 14.5:
            gi3 = gi3[:41] + l3TileId_split[2] + '_' + l3TileId_split[1] + gi3[
                -7:]
        else:
            gi3 = gi3[:41] + l3TileId_split[2] + '_' + l3TileId_split[1] + gi3[
                -7:]
        gi3 = gi3.replace('USER', 'OPER')
        ds3 = ds3.replace('USER', 'OPER')
        Granule = objectify.Element('Granule')
        Granule.attrib['granuleIdentifier'] = gi3
        Granule.attrib['datastripIdentifier'] = ds3
        Granule.attrib['imageFormat'] = 'JPEG2000'

        gl = objectify.Element('Granule_List')
        gl.append(Granule)
        for index in bandIndex:
            bandName = self.getBandNameFromIndex(index)
            if index == self.SCL:
                filename = self._L3_Tile_SCL_File
            elif index == self.MSC:
                filename = self._L3_Tile_MSC_File
            else:
                filename = self._L3_Tile_BND_File.replace('BXX', bandName)
            if self.testBand(productLevel, index):
                band = self.getBand(productLevel, index)
            else:
                # create mosaic map if first tile:
                scl = self.getBand(productLevel, self.SCL)
                scl[scl > 0] = 1
                self.setBand(productLevel, self.MSC)
            # Median Filter:
            mf = self.config.medianFilter
            if (mf > 0):
                band = ndimage.filters.median_filter(band, (mf, mf))

            if (index == self.SCL) or (index == self.MSC):
                self.glymurWrapper(filename, band.astype(uint8))
            else:
                self.glymurWrapper(filename, band.astype(uint16))
            self.config.logger.info('Band ' + bandName + ' exported')
            self.config.timestamp('L3_Tables: band ' + bandName + ' exported')
            filename = os.path.basename(filename.strip('.jp2'))
            imageFile3 = etree.Element('IMAGE_FILE')
            # by intention os.path.join is not used here, as otherwise validation on windows fails:
            resolution = 'R' + str(self.config.resolution) + 'm/'
            imageFile3.text = 'GRANULE/' + self.config.L3_TILE_ID + '/IMG_DATA/' + resolution + filename
            Granule.append(imageFile3)

        self.createTci('L3')
        xp = L3_XmlParser(self.config, 'UP03')
        pi = xp.getTree('General_Info', 'Product_Info')
        po = pi.Product_Organisation
        po.append(gl)
        xp.export()

        #update on tile level
        xp = L3_XmlParser(self.config, 'T03')
        ti3 = xp.getTree('General_Info', 'TILE_ID')
        ti3._setText(gi3)
        di3 = xp.getTree('General_Info', 'DATASTRIP_ID')
        di3._setText(ds3)

        # clean up and post processing actions:
        pxlQI = objectify.Element('Pixel_Level_QI')
        pxlQI.attrib['resolution'] = str(self.config.resolution)
        pxlQI.append(objectify.Element('TILE_CLASSIFICATION_MASK'))
        pxlQI.append(objectify.Element('TILE_MOSAIC_MASK'))

        pxlQI.TILE_CLASSIFICATION_MASK = os.path.basename(
            self._L3_Tile_SCL_File).replace('.jp2', '')
        pxlQI.TILE_MOSAIC_MASK = os.path.basename(
            self._L3_Tile_MSC_File).replace('.jp2', '')

        qiiL3 = xp.getTree('Quality_Indicators_Info', 'Pixel_Level_QI')
        qiiL3Len = len(qiiL3)
        for i in range(qiiL3Len):
            if int(qiiL3[i].attrib['resolution']) == self.config.resolution:
                qiiL3[i] = pxlQI
                break

        xp.export()
        self.config.timestamp(productLevel + '_Tables: stop export')
        return True
Example #4
0
    def updateProductMetadata(self):
        ''' Update the product metadata for each new synthesis.
        '''

        dbname = os.path.join(self.config.L3_TARGET_DIR, '.database.h5')
        h5file = tables.open_file(dbname, mode='a')
        table = h5file.root.group1.classes

        totalPixelCount = 0
        dataPixelCount = 0
        nodataPixelCount = 0
        goodPixelCount = 0
        badPixelCount = 0
        satDefPixelCount = 0
        darkFeaturesCount = 0
        cloudShadowsCount = 0
        vegetationCount = 0
        bareSoilsCount = 0
        waterCount = 0
        lowProbaCloudsCount = 0
        medProbaCloudsCount = 0
        hiProbaCloudsCount = 0
        thinCirrusCount = 0
        snowIceCount = 0

        for row in table.iterrows():
            if row['RESOLUTION'] == self.config.resolution:
                totalPixelCount += row['TOTAL_PIXELS']
                dataPixelCount += row['DATA_PIXELS']
                nodataPixelCount += row['NODATA_PIXELS']
                goodPixelCount += row['GOOD_PIXELS']
                badPixelCount += row['BAD_PIXELS']
                satDefPixelCount += row['SAT_DEF_PIXELS']
                darkFeaturesCount += row['DARK_PIXELS']
                cloudShadowsCount += row['CLOUD_SHADOWS']
                vegetationCount += row['VEGETATION']
                bareSoilsCount += row['NOT_VEGETATED']
                waterCount += row['WATER']
                lowProbaCloudsCount += row['UNCLASSIFIED']
                medProbaCloudsCount += row['MED_PROBA_CLOUDS']
                hiProbaCloudsCount += row['HIGH_PROBA_CLOUDS']
                thinCirrusCount += row['THIN_CIRRUS']
                snowIceCount += row['SNOW_ICE']

        table.flush()
        h5file.close()

        totalPixelCount = float(totalPixelCount) * 0.01
        dataPixelCount = float(dataPixelCount) * 0.01
        dataPixelPercentage = float32(dataPixelCount) / totalPixelCount * 100
        nodataPixelPercentage = float32(nodataPixelCount) / totalPixelCount
        goodPixelPercentage = float32(goodPixelCount) / dataPixelCount
        badPixelPercentage = float32(badPixelCount) / dataPixelCount
        satDefPixelPercentage = float32(satDefPixelCount) / dataPixelCount
        darkFeaturesPercentage = float32(darkFeaturesCount) / dataPixelCount
        cloudShadowsPercentage = float32(cloudShadowsCount) / dataPixelCount
        vegetationPercentage = float32(vegetationCount) / dataPixelCount
        bareSoilsPercentage = float32(bareSoilsCount) / dataPixelCount
        waterPercentage = float32(waterCount) / dataPixelCount
        lowProbaCloudsPercentage = float32(
            lowProbaCloudsCount) / dataPixelCount
        medProbaCloudsPercentage = float32(
            medProbaCloudsCount) / dataPixelCount
        hiProbaCloudsPercentage = float32(hiProbaCloudsCount) / dataPixelCount
        thinCirrusPercentage = float32(thinCirrusCount) / dataPixelCount
        snowIcePercentage = float32(snowIceCount) / dataPixelCount

        classificationQI = objectify.Element('Classification_QI')
        classificationQI.attrib['resolution'] = str(self.config.resolution)
        classificationQI.append(objectify.Element('TOTAL_PIXEL_COUNT'))
        classificationQI.append(objectify.Element('DATA_PIXEL_COUNT'))
        classificationQI.append(objectify.Element('DATA_PIXEL_PERCENTAGE'))
        classificationQI.append(objectify.Element('NODATA_PIXEL_COUNT'))
        classificationQI.append(objectify.Element('NODATA_PIXEL_PERCENTAGE'))
        classificationQI.append(objectify.Element('GOOD_PIXEL_COUNT'))
        classificationQI.append(objectify.Element('GOOD_PIXEL_PERCENTAGE'))
        classificationQI.append(objectify.Element('BAD_PIXEL_COUNT'))
        classificationQI.append(objectify.Element('BAD_PIXEL_PERCENTAGE'))
        classificationQI.append(
            objectify.Element('SATURATED_DEFECTIVE_PIXEL_COUNT'))
        classificationQI.append(
            objectify.Element('SATURATED_DEFECTIVE_PIXEL_PERCENTAGE'))
        classificationQI.append(objectify.Element('DARK_FEATURES_COUNT'))
        classificationQI.append(objectify.Element('DARK_FEATURES_PERCENTAGE'))
        classificationQI.append(objectify.Element('CLOUD_SHADOWS_COUNT'))
        classificationQI.append(objectify.Element('CLOUD_SHADOWS_PERCENTAGE'))
        classificationQI.append(objectify.Element('VEGETATION_COUNT'))
        classificationQI.append(objectify.Element('VEGETATION_PERCENTAGE'))
        classificationQI.append(objectify.Element('NOT_VEGETATED_COUNT'))
        classificationQI.append(objectify.Element('NOT_VEGETATED_PERCENTAGE'))
        classificationQI.append(objectify.Element('WATER_COUNT'))
        classificationQI.append(objectify.Element('WATER_PERCENTAGE'))
        classificationQI.append(objectify.Element('UNCLASSIFIED_COUNT'))
        classificationQI.append(objectify.Element('UNCLASSIFIED_PERCENTAGE'))
        classificationQI.append(objectify.Element('MEDIUM_PROBA_CLOUDS_COUNT'))
        classificationQI.append(
            objectify.Element('MEDIUM_PROBA_CLOUDS_PERCENTAGE'))
        classificationQI.append(objectify.Element('HIGH_PROBA_CLOUDS_COUNT'))
        classificationQI.append(
            objectify.Element('HIGH_PROBA_CLOUDS_PERCENTAGE'))
        classificationQI.append(objectify.Element('THIN_CIRRUS_COUNT'))
        classificationQI.append(objectify.Element('THIN_CIRRUS_PERCENTAGE'))
        classificationQI.append(objectify.Element('SNOW_ICE_COUNT'))
        classificationQI.append(objectify.Element('SNOW_ICE_PERCENTAGE'))

        classificationQI.TOTAL_PIXEL_COUNT = int(totalPixelCount * 100)
        classificationQI.DATA_PIXEL_COUNT = int(dataPixelCount * 100)
        classificationQI.DATA_PIXEL_PERCENTAGE = dataPixelPercentage
        classificationQI.NODATA_PIXEL_COUNT = nodataPixelCount
        classificationQI.NODATA_PIXEL_PERCENTAGE = nodataPixelPercentage
        classificationQI.GOOD_PIXEL_COUNT = goodPixelCount
        classificationQI.GOOD_PIXEL_PERCENTAGE = goodPixelPercentage
        classificationQI.BAD_PIXEL_COUNT = badPixelCount
        classificationQI.BAD_PIXEL_PERCENTAGE = badPixelPercentage
        classificationQI.SATURATED_DEFECTIVE_PIXEL_COUNT = satDefPixelCount
        classificationQI.SATURATED_DEFECTIVE_PIXEL_PERCENTAGE = satDefPixelPercentage
        classificationQI.DARK_FEATURES_COUNT = darkFeaturesCount
        classificationQI.DARK_FEATURES_PERCENTAGE = darkFeaturesPercentage
        classificationQI.CLOUD_SHADOWS_COUNT = cloudShadowsCount
        classificationQI.CLOUD_SHADOWS_PERCENTAGE = cloudShadowsPercentage
        classificationQI.VEGETATION_COUNT = vegetationCount
        classificationQI.VEGETATION_PERCENTAGE = vegetationPercentage
        classificationQI.NOT_VEGETATED_COUNT = bareSoilsCount
        classificationQI.NOT_VEGETATED_PERCENTAGE = bareSoilsPercentage
        classificationQI.WATER_COUNT = waterCount
        classificationQI.WATER_PERCENTAGE = waterPercentage
        classificationQI.UNCLASSIFIED_COUNT = lowProbaCloudsCount
        classificationQI.UNCLASSIFIED_PERCENTAGE = lowProbaCloudsPercentage
        classificationQI.MEDIUM_PROBA_CLOUDS_COUNT = medProbaCloudsCount
        classificationQI.MEDIUM_PROBA_CLOUDS_PERCENTAGE = medProbaCloudsPercentage
        classificationQI.HIGH_PROBA_CLOUDS_COUNT = hiProbaCloudsCount
        classificationQI.HIGH_PROBA_CLOUDS_PERCENTAGE = hiProbaCloudsPercentage
        classificationQI.THIN_CIRRUS_COUNT = thinCirrusCount
        classificationQI.THIN_CIRRUS_PERCENTAGE = thinCirrusPercentage
        classificationQI.SNOW_ICE_COUNT = snowIceCount
        classificationQI.SNOW_ICE_PERCENTAGE = snowIcePercentage

        xp = L3_XmlParser(self.config, 'UP03')
        l3qi = xp.getTree('Quality_Indicators_Info', 'Classification_QI')
        l3qiLen = len(l3qi)
        for i in range(l3qiLen):
            if int(l3qi[i].attrib['resolution']) == self.config.resolution:
                l3qi[i].clear()
                l3qi[i] = classificationQI
                xp.export()
                break

        return True
Example #5
0
    def createL3_Tile(self, tileId):
        ''' Create an L3 tile

            :param tileId: the tile ID
            :type tileId: string

        '''

        L2A_TILE_ID = tileId
        if self.config.namingConvention == 'SAFE_STANDARD':
            strList = L2A_TILE_ID.split('_')
            L3_TILE_ID = 'L03_' + strList[-2] + '_' + strList[
                -3] + '_' + strList[-4]
        else:
            L3_TILE_ID = L2A_TILE_ID.replace('L2A_', 'L03_')
            L3_TILE_ID = L3_TILE_ID.replace('USER', 'OPER')

        self.config.L3_TILE_ID = L3_TILE_ID
        sourceDir = self.config.sourceDir
        L2A_UP_ID = self.config.L2A_UP_ID
        L3_TARGET_DIR = self.config.L3_TARGET_DIR
        GRANULE = 'GRANULE'
        QI_DATA = 'QI_DATA'
        L2A_TILE_ID = os.path.join(sourceDir, L2A_UP_ID, GRANULE, L2A_TILE_ID)
        L3_TILE_ID = os.path.join(L3_TARGET_DIR, GRANULE, L3_TILE_ID)

        try:
            os.mkdir(L3_TILE_ID)
            os.mkdir(os.path.join(L3_TILE_ID, QI_DATA))
            self.config.logger.info('new working directory is: ' + L3_TILE_ID)
        except:
            pass

        filelist = sorted(os.listdir(L2A_TILE_ID))
        found = False
        if self.config.namingConvention == 'SAFE_STANDARD':
            L2A_UP_MASK = '*_MTD_L2A_TL_*.xml'
        else:
            L2A_UP_MASK = 'MTD_TL.xml'
        for filename in filelist:
            if (fnmatch.fnmatch(filename, L2A_UP_MASK) == True):
                found = True
                break
        if found == False:
            self.config.logger.fatal('No metadata in tile')
            self.config.exitError()

        assert isinstance(filename, object)
        L2A_TILE_MTD_XML = os.path.join(L2A_TILE_ID, filename)
        L3_TILE_MTD_XML = 'MTD_TL.xml'
        L3_TILE_MTD_XML = os.path.join(L3_TILE_ID, L3_TILE_MTD_XML)
        copy_file(L2A_TILE_MTD_XML, L3_TILE_MTD_XML)
        self.config.L2A_TILE_MTD_XML = L2A_TILE_MTD_XML
        xp = L3_XmlParser(self.config, 'T2A')
        TILE_ID_2A = xp.getTree('General_Info', 'TILE_ID')
        if TILE_ID_2A == False:
            # it's a version 14.2 metadata:
            TILE_ID_2A = xp.getTree('General_Info', 'TILE_ID_2A')

        self.config.TILE_ID_2A = TILE_ID_2A.text
        if xp.convert() != 0:
            xp = L3_XmlParser(self.config, 'T2A')
        xp.validate()
        self.config.L3_TILE_MTD_XML = L3_TILE_MTD_XML

        #update tile and datastrip id in metadata file.
        xp = L3_XmlParser(self.config, 'T03')
        if (xp.convert() == False):
            self.logger.fatal('error in converting tile metadata to level 3')
            self.exitError()
        xp = L3_XmlParser(self.config, 'T03')
        try:
            tree = xp.getTree('General_Info', 'L1C_TILE_ID')
            del tree[:]
        except:
            pass
        try:
            # remove all QI items from the past, as they will be calculated
            # directly from contents of images
            tree = xp.getTree('Quality_Indicators_Info',
                              'L1C_Image_Content_QI')
            del tree[:]
            tree = xp.getTree('Quality_Indicators_Info',
                              'L2A_Image_Content_QI')
            del tree[:]
            tree = xp.getTree('Quality_Indicators_Info', 'L1C_Pixel_Level_QI')
            del tree[:]
            tree = xp.getTree('Quality_Indicators_Info', 'L2A_Pixel_Level_QI')
            del tree[:]
            tree = xp.getTree('Quality_Indicators_Info', 'PVI_FILENAME')
            del tree[:]
        except:
            try:
                # test on version 14.5 data:
                tree = xp.getTree('Quality_Indicators_Info',
                                  'Image_Content_QI')
                del tree[:]
                tree = xp.getTree('Quality_Indicators_Info', 'Pixel_Level_QI')
                del tree[:]
                tree = xp.getTree('Quality_Indicators_Info', 'PVI_FILENAME')
                del tree[:]
            except:
                pass
        xp.export()

        # read updated file and append new items:
        # create the first three enties of the Quality Measures:
        xp = L3_XmlParser(self.config, 'T03')
        root = xp.getRoot('Quality_Indicators_Info')

        tree = objectify.Element('Pixel_Level_QI')
        tree.attrib['resolution'] = str(self.config.resolution)
        root.append(tree)

        tree = objectify.Element('Classification_QI')
        tree.attrib['resolution'] = str(self.config.resolution)
        root.append(tree)

        tree = objectify.Element('Mosaic_QI')
        tree.attrib['resolution'] = str(self.config.resolution)
        root.append(tree)

        xp.export()

        #update tile id in ds metadata file.
        xp = L3_XmlParser(self.config, 'UP2A')
        if self.config.productVersion > 14.2:
            pi = xp.getTree('General_Info', 'Product_Info')
            gr2a = pi.Product_Organisation.Granule_List.Granule
        else:
            pi = xp.getTree('General_Info', 'L2A_Product_Info')
            try:
                gr2a = pi.L2A_Product_Organisation.Granule_List.Granule
            except:  # it's a boring old 13.1 product:
                gr2a = pi.L2A_Product_Organisation.Granule_List.Granules
        gi2a = gr2a.attrib['granuleIdentifier']
        gi03 = gi2a.replace('L2A_', 'L03_')
        xp = L3_XmlParser(self.config, 'DS03')
        ti = xp.getTree('Image_Data_Info', 'Tiles_Information')
        ti.Tile_List.append(objectify.Element('Tile', tileId=gi03))
        xp.export()
        return
Example #6
0
    def createL3_TargetProduct(self):
        ''' Create the L3 target product.

            :return: true if succesful
            :rtype: bool

        '''

        self.config.logger.info('Creating L3 target product ...')
        L2A_UP_MASK = '*L2A_*'
        #L2A_UP_DIR = os.path.join(self.config.sourceDir, self.config.L2A_UP_ID)
        L2A_UP_DIR = self.config.L2A_UP_DIR
        # detect the filename for the datastrip metadata:
        L2A_DS_DIR = os.path.join(L2A_UP_DIR, 'DATASTRIP')
        if os.path.exists(L2A_DS_DIR) == False:
            stderrWrite('directory "%s" does not exist.\n' % L2A_DS_DIR)
            self.config.exitError()
            return False

        if self.config.namingConvention == 'SAFE_STANDARD':
            L2A_DS_MASK = 'S2?_USER_MSI_L2A_DS_*'
        else:
            L2A_DS_MASK = 'DS_*'
        dirlist = sorted(os.listdir(L2A_DS_DIR))
        found = False

        for dirname in dirlist:
            if (fnmatch.fnmatch(dirname, L2A_DS_MASK) == True):
                found = True
                break

        L2A_DS_DIR = os.path.join(L2A_DS_DIR, dirname)
        if self.config.namingConvention == 'SAFE_STANDARD':
            L2A_DS_MTD_XML = (dirname[:-7] + '.xml').replace('_MSI_', '_MTD_')
        else:
            L2A_DS_MTD_XML = 'MTD_DS.xml'
        L2A_DS_MTD_XML = os.path.join(L2A_DS_DIR, L2A_DS_MTD_XML)
        if found == False:
            stderrWrite('No metadata in datastrip\n.')
            self.config.exitError()

        dirname, basename = os.path.split(L2A_UP_DIR)
        if (fnmatch.fnmatch(basename, L2A_UP_MASK) == False):
            stderrWrite(basename +
                        ': identifier "%s" is missing.' % L2A_UP_MASK)
            self.config.exitError()
            return False

        GRANULE = os.path.join(L2A_UP_DIR, 'GRANULE')
        if os.path.exists(GRANULE) == False:
            stderrWrite('directory "' + GRANULE + '" does not exist.')
            self.config.exitError()
            return False
        #
        # the product (directory) structure:
        #-------------------------------------------------------
        L3_TARGET_ID = basename
        L3_TARGET_ID = L3_TARGET_ID.replace('L2A_', 'L03_')
        strList = L3_TARGET_ID.split('_')
        if self.config.namingConvention == 'SAFE_STANDARD':
            L3_TARGET_ID = strList[0] + '_' + strList[3] + '_' + strList[
                5] + '_' + 'N0000_R000_T00XXX_' + self.config.minTime + '.SAFE'
        else:
            L3_TARGET_ID = L3_TARGET_ID.replace(strList[6],
                                                self.config.minTime + '.SAFE')

        if self.config.targetDir != 'DEFAULT':
            targetDir = self.config.targetDir
            if os.name == 'nt' and not '\\\\?\\' in targetDir:
                targetDir = u'\\'.join([u'\\\\?', targetDir])
            if (os.path.exists(targetDir) == False):
                os.mkdir(targetDir)
        else:
            targetDir = dirname
        self.config.targetDir = targetDir

        L3_TARGET_DIR = os.path.join(targetDir, L3_TARGET_ID)
        self.config.L3_TARGET_DIR = L3_TARGET_DIR
        self.config.L3_TARGET_ID = L3_TARGET_ID

        L2A_INSPIRE_XML = os.path.join(L2A_UP_DIR, 'INSPIRE.xml')
        L2A_MANIFEST_SAFE = os.path.join(L2A_UP_DIR, 'manifest.safe')

        L3_INSPIRE_XML = os.path.join(L3_TARGET_DIR, 'INSPIRE.xml')
        L3_MANIFEST_SAFE = os.path.join(L3_TARGET_DIR, 'manifest.safe')

        AUX_DATA = 'AUX_DATA'
        DATASTRIP = 'DATASTRIP'
        GRANULE = 'GRANULE'
        HTML = 'HTML'
        REP_INFO = 'rep_info'

        self.config.L2A_DS_MTD_XML = L2A_DS_MTD_XML
        xp = L3_XmlParser(self.config, 'DS2A')
        xp.export()
        xp.validate()

        copy_tree(os.path.join(L2A_UP_DIR, AUX_DATA),
                  os.path.join(L3_TARGET_DIR, AUX_DATA))
        copy_tree(os.path.join(L2A_UP_DIR, DATASTRIP),
                  os.path.join(L3_TARGET_DIR, DATASTRIP))
        copy_tree(os.path.join(L2A_UP_DIR, HTML),
                  os.path.join(L3_TARGET_DIR, HTML))
        copy_tree(os.path.join(L2A_UP_DIR, REP_INFO),
                  os.path.join(L3_TARGET_DIR, REP_INFO))
        copy_file(L2A_INSPIRE_XML, L3_INSPIRE_XML)
        copy_file(L2A_MANIFEST_SAFE, L3_MANIFEST_SAFE)
        if not os.path.exists(os.path.join(L3_TARGET_DIR, GRANULE)):
            os.mkdir(os.path.join(L3_TARGET_DIR, GRANULE))

        self.config.L3_INSPIRE_XML = L2A_INSPIRE_XML
        self.config.L3_MANIFEST_SAFE = L2A_MANIFEST_SAFE
        self.config.L3_TARGET_DIR = L3_TARGET_DIR

        #create user product:
        if self.config.namingConvention == 'SAFE_STANDARD':
            S2A_mask = 'S2?_USER_MTD_SAFL2A_*.xml'
        else:
            S2A_mask = 'MTD_MSIL2A.xml'
        filelist = sorted(os.listdir(L2A_UP_DIR))
        found = False
        for filename in filelist:
            if (fnmatch.fnmatch(filename, S2A_mask) == True):
                found = True
                break
        if found == False:
            stderrWrite('No metadata for user product')
            self.config.exitError()

        # prepare L3 User Product metadata file
        fn_L2A = os.path.join(L2A_UP_DIR, filename)
        fn_L3 = 'MTD_MSIL03.xml'
        fn_L3 = os.path.join(L3_TARGET_DIR, fn_L3)
        self.config.L2A_UP_MTD_XML = fn_L2A
        xp = L3_XmlParser(self.config, 'UP2A')
        if xp.convert() != 0:
            xp = L3_XmlParser(self.config, 'UP2A')
        xp.validate()
        self.config.L3_TARGET_MTD_XML = fn_L3
        # copy L2A schemes from config_dir into rep_info:
        upScheme3 = self.config.upScheme3
        basename = os.path.basename(upScheme3)
        copy_file(os.path.join(self.config.configDir, upScheme3),
                  os.path.join(L3_TARGET_DIR, REP_INFO, basename))
        tileScheme3 = self.config.tileScheme3
        basename = os.path.basename(tileScheme3)
        copy_file(os.path.join(self.config.configDir, tileScheme3),
                  os.path.join(L3_TARGET_DIR, REP_INFO, basename))
        dsScheme3 = self.config.dsScheme3
        basename = os.path.basename(dsScheme3)
        copy_file(os.path.join(self.config.configDir, dsScheme3),
                  os.path.join(L3_TARGET_DIR, REP_INFO, basename))

        # copy L3 User Product metadata file:
        copy_file(fn_L2A, fn_L3)

        # remove old L2A entries from L3_TARGET_MTD_XML:
        xp = L3_XmlParser(self.config, 'UP03')
        if (xp.convert() == False):
            self.logger.fatal(
                'error in converting user product metadata to level 3')
            stderrWrite('Error in converting user product metadata to level 3')
            self.config.exitError()
        xp = L3_XmlParser(self.config, 'UP03')
        pi = xp.getTree('General_Info', 'Product_Info')
        # update L2A entries from L2A_UP_MTD_XML:
        tmin = self.config.minTime
        tmax = self.config.maxTime
        pi.PRODUCT_START_TIME = tmin[:4] + '-' + tmin[4:6] + '-' + tmin[
            6:11] + ':' + tmin[11:13] + ':' + tmin[13:15] + '.000Z'
        pi.PRODUCT_STOP_TIME = tmax[:4] + '-' + tmax[4:6] + '-' + tmax[
            6:11] + '-' + tmax[11:13] + ':' + tmax[13:15] + '.000Z'
        # update L2A and L3 entries from L2A_UP_MTD_XML:
        try:
            del pi.PRODUCT_URI_1C
        except:
            pass
        pi.PRODUCT_URI = self.config.L3_TARGET_ID
        pi.PROCESSING_LEVEL = 'Level-3p'
        pi.PRODUCT_TYPE = 'S2MSI3p'
        pi.PROCESSING_ALGORITHM = self.config.algorithm
        pi.RADIOMETRIC_PREFERENCE = self.config.radiometricPreference
        dt = datetime.utcnow()
        pi.GENERATION_TIME = strftime('%Y-%m-%dT%H:%M:%SZ', dt.timetuple())
        if self.config.namingConvention == 'SAFE_STANDARD':
            try:
                qo = pi.Query_Options
                del qo[:]
                qo = objectify.Element('Query_Options')
                qo.attrib['completeSingleTile'] = "false"
                qo.append(objectify.Element('PRODUCT_FORMAT'))
                qo.PRODUCT_FORMAT = 'SAFE_COMPACT'
                pi.append(qo)
            except:
                pass
        try:
            gl = pi.Product_Organisation.Granule_List
            del gl[:]
        except:
            pass
        try:
            pic = xp.getTree('General_Info', 'Product_Image_Characteristics')
            del pic.QUANTIFICATION_VALUES_LIST.L1C_TOA_QUANTIFICATION_VALUE
        except:
            pass
        try:
            aux = xp.getRoot('L2A_Auxiliary_Data_Info')
            del aux[:]
        except:
            pass
        try:
            aux = xp.getRoot('Auxiliary_Data_Info')
            aux.clear()
            node = objectify.Element('GIPP_LIST')
            aux.append(node)
        except:
            pass
        try:
            qii = xp.getRoot('L2A_Quality_Indicators_Info')
            del qii[:]
        except:
            pass
        try:
            qii = xp.getRoot('Quality_Indicators_Info')
            qii.clear()
            node = objectify.Element('Classification_QI')
            node.attrib['resolution'] = str(self.config.resolution)
            qii.append(node)
        except:
            pass
        xp.export()

        #create datastrip ID:
        L3_DS_DIR = os.path.join(L3_TARGET_DIR, DATASTRIP)
        dirlist = sorted(os.listdir(L3_DS_DIR))
        found = False
        for dirname in dirlist:
            if (fnmatch.fnmatch(dirname, L2A_DS_MASK) == True):
                found = True
                break
        if found == False:
            stderrWrite('No subdirectory in datastrip')
            self.config.exitError()

        if self.config.namingConvention == 'SAFE_STANDARD':
            L3_DS_ID = dirname[17:-7]
            L3_DS_OLD = os.path.join(L3_DS_DIR, dirname)
            L3_DS_DIR = os.path.join(L3_DS_DIR, L3_DS_ID)
            os.rename(L3_DS_OLD, L3_DS_DIR)
        else:
            L3_DS_ID = dirname
            L3_DS_DIR = os.path.join(L3_DS_DIR, L3_DS_ID)

        self.config.L3_DS_ID = L3_DS_ID
        filelist = sorted(os.listdir(L3_DS_DIR))
        found = False
        L2A_DS_MTD_MSK = 'S2?_USER_MTD_L2A_DS_*.xml'
        for filename in filelist:
            if (fnmatch.fnmatch(filename, L2A_DS_MTD_MSK) == True):
                L3_DS_OLD = os.path.join(L3_DS_DIR, filename)
                L3_DS_MTD = os.path.join(L3_DS_DIR, 'MTD_DS.xml')
                os.rename(L3_DS_OLD, L3_DS_MTD)
                filename = 'MTD_DS.xml'
                found = True
            elif (fnmatch.fnmatch(filename, 'MTD_DS.xml') == True):
                found = True
                break
        if found == False:
            stderrWrite('No metadata in datastrip')
            self.config.exitError()
        self.config.L3_DS_MTD_XML = os.path.join(L3_DS_DIR, filename)
        xp = L3_XmlParser(self.config, 'DS03')
        if (xp.convert() == False):
            stderrWrite('Error in converting datastrip metadata to level 3.')
            self.logger.fatal(
                'error in converting datastrip metadata to level 3.')
            self.config.exitError()
        xp = L3_XmlParser(self.config, 'DS03')
        ti = xp.getTree('Image_Data_Info', 'Tiles_Information')
        del ti.Tile_List.Tile[:]

        ri = xp.getTree('Image_Data_Info', 'Radiometric_Info')
        qvl = objectify.Element('QUANTIFICATION_VALUES_LIST')
        qvl.BOA_QUANTIFICATION_VALUE = str(int(self.config.dnScale))
        qvl.BOA_QUANTIFICATION_VALUE.attrib['unit'] = 'none'
        qvl.AOT_QUANTIFICATION_VALUE = str(
            self.config.L2A_AOT_QUANTIFICATION_VALUE)
        qvl.AOT_QUANTIFICATION_VALUE.attrib['unit'] = 'none'
        qvl.WVP_QUANTIFICATION_VALUE = str(
            self.config.L2A_WVP_QUANTIFICATION_VALUE)
        qvl.WVP_QUANTIFICATION_VALUE.attrib['unit'] = 'cm'
        ri.QUANTIFICATION_VALUES_LIST = qvl

        auxinfo = xp.getRoot('Auxiliary_Data_Info')
        if (xp.getTree('Auxiliary_Data_Info', 'GRI_List')) == False:
            gfn = xp.getTree('Auxiliary_Data_Info', 'GRI_FILENAME')
            del gfn[:]
            gl = etree.Element('GRI_List')
            gl.append(gfn)
            auxinfo.append(gl)
        if (xp.getTree('Auxiliary_Data_Info', 'LUT_List')) == False:
            ll = etree.Element('LUT_List')
            auxinfo.append(ll)
        if (xp.getTree('Auxiliary_Data_Info',
                       'SNOW_CLIMATOLOGY_MAP')) == False:
            ll = etree.Element('SNOW_CLIMATOLOGY_MAP')
            ll.text = 'None'
            auxinfo.append(ll)
        if (xp.getTree('Auxiliary_Data_Info',
                       'ESACCI_WaterBodies_Map')) == False:
            ll = etree.Element('ESACCI_WaterBodies_Map')
            ll.text = 'None'
            auxinfo.append(ll)
        if (xp.getTree('Auxiliary_Data_Info',
                       'ESACCI_LandCover_Map')) == False:
            ll = etree.Element('ESACCI_LandCover_Map')
            ll.text = 'None'
            auxinfo.append(ll)
        if (xp.getTree('Auxiliary_Data_Info',
                       'ESACCI_SnowCondition_Map_Dir')) == False:
            ll = etree.Element('ESACCI_SnowCondition_Map_Dir')
            ll.text = 'None'
            auxinfo.append(ll)

        xp.export()
        self.createTable()
        return True
Example #7
0
    def exportBandList(self, productLevel):
        ''' Export all bands of current tile.
            converts all bands from hdf5 to JPEG-2000.
            
            :param productLevel: [L1C | L2A | L3].
            :type productLevel: str
            :return: false if error occurred during export.
            :rtype: boolean           

        '''
        bandDir = self._L3_bandDir
        # converts all bands from hdf5 to JPEG 2000
        if (os.path.exists(bandDir) == False):
            self.config.logger.fatal('missing directory %s:' % bandDir)
            self.config.exitError()
            return False

        os.chdir(bandDir)
        self.config.timestamp(productLevel + '_Tables: start export')
        if (self._resolution == 10):
            bandIndex = [1, 2, 3, 7, 14, 30]

        elif (self._resolution == 20):
            bandIndex = [1, 2, 3, 4, 5, 6, 8, 11, 12, 14, 30]

        elif (self._resolution == 60):
            bandIndex = [0, 1, 2, 3, 4, 5, 6, 8, 9, 11, 12, 14, 30]

        #prepare the xml export
        Granules = objectify.Element('Granules')
        Granules.attrib['granuleIdentifier'] = self.config.L3_TILE_ID
        Granules.attrib['datastripIdentifier'] = self.config.L3_DS_ID
        Granules.attrib['imageFormat'] = 'JPEG2000'
        gl = objectify.Element('Granule_List')
        gl.append(Granules)
        for index in bandIndex:
            bandName = self.getBandNameFromIndex(index)
            if index == 14:
                filename = self._L3_Tile_SCL_File
            elif index == 30:
                filename = self._L3_Tile_MSC_File
            else:
                filename = self._L3_Tile_BND_File.replace('BXX', bandName)
            band = self.getBand(productLevel, index)

            # Median Filter:
            mf = self.config.medianFilter
            if (mf > 0):
                band = ndimage.filters.median_filter(band, (mf, mf))

            kwargs = {"tilesize": (2048, 2048), "prog": "RPCL"}
            glymur.Jp2k(filename, band.astype(uint16), **kwargs)
            self.config.logger.info('Band ' + bandName + ' exported')
            self.config.timestamp('L3_Tables: band ' + bandName + ' exported')
            filename = os.path.basename(filename.strip('.jp2'))
            imageId = etree.Element('IMAGE_ID_3')
            imageId.text = filename
            Granules.append(imageId)

        xp = L3_XmlParser(self.config, 'UP03')
        pi = xp.getTree('General_Info', 'L3_Product_Info')
        po = pi.L3_Product_Organisation
        po.append(gl)
        xp.export()

        # update on tile level
        xp = L3_XmlParser(self.config, 'T03')
        gi = xp.getRoot('General_Info')
        tiOld = xp.getTree('General_Info', 'TILE_ID_3')
        tiNew = etree.Element('TILE_ID_3')
        tiNew.text = self.config.L3_TILE_ID
        gi.replace(tiOld, tiNew)
        dsOld = xp.getTree('General_Info', 'DATASTRIP_ID_3')
        dsNew = etree.Element('DATASTRIP_ID_3')
        dsNew.text = self.config.L3_DS_ID
        gi.replace(dsOld, dsNew)

        # clean up and post processing actions:
        pxlQI = objectify.Element('L3_Mosaic_QI')
        pxlQI.attrib['resolution'] = str(self.config.resolution)
        pxlQI.append(objectify.Element('PVI_FILENAME'))
        pxlQI.append(objectify.Element('L3_TILE_CLASSIFICATION_MASK'))
        pxlQI.append(objectify.Element('L3_TILE_MOSAIC_MASK'))

        pxlQI.PVI_FILENAME = os.path.basename(self._L3_Tile_PVI_File).replace(
            '.jp2', '')
        pxlQI.L3_TILE_CLASSIFICATION_MASK = os.path.basename(
            self._L3_Tile_SCL_File).replace('.jp2', '')
        pxlQI.L3_TILE_MOSAIC_MASK = os.path.basename(
            self._L3_Tile_MSC_File).replace('.jp2', '')

        qiiL3 = xp.getTree('Quality_Indicators_Info', 'L3_Pixel_Level_QI')
        qiiL3Len = len(qiiL3)
        for i in range(qiiL3Len):
            if int(qiiL3[i].attrib['resolution']) == self.config.resolution:
                qiiL3[i].clear()
                qiiL3[i] = pxlQI
                break
        xp.export()

        # globdir = self.config.L3_TARGET_ID + '/GRANULE/' + self.config.L3_TILE_ID + '/*/*.jp2.aux.xml'
        # for filename in glob.glob(globdir):
        #     os.remove(filename)
        # globdir = self.config.L3_TARGET_ID + '/GRANULE/' + self.config.L3_TILE_ID + '/*/*/*.jp2.aux.xml'
        # for filename in glob.glob(globdir):
        #     os.remove(filename)
        self.config.timestamp(productLevel + '_Tables: stop export')
        return True
Example #8
0
    def createL3_Tile(self, tileId):
        ''' Create an L3 tile

            :param tileId: the tile ID
            :type tileId: string

        '''

        L2A_TILE_ID = tileId
        L3_TILE_ID = L2A_TILE_ID.replace('L2A_', 'L03_')
        self.config.L3_TILE_ID = L3_TILE_ID
        sourceDir = self.config.sourceDir
        L2A_UP_ID = self.config.L2A_UP_ID
        L3_TARGET_DIR = self.config.L3_TARGET_DIR
        GRANULE = 'GRANULE'
        QI_DATA = 'QI_DATA'

        L2A_TILE_ID = os.path.join(sourceDir, L2A_UP_ID, GRANULE, L2A_TILE_ID)
        L3_TILE_ID = os.path.join(L3_TARGET_DIR, GRANULE, L3_TILE_ID)

        try:
            os.mkdir(L3_TILE_ID)
            os.mkdir(os.path.join(L3_TILE_ID, QI_DATA))
            self.config.logger.info('new working directory is: ' + L3_TILE_ID)
        except:
            pass

        filelist = sorted(os.listdir(L2A_TILE_ID))
        found = False
        L2A_UP_MASK = '*_L2A_*'
        for filename in filelist:
            if (fnmatch.fnmatch(filename, L2A_UP_MASK) == True):
                found = True
                break
        if found == False:
            self.config.logger.fatal('No metadata in tile')
            self.config.exitError()

        assert isinstance(filename, object)
        L2A_TILE_MTD_XML = os.path.join(L2A_TILE_ID, filename)
        L3_TILE_MTD_XML = filename
        L3_TILE_MTD_XML = L3_TILE_MTD_XML.replace('L2A_', 'L03_')
        L3_TILE_MTD_XML = os.path.join(L3_TILE_ID, L3_TILE_MTD_XML)
        copy_file(L2A_TILE_MTD_XML, L3_TILE_MTD_XML)
        self.config.L2A_TILE_MTD_XML = L2A_TILE_MTD_XML
        xp = L3_XmlParser(self.config, 'T2A')
        xp.export()
        xp.validate()
        self.config.L3_TILE_MTD_XML = L3_TILE_MTD_XML

        #update tile and datastrip id in metadata file.
        if (self.config.resolution == 20) or (self.config.resolution == 60):
            copy_file(L2A_TILE_MTD_XML, L3_TILE_MTD_XML)
            xp = L3_XmlParser(self.config, 'T03')
            if (xp.convert() == False):
                self.logger.fatal(
                    'error in converting tile metadata to level 3')
                self.exitError()
            xp = L3_XmlParser(self.config, 'T03')
            try:
                # remove all QI items from the past, as they will be calculated
                # directly from contents of images
                tree = xp.getTree('Quality_Indicators_Info',
                                  'L1C_Image_Content_QI')
                del tree[:]
                tree = xp.getTree('Quality_Indicators_Info',
                                  'L2A_Image_Content_QI')
                del tree[:]
                tree = xp.getTree('Quality_Indicators_Info',
                                  'L1C_Pixel_Level_QI')
                del tree[:]
                tree = xp.getTree('Quality_Indicators_Info',
                                  'L2A_Pixel_Level_QI')
                del tree[:]
                tree = xp.getTree('Quality_Indicators_Info', 'PVI_FILENAME')
                del tree[:]
            except:
                pass
            xp.export()

            # read updated file and append new items:
            # create the first three enties of the Quality Measures:
            xp = L3_XmlParser(self.config, 'T03')
            root = xp.getRoot('Quality_Indicators_Info')

            tree = objectify.Element('L3_Pixel_Level_QI')
            tree.attrib['resolution'] = str(self.config.resolution)
            root.append(tree)

            tree = objectify.Element('L3_Classification_QI')
            tree.attrib['resolution'] = str(self.config.resolution)
            root.append(tree)

            tree = objectify.Element('L3_Mosaic_QI')
            tree.attrib['resolution'] = str(self.config.resolution)
            root.append(tree)

            xp.export()

            #update tile id in ds metadata file.
            xp = L3_XmlParser(self.config, 'DS03')
            ti = xp.getTree('Image_Data_Info', 'Tiles_Information')
            ti.Tile_List.append(
                objectify.Element('Tile', tileId=os.path.basename(L3_TILE_ID)))
            xp.export()
        return
Example #9
0
    def createL3_TargetProduct(self):
        ''' Create the L3 target product.

            :return: true if succesful
            :rtype: bool

        '''
        self.config.logger.info('Creating L3 target product ...')
        L2A_UP_MASK = '*2A_*'
        L2A_UP_DIR = os.path.join(self.config.sourceDir, self.config.L2A_UP_ID)
        # detect the filename for the datastrip metadata:
        L2A_DS_DIR = os.path.join(L2A_UP_DIR, 'DATASTRIP')
        if os.path.exists(L2A_DS_DIR) == False:
            stderrWrite('directory "%s" does not exist.\n' % L2A_DS_DIR)
            self.config.exitError()
            return False

        L2A_DS_MASK = '*_L2A_*'
        dirlist = sorted(os.listdir(L2A_DS_DIR))
        found = False

        for dirname in dirlist:
            if (fnmatch.fnmatch(dirname, L2A_DS_MASK) == True):
                found = True
                break

        L2A_DS_DIR = os.path.join(L2A_DS_DIR, dirname)
        L2A_DS_MTD_XML = (dirname[:-7] + '.xml').replace('_MSI_', '_MTD_')
        L2A_DS_MTD_XML = os.path.join(L2A_DS_DIR, L2A_DS_MTD_XML)
        if found == False:
            stderrWrite('No metadata in datastrip\n.')
            self.config.exitError()

        dirname, basename = os.path.split(L2A_UP_DIR)
        if (fnmatch.fnmatch(basename, L2A_UP_MASK) == False):
            stderrWrite(basename + ': identifier "*2A_*" is missing.')
            self.config.exitError()
            return False

        GRANULE = os.path.join(L2A_UP_DIR, 'GRANULE')
        if os.path.exists(GRANULE) == False:
            stderrWrite('directory "' + GRANULE + '" does not exist.')
            self.config.exitError()
            return False
        #
        # the product (directory) structure:
        #-------------------------------------------------------
        L3_TARGET_ID = basename
        L3_TARGET_ID = L3_TARGET_ID.replace('L2A_', 'L03_')
        L3_TARGET_ID = L3_TARGET_ID.replace(L3_TARGET_ID[47:62],
                                            self.config.minTime)
        L3_TARGET_ID = L3_TARGET_ID.replace(L3_TARGET_ID[63:78],
                                            self.config.maxTime)

        if self.config.targetDir != 'DEFAULT':
            targetDir = self.config.targetDir
            if os.name == 'nt' and not '\\\\?\\' in targetDir:
                targetDir = u'\\'.join([u'\\\\?', targetDir])
            if (os.path.exists(targetDir) == False):
                os.mkdir(targetDir)
        else:
            targetDir = dirname
        self.config.targetDir = targetDir

        L3_TARGET_DIR = os.path.join(targetDir, L3_TARGET_ID)
        self.config.L3_TARGET_DIR = L3_TARGET_DIR
        self.config.L3_TARGET_ID = L3_TARGET_ID

        L2A_INSPIRE_XML = os.path.join(L2A_UP_DIR, 'INSPIRE.xml')
        L2A_MANIFEST_SAFE = os.path.join(L2A_UP_DIR, 'manifest.safe')

        L3_INSPIRE_XML = os.path.join(L3_TARGET_DIR, 'INSPIRE.xml')
        L3_MANIFEST_SAFE = os.path.join(L3_TARGET_DIR, 'manifest.safe')

        AUX_DATA = 'AUX_DATA'
        DATASTRIP = 'DATASTRIP'
        GRANULE = 'GRANULE'
        HTML = 'HTML'
        REP_INFO = 'rep_info'

        self.config.L2A_DS_MTD_XML = L2A_DS_MTD_XML
        xp = L3_XmlParser(self.config, 'DS2A')
        xp.export()
        xp.validate()

        copy_tree(os.path.join(L2A_UP_DIR, AUX_DATA),
                  os.path.join(L3_TARGET_DIR, AUX_DATA))
        copy_tree(os.path.join(L2A_UP_DIR, DATASTRIP),
                  os.path.join(L3_TARGET_DIR, DATASTRIP))
        copy_tree(os.path.join(L2A_UP_DIR, HTML),
                  os.path.join(L3_TARGET_DIR, HTML))
        copy_tree(os.path.join(L2A_UP_DIR, REP_INFO),
                  os.path.join(L3_TARGET_DIR, REP_INFO))
        copy_file(L2A_INSPIRE_XML, L3_INSPIRE_XML)
        copy_file(L2A_MANIFEST_SAFE, L3_MANIFEST_SAFE)
        if not os.path.exists(os.path.join(L3_TARGET_DIR, GRANULE)):
            os.mkdir(os.path.join(L3_TARGET_DIR, GRANULE))

        self.config.L3_INSPIRE_XML = L2A_INSPIRE_XML
        self.config.L3_MANIFEST_SAFE = L2A_MANIFEST_SAFE
        self.config.L3_TARGET_DIR = L3_TARGET_DIR

        #create user product:
        S2A_mask = 'S2A_*'
        filelist = sorted(os.listdir(L2A_UP_DIR))
        found = False
        for filename in filelist:
            if (fnmatch.fnmatch(filename, S2A_mask) == True):
                found = True
                break
        if found == False:
            stderrWrite('No metadata for user product')
            self.config.exitError()

        # prepare L3 User Product metadata file
        fn_L2A = os.path.join(L2A_UP_DIR, filename)
        fn_L3 = filename[:4] + 'USER' + filename[8:]
        fn_L3 = fn_L3.replace('L2A_', 'L03_')
        fn_L3 = os.path.join(L3_TARGET_DIR, fn_L3)
        self.config.L2A_UP_MTD_XML = fn_L2A
        xp = L3_XmlParser(self.config, 'UP2A')
        xp.export()
        xp.validate()
        self.config.L3_TARGET_MTD_XML = fn_L3
        # copy L2A schemes from config_dir into rep_info:
        xp = L3_XmlParser(self.config, 'GIPP')
        cs = xp.getRoot('Common_Section')

        upScheme2a = cs.UP_Scheme_2A.text
        basename = os.path.basename(upScheme2a)
        copy_file(os.path.join(self.config.configDir, upScheme2a),
                  os.path.join(L3_TARGET_DIR, REP_INFO, basename))

        tileScheme2a = cs.Tile_Scheme_2A.text
        basename = os.path.basename(tileScheme2a)
        copy_file(os.path.join(self.config.configDir, tileScheme2a),
                  os.path.join(L3_TARGET_DIR, REP_INFO, basename))

        dsScheme2a = cs.DS_Scheme_2A.text
        basename = os.path.basename(dsScheme2a)
        copy_file(os.path.join(self.config.configDir, dsScheme2a),
                  os.path.join(L3_TARGET_DIR, REP_INFO, basename))

        # copy L3 User Product metadata file:
        copy_file(fn_L2A, fn_L3)

        # remove old L2A entries from L3_TARGET_MTD_XML:
        xp = L3_XmlParser(self.config, 'UP03')
        if (xp.convert() == False):
            self.logger.fatal(
                'error in converting user product metadata to level 3')
            stderrWrite('Error in converting user product metadata to level 3')
            self.config.exitError()
        xp = L3_XmlParser(self.config, 'UP03')
        pi = xp.getTree('General_Info', 'L3_Product_Info')
        # update L2A entries from L2A_UP_MTD_XML:

        # 2015-07-30T10:39:14.021Z
        tmin = self.config.minTime
        tmax = self.config.maxTime
        pi.PRODUCT_START_TIME = tmin[:4] + '-' + tmin[4:6] + '-' + tmin[
            6:11] + ':' + tmin[11:13] + ':' + tmin[13:15] + '.000Z'
        pi.PRODUCT_STOP_TIME = tmax[:4] + '-' + tmax[4:6] + '-' + tmax[
            6:11] + '-' + tmax[11:13] + ':' + tmax[13:15] + '.000Z'
        pi.PROCESSING_LEVEL = 'Level-3p'
        pi.PRODUCT_TYPE = 'S2MSI3p'
        pi.PROCESSING_ALGORITHM = self.config.algorithm
        pi.RADIOMETRIC_PREFERENCE = self.config.radiometricPreference
        dt = datetime.utcnow()
        pi.GENERATION_TIME = strftime('%Y-%m-%dT%H:%M:%SZ', dt.timetuple())
        qo = pi.Query_Options
        del qo[:]
        del pi.L3_Product_Organisation.Granule_List[:]
        aux = xp.getRoot('Auxiliary_Data_Info')
        del aux[:]
        l3auxData = xp.getTree('L3_Auxiliary_Data_Info', 'Aux_Data')
        l3auxData.clear()
        qii = xp.getRoot('Quality_Indicators_Info')
        del qii[:]
        l3icqi = xp.getTree('L3_Quality_Indicators_Info', 'Image_Content_QI')
        del l3icqi[:]
        l3qii = xp.getRoot('L3_Quality_Indicators_Info')
        tree = objectify.Element('L3_Classification_QI')
        tree.attrib['resolution'] = str(self.config.resolution)
        l3qii.append(tree)
        xp.export()

        #create datastrip ID:
        L3_DS_DIR = os.path.join(L3_TARGET_DIR, DATASTRIP)
        dirlist = sorted(os.listdir(L3_DS_DIR))
        found = False
        for dirname in dirlist:
            if (fnmatch.fnmatch(dirname, S2A_mask) == True):
                found = True
                break
        if found == False:
            stderrWrite('No subdirectory in datastrip')
            self.config.exitError()

        L2A_DS_ID = dirname
        L3_DS_ID = L2A_DS_ID[:4] + 'USER' + L2A_DS_ID[8:]
        L3_DS_ID = L3_DS_ID.replace('L2A_', 'L03_')
        self.config.L3_DS_ID = L3_DS_ID

        olddir = os.path.join(L3_DS_DIR, L2A_DS_ID)
        newdir = os.path.join(L3_DS_DIR, L3_DS_ID)
        os.rename(olddir, newdir)

        #find datastrip metadada, rename and change it:
        L3_DS_DIR = newdir
        filelist = sorted(os.listdir(L3_DS_DIR))
        found = False
        for filename in filelist:
            if (fnmatch.fnmatch(filename, S2A_mask) == True):
                found = True
                break
        if found == False:
            stderrWrite('No metadata in datastrip')
            self.config.exitError()

        LXX_DS_MTD_XML = filename
        L3_DS_MTD_XML = LXX_DS_MTD_XML[:4] + 'USER' + LXX_DS_MTD_XML[8:]
        L3_DS_MTD_XML = L3_DS_MTD_XML.replace('L2A_', 'L03_')
        oldfile = os.path.join(L3_DS_DIR, LXX_DS_MTD_XML)
        newfile = os.path.join(L3_DS_DIR, L3_DS_MTD_XML)
        self.config.L3_DS_MTD_XML = newfile

        os.rename(oldfile, newfile)
        xp = L3_XmlParser(self.config, 'DS03')
        if (xp.convert() == False):
            stderrWrite('Error in converting datastrip metadata to level 3.')
            self.logger.fatal(
                'error in converting datastrip metadata to level 3.')
            self.config.exitError()
        xp = L3_XmlParser(self.config, 'DS03')
        ti = xp.getTree('Image_Data_Info', 'Tiles_Information')
        del ti.Tile_List.Tile[:]
        xp.export()
        self.createTable()
        return True