# Copyright (c) 2025, TU Wien
# of Geodesy and Geoinformation (GEO).
# All rights reserved.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VIENNA UNIVERSITY OF TECHNOLOGY,
# DEPARTMENT OF GEODESY AND GEOINFORMATION BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
ACube file name definition.
"""
import copy
import datetime as dt
from datetime import datetime
from collections import OrderedDict
from geopathfinder.file_naming import SmartFilename
[docs]
class ACubeFilename(SmartFilename):
"""
ACube file name definition using SmartFilename class.
"""
fields_def = OrderedDict([('var_name', {
'len': 9
}), ('algorithm', {
'len': 7
}), ('sat_name', {
'len': 3
}), ('product', {
'len': 7,
'delim': ''
}), ('pol', {
'len': 2,
'delim': ''
}), ('direction', {
'len': 1
}), ('dtime_1', {
'len': 8
}), ('dtime_2', {
'len': 8
}), ('grid_name', {
'len': 6
}), ('tile_name', {
'len': 10
})])
pad = "-"
delimiter = "_"
def __init__(self, fields, ext=".tif", convert=False, compact=False):
"""
Constructor of ACube Filename class.
Parameters
----------
fields: dict
Dictionary specifying the different parts of the filename.
convert: bool, optional
If true, decoding is applied to parts of the filename, where such an operation is available (default is False).
"""
self.date_format = "%Y%m%d"
self.time_format = "%H%M%S"
fields = fields.copy()
if 'dtime_2' not in fields.keys():
self.single_date = True
apply_dtime_2 = False
else:
self.single_date = False
apply_dtime_2 = True
if isinstance(fields['dtime_2'], str):
if fields['dtime_2'].endswith('--'):
self.single_date = True
else:
if isinstance(fields['dtime_2'],
dt.time) or (fields['dtime_2'].year < 1950):
self.single_date = True
if 'dtime_1' in fields.keys():
if self.single_date:
date = self.encode_date(fields['dtime_1'])
if apply_dtime_2:
time = self.encode_time(fields['dtime_2'])
else:
time = self.encode_time(fields['dtime_1'])
fields['dtime_1'] = date
fields['dtime_2'] = time
else:
fields['dtime_1'] = self.encode_date(fields['dtime_1'])
fields['dtime_2'] = self.encode_date(fields['dtime_2'])
fields_def_ext = copy.deepcopy(ACubeFilename.fields_def)
fields_def_ext['dtime_1']['decoder'] = lambda x: self.decode_date(x)
fields_def_ext['dtime_1']['encoder'] = lambda x: self.encode_date(x)
fields_def_ext['dtime_2']['decoder'] = lambda x: self.decode_time(x)
fields_def_ext['dtime_2']['encoder'] = lambda x: self.encode_time(x)
super(ACubeFilename, self).__init__(fields,
fields_def_ext,
ext=ext,
pad=ACubeFilename.pad,
delimiter=ACubeFilename.delimiter,
convert=convert,
compact=compact)
[docs]
@classmethod
def from_filename(cls, filename_str, convert=False, compact=False):
"""
Converts a filename given as a string into an ACubeFilename class object.
Parameters
----------
filename_str : str
Filename without any paths (e.g., "SIG0-----_SGRTA01_S1A_IWGRDH1VHD_20160126_054245--_EU010M_E043N016T1.tif").
convert: bool, optional
If true, decoding is applied to parts of the filename, where such an operation is available (default is False).
Returns
-------
ACubeFilename
Class representing an ACube file name.
"""
return super().from_filename(filename_str,
ACubeFilename.fields_def,
pad=ACubeFilename.pad,
delimiter=ACubeFilename.delimiter,
convert=convert,
compact=compact)
@property
def stime(self):
"""
Start time.
Returns
-------
datetime.datetime
Start time.
"""
try:
return datetime.combine(
self["dtime_1"],
self["dtime_2"]) if self.single_date else self["dtime_1"]
except TypeError:
return None
@property
def etime(self):
"""
End time.
Returns
-------
datetime.datetime
End time.
"""
try:
return datetime.combine(
self["dtime_1"],
self["dtime_2"]) if self.single_date else self["dtime_2"]
except TypeError:
return None
@property
def time(self):
"""
Unified time.
Returns
-------
datetime.datetime
Unified time.
"""
try:
if self.single_date:
return self.stime
else:
return self.stime + (self.etime - self.stime) / 2
except TypeError:
return None
@property
def ftile(self):
"""
Builds the full tile name from other filename attributes (e.g. 'EU010M_E048N015T1').
Returns
-------
ftile: str
Full tile name consisting of grid name (e.g., 'EU10M') and tile name (e.g., 'E048N015T1').
"""
try:
ftile = "_".join([self["grid_name"], self["tile_name"]])
except TypeError:
ftile = None
return ftile
[docs]
def decode_date(self, string):
"""
Decodes a string into a datetime.date object. The format is given by the class.
Parameters
----------
string: str, object
String needed to be decoded to a datetime.date object.
Returns
-------
datetime.date, object
Original object or datetime.date object parsed from the given string.
"""
if isinstance(string, str):
return datetime.strptime(string, self.date_format).date()
else:
return string
[docs]
def decode_time(self, string):
"""
Decodes a string into a datetime.time/datetime.date object. The format is given by the class and the conversion
follows the 'single_date' setting.
Parameters
----------
string: str, object
String needed to be decoded to a datetime.time/datetime.date object.
Returns
-------
datetime.date, datetime.time, object
Original object, datetime.date or datetime.time object parsed from the given string.
"""
if isinstance(string, str):
if self.single_date:
return datetime.time(
datetime.strptime(string, self.time_format))
else:
return self.decode_date(string)
else:
return string
[docs]
def encode_date(self, time_obj):
"""
Encodes a datetime.datetime/datetime.date object into a string. The format is given by the class.
Parameters
----------
time_obj: datetime.datetime, datetime.date or object
Datetime object needed to be encoded to a string.
Returns
-------
str, object
Original object or str object parsed from the given datetime object.
"""
if isinstance(time_obj, (dt.datetime, dt.date, dt.time)):
return time_obj.strftime(self.date_format)
else:
return time_obj
[docs]
def encode_time(self, time_obj):
"""
Encodes a datetime.datetime/datetime.date object into a string. The format is given by the class.
Parameters
----------
time_obj: datetime.datetime, datetime.date or object
Datetime object needed to be encoded to a string.
Returns
-------
str, object
Original object or str object parsed from the given datetime object.
"""
if isinstance(time_obj, (dt.datetime, dt.time, dt.date)):
if self.single_date:
return time_obj.strftime(self.time_format)
else:
return time_obj.strftime(self.date_format)
else:
return time_obj
if __name__ == '__main__':
pass