Exemple #1
0
    def fillInLevel1(self, metaDict):
        """
        I wrote this code back in 2018, when it worked fine. I am guessing that 
        all I had to test on was a Level-1 file, which we now no longer
        wish to process. I have left this code here for completeness. It may
        be completely obsolete. 
        """
        self.productType = metaDict[
            'METADATA_GRANULE_DESCRIPTION_ProductShortName']
        startTimeStr = metaDict['time_coverage_start']
        self.startTime = datetime.datetime.strptime(startTimeStr,
                                                    "%Y-%m-%dT%H:%M:%SZ")
        stopTimeStr = metaDict['time_coverage_end']
        self.stopTime = datetime.datetime.strptime(stopTimeStr,
                                                   "%Y-%m-%dT%H:%M:%SZ")
        # And the generic time stamp, halfway between start and stop
        duration = self.stopTime - self.startTime
        self.datetime = self.startTime + datetime.timedelta(duration.days / 2)

        self.instrument = metaDict['sensor']
        self.satId = metaDict['platform']

        creationTimeStr = metaDict['date_created']
        self.generationTime = datetime.datetime.strptime(
            creationTimeStr, "%Y-%m-%dT%H:%M:%SZ")
        self.processingSoftwareVersion = metaDict['processor_version']
        # Leaving this as a string, in case they assume it later. It is a string in
        # sen2meta.
        self.processingLevel = metaDict[
            'METADATA_GRANULE_DESCRIPTION_ProcessLevel']

        self.absoluteOrbitNumber = int(metaDict['orbit'])

        # Make an attempt at the footprint outline. Stole most of this from sen3meta.
        # Not yet sure whether most S5P products will be swathe products, or if there
        # will be some which are chopped up further.
        posListStr = metaDict[
            'METADATA_EOP_METADATA_om:featureOfInterest_eop:multiExtentOf_gml:surfaceMembers_gml:exterior_gml:posList']
        posListStrVals = posListStr.split()
        numVals = len(posListStrVals)
        # Note that a gml:posList has pairs in order [lat long ....], with no sensible pair delimiter
        posListPairs = [
            "{} {}".format(posListStrVals[i + 1], posListStrVals[i])
            for i in range(0, numVals, 2)
        ]
        posListVals = [[float(x), float(y)]
                       for (x, y) in [pair.split() for pair in posListPairs]]

        footprintGeom = geomutils.geomFromOutlineCoords(posListVals)
        prefEpsg = geomutils.findSensibleProjection(footprintGeom)
        if prefEpsg is not None:
            self.centroidXY = geomutils.findCentroid(footprintGeom, prefEpsg)
        else:
            self.centroidXY = None
        self.outlineWKT = footprintGeom.ExportToWkt()

        # Currently have no mechanism for a preview image
        self.previewImgBin = None
Exemple #2
0
def filterByRegion(metalist, boundingBox, searchPolygon):
    """
    Filter the list items based on their footprint polygons, and given region.
    If searchPolygon is not None, use that, otherwise use the boundingBox. 
    
    """
    if searchPolygon is None:
        (westLong, eastLong, southLat, northLat) = boundingBox
        bboxWkt = 'POLYGON(({left} {top}, {right} {top}, {right} {bottom}, {left} {bottom}, {left} {top}))'.format(
            left=westLong, right=eastLong, top=northLat, bottom=southLat)
        searchPolygon = ogr.Geometry(wkt=bboxWkt)

    metalistFiltered = []
    for (urlStr, metaObj) in metalist:
        footprintGeom = ogr.Geometry(wkt=str(metaObj.footprintWkt))
        prefEpsg = geomutils.findSensibleProjection(footprintGeom)
        if geomutils.crossesDateline(footprintGeom, prefEpsg):
            footprintGeom = geomutils.splitAtDateline(footprintGeom, prefEpsg)
        if footprintGeom.Intersects(searchPolygon):
            metalistFiltered.append((urlStr, metaObj))
    return metalistFiltered
Exemple #3
0
    def __init__(self, xmlStr=None, xmlfilename=None, zipfilename=None):
        if xmlStr is None:
            if xmlfilename is not None:
                xmlStr = open(xmlfilename).read()
            elif zipfilename is not None:
                zf = zipfile.ZipFile(zipfilename, 'r')
                filenames = [zi.filename for zi in zf.infolist()]
                metadataXmlfile = [
                    fn for fn in filenames if fn.endswith('xfdumanifest.xml')
                ][0]
                mf = zf.open(metadataXmlfile)
                xmlStr = mf.read()
                del mf

        doc = minidom.parseString(xmlStr)

        xfduNode = doc.getElementsByTagName('xfdu:XFDU')[0]
        metadataSectionNode = xfduNode.getElementsByTagName(
            'metadataSection')[0]
        metadataNodeList = metadataSectionNode.getElementsByTagName(
            'metadataObject')

        # Acquisition times
        acquisitionPeriodNode = self.findMetadataNodeByIdName(
            metadataNodeList, 'acquisitionPeriod')
        startTimeNode = acquisitionPeriodNode.getElementsByTagName(
            'sentinel-safe:startTime')[0]
        startTimeStr = startTimeNode.firstChild.data.strip()
        self.startTime = datetime.datetime.strptime(startTimeStr,
                                                    "%Y-%m-%dT%H:%M:%S.%fZ")
        stopTimeNode = acquisitionPeriodNode.getElementsByTagName(
            'sentinel-safe:stopTime')[0]
        stopTimeStr = stopTimeNode.firstChild.data.strip()
        self.stopTime = datetime.datetime.strptime(stopTimeStr,
                                                   "%Y-%m-%dT%H:%M:%S.%fZ")

        # Platform details
        platformNode = self.findMetadataNodeByIdName(metadataNodeList,
                                                     'platform')
        familyNameNodeList = platformNode.getElementsByTagName(
            'sentinel-safe:familyName')
        satFamilyNameNode = [
            node for node in familyNameNodeList
            if node.getAttribute('abbreviation') == ''
        ][0]
        satFamilyNameStr = satFamilyNameNode.firstChild.data.strip()
        satNumberNode = platformNode.getElementsByTagName(
            'sentinel-safe:number')[0]
        satNumberStr = satNumberNode.firstChild.data.strip()
        if satFamilyNameStr == "Sentinel-3":
            self.satId = "S3" + satNumberStr
        else:
            raise Sen3MetaError(
                "Satellite family = '{}', does not appear to be Sentinel-3".
                format(satFamilyNameStr))
        instrumentNode = platformNode.getElementsByTagName(
            'sentinel-safe:instrument')[0]
        instrFamilyNameNode = instrumentNode.getElementsByTagName(
            'sentinel-safe:familyName')[0]
        self.instrument = instrFamilyNameNode.getAttribute('abbreviation')

        # Footprint. Confusingly, this is stored under the measurementFrameSet metadata node.
        frameSetNode = self.findMetadataNodeByIdName(metadataNodeList,
                                                     'measurementFrameSet')
        posListNode = frameSetNode.getElementsByTagName('gml:posList')[0]
        posListStr = posListNode.firstChild.data.strip()
        posListStrVals = posListStr.split()
        numVals = len(posListStrVals)
        # Note that a gml:posList has pairs in order [lat long ....], with no sensible pair delimiter
        posListPairs = [
            "{} {}".format(posListStrVals[i + 1], posListStrVals[i])
            for i in range(0, numVals, 2)
        ]
        posListVals = [[float(x), float(y)]
                       for (x, y) in [pair.split() for pair in posListPairs]]

        footprintGeom = geomutils.geomFromOutlineCoords(posListVals)
        prefEpsg = geomutils.findSensibleProjection(footprintGeom)
        if prefEpsg is not None:
            self.centroidXY = geomutils.findCentroid(footprintGeom, prefEpsg)
        else:
            self.centroidXY = None
        self.outlineWKT = footprintGeom.ExportToWkt()

        # Frame, which is not stored in the measurementFrameSet node, but in
        # the generalProductInfo node.
        prodInfoNode = self.findMetadataNodeByIdName(
            metadataNodeList, 'generalProductInformation')
        frameNodeList = prodInfoNode.getElementsByTagName(
            'sentinel3:alongtrackCoordinate')
        self.frameNumber = None
        if len(frameNodeList) > 0:
            frameNode = frameNodeList[0]
            self.frameNumber = int(frameNode.firstChild.data.strip())

        # Processing level
        productTypeNode = prodInfoNode.getElementsByTagName(
            'sentinel3:productType')[0]
        self.productType = productTypeNode.firstChild.data.strip()
        self.processingLevel = self.productType[3]
        self.productName = self.productType[5:]

        # Product creation/processing time. Note that they use a different time format (sigh.....)
        creationTimeNode = prodInfoNode.getElementsByTagName(
            'sentinel3:creationTime')[0]
        generationTimeStr = creationTimeNode.firstChild.data.strip()
        self.generationTime = datetime.datetime.strptime(
            generationTimeStr, "%Y%m%dT%H%M%S")
        # I think this is as close as we get to a software version number.
        baselineNode = prodInfoNode.getElementsByTagName(
            'sentinel3:baselineCollection')[0]
        self.baselineCollection = baselineNode.firstChild.data.strip()

        # Orbit number
        orbitRefNode = self.findMetadataNodeByIdName(
            metadataNodeList, 'measurementOrbitReference')
        relativeOrbitNode = orbitRefNode.getElementsByTagName(
            'sentinel-safe:relativeOrbitNumber')[0]
        self.relativeOrbitNumber = int(
            relativeOrbitNode.firstChild.data.strip())
        absoluteOrbitNode = orbitRefNode.getElementsByTagName(
            'sentinel-safe:orbitNumber')[0]
        self.absoluteOrbitNumber = int(
            absoluteOrbitNode.firstChild.data.strip())
        cycleNode = orbitRefNode.getElementsByTagName(
            'sentinel-safe:cycleNumber')[0]
        self.cycleNumber = int(cycleNode.firstChild.data.strip())

        # MD5 checksum for .nc files
        dataSectionNode = xfduNode.getElementsByTagName('dataObjectSection')[0]
        dataList = dataSectionNode.getElementsByTagName('dataObject')
        md5 = {}
        for dataObject in dataList:
            key = dataObject.getElementsByTagName(
                'fileLocation')[0].getAttribute('href')
            value = dataObject.getElementsByTagName(
                'checksum')[0].firstChild.data.strip()
            md5[key] = value
        self.md5 = md5

        # Currently have no mechanism for a preview image
        self.previewImgBin = None
    def __init__(self, zipfilename=None):
        """
        Currently only operates on the zipfile itself. 
        """
        if zipfilename is None:
            raise Sen1MetaError("Must give zipfilename")
        
        zf = zipfile.ZipFile(zipfilename, 'r')
        filenames = [zi.filename for zi in zf.infolist()]
        safeDirName = [fn for fn in filenames if fn.endswith('.SAFE/')][0]
        bn = safeDirName.replace('.SAFE/', '')
        
        #use manifest.safe
        metafilename = 'manifest.safe'
        fullmetafilename = safeDirName + metafilename
        mf = zf.open(fullmetafilename)
        xmlStr = mf.read()
        del mf

        doc = minidom.parseString(xmlStr)
        xfduNode = doc.getElementsByTagName('xfdu:XFDU')[0]
        metadataSectionNode = xfduNode.getElementsByTagName('metadataSection')[0]
        metadataNodeList = metadataSectionNode.getElementsByTagName('metadataObject')

        # Product information
        generalProductInformation = self.findMetadataNodeByIdName(metadataNodeList, 'generalProductInformation')
        productInformation = self.getElementsContainTagName(generalProductInformation,'standAloneProductInformation')[0]
        
        productTypeNodes=self.getElementsContainTagName(productInformation,'productType')
        if len(productTypeNodes)>0:
            self.productType= productTypeNodes[0].firstChild.data.strip()
        else:
            #this may happen if product type is RAW
            self.productType= os.path.basename(zipfilename).split("_")[2]
            
        self.polarisation = sorted([node.firstChild.data.strip() for node in self.getElementsContainTagName(productInformation,'transmitterReceiverPolarisation')])

        #productInformation = generalProductInformation.getElementsByTagName('s1sarl1:standAloneProductInformation')[0]
        #self.productType = productInformation.getElementsByTagName('s1sarl1:productType')[0].firstChild.data.strip()
        #self.polarisation = sorted([node.firstChild.data.strip() for node in productInformation.getElementsByTagName('s1sarl1:transmitterReceiverPolarisation')])
        
        # Acquisition times
        acquisitionPeriodNode = self.findMetadataNodeByIdName(metadataNodeList, 'acquisitionPeriod')
        startTimeNode = self.getElementsContainTagName(acquisitionPeriodNode,'startTime')[0]
        startTimeStr = startTimeNode.firstChild.data.strip()
        if 'Z' in startTimeStr[-1]:
            self.startTime = datetime.datetime.strptime(startTimeStr, "%Y-%m-%dT%H:%M:%S.%fZ")
        else:
            self.startTime = datetime.datetime.strptime(startTimeStr, "%Y-%m-%dT%H:%M:%S.%f")
        stopTimeNode = self.getElementsContainTagName(acquisitionPeriodNode,'stopTime')[0]
        stopTimeStr = stopTimeNode.firstChild.data.strip()
        if 'Z' in startTimeStr[-1]:
            self.stopTime = datetime.datetime.strptime(stopTimeStr, "%Y-%m-%dT%H:%M:%S.%fZ")
        else: 
            self.stopTime = datetime.datetime.strptime(stopTimeStr, "%Y-%m-%dT%H:%M:%S.%f")

        # ESA processing time
        processingNodeList = findElementByXPath(xfduNode, 
            "metadataObject/metadataWrap/xmlData/safe:processing")
        if len(processingNodeList) == 0:
            # The RAW product has a slightly different tag name - sigh.....
            processingNodeList = findElementByXPath(xfduNode, 
                "metadataObject/metadataWrap/xmlData/processing")
        self.generationTime = None
        self.processingSoftwareVersion = None
        if len(processingNodeList) > 0:
            processingNode = processingNodeList[0]
            processingStartTimeStr = processingNode.getAttribute('start')
            self.generationTime = datetime.datetime.strptime(processingStartTimeStr, 
                "%Y-%m-%dT%H:%M:%S.%f")
            softwareNodeList = findElementByXPath(processingNode, "safe:software")
            if len(softwareNodeList) == 0:
                # This seems to be an old name for the software tag
                softwareNodeList = findElementByXPath(processingNode, "software")
            # The RAW product does not seem to have this under either name. Sigh.....
            if len(softwareNodeList) > 0:
                self.processingSoftwareVersion = softwareNodeList[0].getAttribute('version')

        # platform
        platform = self.findMetadataNodeByIdName(metadataNodeList, 'platform')
        platformNode = self.getElementsContainTagName(platform,'platform')[0]
        familyName = self.getElementsContainTagName(platformNode,'familyName')[0].firstChild.data.strip()
        # to be consistent with other metadata, this has to be "S1" not "Sentinel-1"
        self.satId = familyName[0]+ familyName[-1]+self.getElementsContainTagName(platformNode,'number')[0].firstChild.data.strip()
        
        instrumentMode = self.getElementsContainTagName(platform,'instrumentMode')[0]
        self.mode = self.getElementsContainTagName(instrumentMode,'mode')[0].firstChild.data.strip()
        self.swath = sorted([node.firstChild.data.strip() for node in self.getElementsContainTagName(instrumentMode,'swath')])
        
        # orbit
        measurementOrbitReference = self.findMetadataNodeByIdName(metadataNodeList, 'measurementOrbitReference')
        orbitReference = self.getElementsContainTagName(measurementOrbitReference,'orbitReference')[0]
        self.absoluteOrbitNumber = self.getElementsContainTagName(orbitReference,'orbitNumber')[0].firstChild.data.strip()
        self.relativeOrbitNumber = self.getElementsContainTagName(orbitReference,'relativeOrbitNumber')[0].firstChild.data.strip()
        self.passDirection = measurementOrbitReference.getElementsByTagName('s1:orbitProperties')[0].getElementsByTagName('s1:pass')[0].firstChild.data.strip().title()


        # footprint
        measurementFrameSet = self.findMetadataNodeByIdName(metadataNodeList, 'measurementFrameSet')      
        posSet = self.getElementsContainTagName(measurementFrameSet,'coordinates')
        # first footprint
        posListStr =  posSet[0].firstChild.data.strip()
        # This list has pairs in order [lat,long lat,long....], different from S1 and S3
        posListPairs= posListStr.split()
        posListVals = [[float(y), float(x)] for (x, y) in [pair.split(',') for pair in posListPairs]]
        footprintGeom = geomutils.geomFromOutlineCoords(posListVals)
        footprintGeom.CloseRings()
        
        # there are more than one polygons for WV products
        if len(posSet)>1:
            footprintGeom = geomutils.ogr.ForceToMultiPolygon(footprintGeom)
            for pos in posSet[1:]:
                posListPairs= pos.firstChild.data.strip().split()
                posListVals = [[float(y), float(x)] for (x, y) in [pair.split(',') for pair in posListPairs]]
                footprint = geomutils.geomFromOutlineCoords(posListVals)
                footprint.CloseRings()
                footprintGeom.AddGeometry(footprint)
            #footprintGeom=footprintGeom.ConvexHull()
        
        self.centroidXY = None
        if footprintGeom.GetGeometryName().upper() == 'POLYGON':
            prefEpsg = geomutils.findSensibleProjection(footprintGeom)
            if prefEpsg is not None:
                self.centroidXY = geomutils.findCentroid(footprintGeom, prefEpsg)
        self.outlineWKT = footprintGeom.ExportToWkt()
                
        # Grab preview data if available, for making a quick-look
        previewDir = os.path.join(safeDirName, "preview")
        previewImgFiles = [fn for fn in filenames if os.path.dirname(fn) == previewDir and
            fn.endswith('.png')]
        self.previewImgBin = None
        if len(previewImgFiles) > 0:
            # If we found some preview images, use the first one. In fact there is probably 
            # only one
            try:
                pf = zf.open(previewImgFiles[0])
                self.previewImgBin = pf.read()
                del pf
            except zipfile.BadZipfile:
                pass
        
        if not hasattr(self, 'startTime'):
            # Assume we could not find anything from inside the zipfile, so
            # fall back to things we can deduce from the filename
            self.fallbackMetadataFromFilename(zipfilename)
    def __init__(self, xmlStr=None, xmlfilename=None, zipfilename=None):
        """
        Take either the name of a zipfile, an XML file, or an XML string, and construct
        the object from the metadata
        """
        self.previewImgBin = None
        if xmlStr is None:
            if xmlfilename is not None:
                xmlStr = open(xmlfilename).read()
            elif zipfilename is not None:
                zf = zipfile.ZipFile(zipfilename, 'r')
                filenames = [zi.filename for zi in zf.infolist()]
                safeDirName = [fn for fn in filenames if fn.endswith('.SAFE/')][0]
                bn = safeDirName.replace('.SAFE/', '')
                # The meta filename is, rather ridiculously, named something slightly different 
                # inside the SAFE directory, so we have to construct that name. 
                metafilename = bn.replace('PRD', 'MTD').replace('MSIL1C', 'SAFL1C') + ".xml"
                fullmetafilename = safeDirName + metafilename
                if fullmetafilename not in filenames:
                    # We have a new format package, in which the meta filename is constant. 
                    fullmetafilename = safeDirName + 'MTD_MSIL1C.xml'
                if fullmetafilename not in filenames:
                    # We have a new format package, in which the meta filename is constant. 
                    fullmetafilename = safeDirName + 'MTD_MSIL2A.xml'
                mf = zf.open(fullmetafilename)
                xmlStr = mf.read()
                del mf
                
                # Read in the raw content of the preview image png file, and stash on the object
                previewFilename = bn.replace('PRD', 'BWI') + ".png"
                previewFullFilename = safeDirName + previewFilename
                if previewFullFilename not in filenames:
                    # Perhaps we have a new format package, with the preview image as 
                    # a jp2 in the QI_DATA directory
                    previewFullFilenameList = [fn for fn in filenames 
                        if fnmatch.fnmatch(fn, '*/GRANULE/*/QI_DATA/*PVI.jp2')]
                    if len(previewFullFilenameList) > 0:
                        previewFullFilename = previewFullFilenameList[0]
                if previewFullFilename in filenames:
                    try:
                        pf = zf.open(previewFullFilename)
                        self.previewImgBin = pf.read()
                        del pf
                    except zipfile.BadZipfile:
                        pass
                
                # Read in the whole set of tile-level XML files, too, so we can 
                # grab tileId values from them
                self.tileNameList = None
                # This is currently commented out, as it adds significant run-time. I
                # expect to return to this in future. 
#                tileXmlPattern = safeDirName + "GRANULE/*/*.xml"
#                tileXmlFiles = [fn for fn in filenames if fnmatch.fnmatch(fn, tileXmlPattern)]
#                tileIdSet = set()
#                for tileXml in tileXmlFiles:
#                    tf = zf.open(tileXml)
#                    tileMeta = Sen2TileMeta(fileobj=tf)
#                    del tf
#                    tileIdSet.add(tileMeta.tileId[1:].upper())
#                self.tileNameList = sorted(list(tileIdSet))
        
        self.zipfileMetaXML = xmlStr
        
        doc = minidom.parseString(xmlStr)
        
        generalInfoNode = doc.getElementsByTagName('n1:General_Info')[0]
        geomInfoNode = doc.getElementsByTagName('n1:Geometric_Info')[0]
        auxilInfoNode = doc.getElementsByTagName('n1:Auxiliary_Data_Info')[0]
        qualInfoNode = doc.getElementsByTagName('n1:Quality_Indicators_Info')[0]
        
        self.processingLevel = findElementByXPath(generalInfoNode, 'Product_Info/PROCESSING_LEVEL')[0].firstChild.data.strip()
        self.spacecraftName = findElementByXPath(generalInfoNode, 'Product_Info/Datatake/SPACECRAFT_NAME')[0].firstChild.data.strip()
        self.satId = "S" + self.spacecraftName.split('-')[1]
        self.processingSoftwareVersion = findElementByXPath(generalInfoNode, 
            'Product_Info/PROCESSING_BASELINE')[0].firstChild.data.strip()
        
        # The image acquisition start and stop times. In older versions of the ESA processing
        # software, note that start and stop times were identical (which is obviously wrong)
        prodStartTimeNode = findElementByXPath(generalInfoNode, 'Product_Info/PRODUCT_START_TIME')[0]
        prodStartTimeStr = prodStartTimeNode.firstChild.data.strip()
        self.startTime = datetime.datetime.strptime(prodStartTimeStr, "%Y-%m-%dT%H:%M:%S.%fZ")
        prodStopTimeNode = findElementByXPath(generalInfoNode, 'Product_Info/PRODUCT_STOP_TIME')[0]
        prodStopTimeStr = prodStopTimeNode.firstChild.data.strip()
        self.stopTime = datetime.datetime.strptime(prodStopTimeStr, "%Y-%m-%dT%H:%M:%S.%fZ")
        # Product generation time, i.e. when ESA processed it
        generationTimeNode = findElementByXPath(generalInfoNode, 'Product_Info/GENERATION_TIME')[0]
        generationTimeStr = generationTimeNode.firstChild.data.strip()
        self.generationTime = datetime.datetime.strptime(generationTimeStr, "%Y-%m-%dT%H:%M:%S.%fZ")
        relOrbitStr = findElementByXPath(generalInfoNode, 'Product_Info/SENSING_ORBIT_NUMBER')[0].firstChild.data.strip()
        self.relativeOrbitNumber = int(relOrbitStr)
        
        # The cloud indicator
        cloudPcntNode = findElementByXPath(qualInfoNode, 'Cloud_Coverage_Assessment')[0]
        self.cloudPcnt = float(cloudPcntNode.firstChild.data.strip())
        
        # The full extPos footprint. This is a very poor excuse for a footprint, but it will
        # do for now. 
        extPosNode = findElementByXPath(geomInfoNode, 'Product_Footprint/Product_Footprint/Global_Footprint/EXT_POS_LIST')[0]
        coordsList = [float(v) for v in extPosNode.firstChild.data.strip().split()]
        x = coordsList[1::2]
        y = coordsList[0::2]
        coords = [[x, y] for (x, y) in zip(x, y)]

        footprintGeom = geomutils.geomFromOutlineCoords(coords)
        prefEpsg = geomutils.findSensibleProjection(footprintGeom)
        self.centroidXY = geomutils.findCentroid(footprintGeom, prefEpsg)
        self.extPosWKT = footprintGeom.ExportToWkt()

        # Special values in imagery
        scaleValNodeList = findElementByXPath(generalInfoNode, 'Product_Image_Characteristics/QUANTIFICATION_VALUE')
        if len(scaleValNodeList) > 0:
            scaleValNode = scaleValNodeList[0]
            self.scaleValue = float(scaleValNode.firstChild.data.strip())
        else:
            # We might be in a L2A file, in which case there are several list_scale values for different products
            scaleValNodeList = findElementByXPath(generalInfoNode, 'Product_Image_Characteristics/QUANTIFICATION_VALUES_LIST')
            if len(scaleValNodeList) > 0:
                scaleValNode = scaleValNodeList[0]
                refScaleNode = findElementByXPath(scaleValNode, 'BOA_QUANTIFICATION_VALUE')[0]
                self.scaleValue = float(refScaleNode.firstChild.data.strip())
                aotScaleNode = findElementByXPath(scaleValNode, 'AOT_QUANTIFICATION_VALUE')[0]
                self.aotScaleValue = float(aotScaleNode.firstChild.data.strip())
                wvpScaleNode = findElementByXPath(scaleValNode, 'WVP_QUANTIFICATION_VALUE')[0]
                self.wvpScaleValue = float(wvpScaleNode.firstChild.data.strip())
        specialValuesNodeList = findElementByXPath(generalInfoNode, 'Product_Image_Characteristics/Special_Values')
        # These guys have no idea how to use XML properly. Sigh......
        for node in specialValuesNodeList:
            name = node.getElementsByTagName('SPECIAL_VALUE_TEXT')[0].firstChild.data.strip()
            value = node.getElementsByTagName('SPECIAL_VALUE_INDEX')[0].firstChild.data.strip()
            if name == "NODATA":
                self.nullVal = int(value)
            elif name == "SATURATED":
                self.saturatedVal = int(value)
Exemple #6
0
    def fillInLevel2(self, metaDict):
        """
        Fill in the various fields for a Level-2 product file
        """
        self.productType = metaDict[
            '/METADATA/GRANULE_DESCRIPTION/NC_GLOBAL#ProductShortName']
        startTimeStr = metaDict['NC_GLOBAL#time_coverage_start']
        timeFormat = "%Y-%m-%dT%H:%M:%SZ"
        # Some products have the trailing Z on the time stamps, some do not. Sigh.....
        if not startTimeStr.endswith('Z'):
            timeFormat = timeFormat[:-1]
        self.startTime = datetime.datetime.strptime(startTimeStr, timeFormat)
        stopTimeStr = metaDict['NC_GLOBAL#time_coverage_end']
        self.stopTime = datetime.datetime.strptime(stopTimeStr, timeFormat)
        # And the generic time stamp, halfway between start and stop
        duration = self.stopTime - self.startTime
        self.datetime = self.startTime + datetime.timedelta(duration.days / 2)

        self.instrument = metaDict['NC_GLOBAL#sensor']
        self.satId = metaDict['NC_GLOBAL#platform']

        creationTimeStr = metaDict['NC_GLOBAL#date_created']
        self.generationTime = datetime.datetime.strptime(
            creationTimeStr, timeFormat)
        self.processingSoftwareVersion = metaDict[
            'NC_GLOBAL#processor_version']
        # Leaving this as a string, in case they assume it later. It is a string in
        # sen2meta.
        self.processingLevel = metaDict[
            '/METADATA/GRANULE_DESCRIPTION/NC_GLOBAL#ProcessLevel']
        # Not sure if this is useful, but just in case
        self.processingMode = metaDict[
            '/METADATA/EOP_METADATA/eop:metaDataProperty/eop:processing/NC_GLOBAL#eop:processingMode']

        self.absoluteOrbitNumber = int(metaDict['NC_GLOBAL#orbit'])

        # Make an attempt at the footprint outline. Stole most of this from sen3meta.
        # Not yet sure whether most S5P products will be swathe products, or if there
        # will be some which are chopped up further.
        posListStr = metaDict[
            '/METADATA/EOP_METADATA/om:featureOfInterest/eop:multiExtentOf/gml:surfaceMembers/gml:exterior/NC_GLOBAL#gml:posList']
        posListStrVals = posListStr.split()
        numVals = len(posListStrVals)
        # Note that a gml:posList has pairs in order [lat long ....], with no sensible pair delimiter
        posListPairs = [
            "{} {}".format(posListStrVals[i + 1], posListStrVals[i])
            for i in range(0, numVals, 2)
        ]
        posListVals = [[float(x), float(y)]
                       for (x, y) in [pair.split() for pair in posListPairs]]

        footprintGeom = geomutils.geomFromOutlineCoords(posListVals)
        prefEpsg = geomutils.findSensibleProjection(footprintGeom)
        if prefEpsg is not None:
            self.centroidXY = geomutils.findCentroid(footprintGeom, prefEpsg)
        else:
            self.centroidXY = None
        self.outlineWKT = footprintGeom.ExportToWkt()

        # Currently have no mechanism for a preview image
        self.previewImgBin = None