Source code for s2d2.sentinel2_product
import os
import numpy as np
from typing import Optional
from .handler.xml import get_root_of_table, get_branch
from .sentinel2_datastrip import Sentinel2Datastrip
from .sentinel2_tile import Sentinel2Tile
from .typing import Path
[docs]
class Sentinel2Product:
"""
Notes
-----
For Sentinel-2 Level 1C the metadata is scattered over the files and
folders. In order to make this organization more understandable, the
following visualization is made:
.. code-block:: text
* S2X_MSIL1C_20XX...
├ AUX_DATA
├ DATASTRIP
│ └ DS_XXX_XXXX...
│ └ QI_DATA
│ └ MTD_DS.xml <- metadata about the data-strip
├ GRANULE
│ └ L1C_TXXXX_XXXX...
│ ├ AUX_DATA
│ ├ IMG_DATA
│ ├ QI_DATA
│ └ MTD_TL.xml <- metadata about the tile
├ HTML
├ rep_info
├ manifest.safe
├ INSPIRE.xml
└ MTD_MSIL1C.xml <- metadata about the product
"""
def __init__(self, path: Optional[Path] = None) -> None:
# add as optional paths of all files used here?
# default calculate relative paths for datastrip and granule
# and pass them to objects below
self.path = path
self.datastrip = Sentinel2Datastrip(path)
self.tile = Sentinel2Tile(path)
self.sensing_time = None
self.spacecraft = None
self.nanval = None
self.satval = None
self.rel_img_dir = None
self.rel_ds_dir = None
self.band_list = None
[docs]
def load_metadata(self) -> None:
"""
Load meta-data from the product file (MTD_MSIL1C.xml)
Notes
-----
The metadata structure of the Sentinel2 product is as follows:
.. code-block:: text
* MTD_MSIL1C.xml
└ n1:Level-1C_Tile_ID
├ n1:General_Info
│ ├ Product_Info
│ │ ├ PRODUCT_URI <- .SAFE folder name
│ │ ├ PROCESSING_LEVEL
│ │ └ PROCESSING_BASELINE
│ ├ SPACECRAFT_NAME
│ ├ SENSING_ORBIT_NUMBER
│ └ SENSING_ORBIT_DIRECTION
├ n1:Geometric_Info
│ ├ Tile_Geocoding
│ │ ├ HORIZONTAL_CS_NAME
│ │ ├ HORIZONTAL_CS_CODE
│ │ ├ Size : resolution={"10","20","60"}
│ │ │ ├ NROWS
│ │ │ └ NCOLS
│ │ └ Geoposition
│ │ ├ ULX
│ │ ├ ULY
│ │ ├ XDIM
│ │ └ YDIM
│ └ Tile_Angles
└ n1:Quality_Indicators_Info
"""
assert os.path.exists(self.path), 'folder does not seem to exist'
root = get_root_of_table(self.path, fname='MTD_MSIL1C.xml')
gnrl_info = get_branch(root, 'General_Info')
prod_info = get_branch(gnrl_info, 'Product_Info')
data_take = get_branch(prod_info, 'Datatake')
self._get_spacecraft_from_xmlstruct(data_take)
imag_spc = get_branch(gnrl_info, 'Product_Image_Characteristics')
self._get_special_image_values_from_xmlstruct(imag_spc)
imag_org = get_branch(prod_info, 'Product_Organisation')
gran_org = get_branch(imag_org, 'Granule_List')
# get location where imagery is situated
self._get_sub_dirs_from_xmlstruct(gran_org)
self.tile.path = os.path.join(self.path, os.path.dirname(self.rel_img_dir))
self.datastrip.path = os.path.join(self.path, self.rel_ds_dir)
[docs]
def load_all_metadata(self):
self.load_metadata()
self.tile.load_metadata()
self.datastrip.load_metadata()
[docs]
def prepare_viewing(self): #todo: not sure yet where to place this or how to name it
toi = self.tile.sensing_time.replace(tzinfo=None)
idx_row = self.datastrip.aocs_flightpath.index.get_indexer([toi], method='nearest')[0]
idx_col = self.datastrip.aocs_flightpath.columns.get_loc('pos')
pos = self.datastrip.aocs_flightpath.iloc[idx_row,idx_col]
sat_radius = np.linalg.norm(pos)
# lat, lon, radius, inclination, period, time_para, combos = calculate_correct_mapping(self.tile.view_angle,
# radius=sat_radius)
# print('.')
[docs]
def _get_spacecraft_from_xmlstruct(self, data_take) -> None:
platform = None
for field in data_take:
if field.tag == 'SPACECRAFT_NAME':
platform = field.text[-1].upper()
if platform is None: return
self.spacecraft = platform
[docs]
def _get_special_image_values_from_xmlstruct(self, imag_spc) -> None:
for spec in imag_spc:
if spec.tag == 'Special_Values':
if spec[0].text == 'NODATA':
self.nanval = int(spec[1].text)
elif spec[0].text == 'SATURATED':
self.satval = int(spec[1].text)
[docs]
def _get_sub_dirs_from_xmlstruct(self, gran_org) -> None:
# get relative path where the tile metadata is situated
rel_path = os.path.dirname(gran_org[0][0].text)
self.rel_img_dir = rel_path
# get relative path where the datastrip metadata is situated
datastrip_id = gran_org[0].attrib['datastripIdentifier']
self.rel_ds_dir = os.path.join('DATASTRIP', '_'.join(datastrip_id.split('_')[4:-1]))